Skip to content

Commit 48771e6

Browse files
authored
Rollup merge of rust-lang#120588 - alexcrichton:wasm-rmeta-object, r=wesleywiser
wasm: Store rlib metadata in wasm object files The goal of this commit is to remove warnings using LLVM tip-of-tree `wasm-ld`. In llvm/llvm-project#78658 the `wasm-ld` LLD driver no longer looks at archive indices and instead looks at all the objects in archives. Previously `lib.rmeta` files were simply raw rustc metadata bytes, not wasm objects, meaning that `wasm-ld` would emit a warning indicating so. WebAssembly targets previously passed `--fatal-warnings` to `wasm-ld` by default which meant that if Rust were to update to LLVM 18 then all wasm targets would not work. This immediate blocker was resolved in rust-lang#120278 which removed `--fatal-warnings` which enabled a theoretical update to LLVM 18 for wasm targets. This current state is ok-enough for now because rustc squashes all linker output by default if it doesn't fail. This means, for example, that rustc squashes all the linker warnings coming out of `wasm-ld` about `lib.rmeta` files with LLVM 18. This again isn't a pressing issue because the information is all hidden, but it runs the risk of being annoying if another linker error were to happen and then the output would have all these unrelated warnings that couldn't be fixed. Thus, this PR comes into the picture. The goal of this PR is to resolve these warnings by using the WebAssembly object file format on wasm targets instead of using raw rustc metadata. When I first implemented the rlib-in-objects scheme in rust-lang#84449 I remember either concluding that `wasm-ld` would either include the metadata in the output or I thought we didn't have to do anything there at all. I think I was wrong on both counts as `wasm-ld` does not include the metadata in the final output unless the object is referenced and we do actually need to do something to resolve these warnings. This PR updates the object file format containing rustc metadata on WebAssembly targets to be an actual WebAssembly file. To avoid bringing in any new dependencies I've opted to hand-code this encoding at this time. If the object gets more complicated though it'd probably be best to pull in `wasmparser` and `wasm-encoder`. For now though there's two adjacent functions reading/writing wasm. The only caveat I know of with this is that if `wasm-ld` does indeed look at the object file then the metadata will be included in the final output. I believe the only thing that could cause that at this time is `--whole-archive` which I don't think is passed for rlibs. I would clarify that I'm not 100% certain about this, however.
2 parents b4ae329 + 4f3348d commit 48771e6

File tree

4 files changed

+97
-21
lines changed

4 files changed

+97
-21
lines changed

Cargo.lock

+11
Original file line numberDiff line numberDiff line change
@@ -2608,6 +2608,7 @@ dependencies = [
26082608
"rustc-std-workspace-alloc",
26092609
"rustc-std-workspace-core",
26102610
"ruzstd",
2611+
"wasmparser",
26112612
]
26122613

26132614
[[package]]
@@ -6036,6 +6037,16 @@ version = "0.2.87"
60366037
source = "registry+https://github.com/rust-lang/crates.io-index"
60376038
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
60386039

6040+
[[package]]
6041+
name = "wasmparser"
6042+
version = "0.118.1"
6043+
source = "registry+https://github.com/rust-lang/crates.io-index"
6044+
checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9"
6045+
dependencies = [
6046+
"indexmap",
6047+
"semver",
6048+
]
6049+
60396050
[[package]]
60406051
name = "web-sys"
60416052
version = "0.3.61"

compiler/rustc_codegen_ssa/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ libc = "0.2.50"
4848
[dependencies.object]
4949
version = "0.32.1"
5050
default-features = false
51-
features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write"]
51+
features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write", "wasm"]
5252

5353
[target.'cfg(windows)'.dependencies.windows]
5454
version = "0.48.0"

compiler/rustc_codegen_ssa/src/back/metadata.rs

+68-20
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use rustc_data_structures::owned_slice::{try_slice_owned, OwnedSlice};
1515
use rustc_metadata::creader::MetadataLoader;
1616
use rustc_metadata::fs::METADATA_FILENAME;
1717
use rustc_metadata::EncodedMetadata;
18+
use rustc_serialize::leb128;
1819
use rustc_session::Session;
1920
use rustc_span::sym;
2021
use rustc_target::abi::Endian;
@@ -420,10 +421,9 @@ pub enum MetadataPosition {
420421
/// it's not in an allowlist of otherwise well known dwarf section names to
421422
/// go into the final artifact.
422423
///
423-
/// * WebAssembly - we actually don't have any container format for this
424-
/// target. WebAssembly doesn't support the `dylib` crate type anyway so
425-
/// there's no need for us to support this at this time. Consequently the
426-
/// metadata bytes are simply stored as-is into an rlib.
424+
/// * WebAssembly - this uses wasm files themselves as the object file format
425+
/// so an empty file with no linking metadata but a single custom section is
426+
/// created holding our metadata.
427427
///
428428
/// * COFF - Windows-like targets create an object with a section that has
429429
/// the `IMAGE_SCN_LNK_REMOVE` flag set which ensures that if the linker
@@ -438,22 +438,13 @@ pub fn create_wrapper_file(
438438
data: &[u8],
439439
) -> (Vec<u8>, MetadataPosition) {
440440
let Some(mut file) = create_object_file(sess) else {
441-
// This is used to handle all "other" targets. This includes targets
442-
// in two categories:
443-
//
444-
// * Some targets don't have support in the `object` crate just yet
445-
// to write an object file. These targets are likely to get filled
446-
// out over time.
447-
//
448-
// * Targets like WebAssembly don't support dylibs, so the purpose
449-
// of putting metadata in object files, to support linking rlibs
450-
// into dylibs, is moot.
451-
//
452-
// In both of these cases it means that linking into dylibs will
453-
// not be supported by rustc. This doesn't matter for targets like
454-
// WebAssembly and for targets not supported by the `object` crate
455-
// yet it means that work will need to be done in the `object` crate
456-
// to add a case above.
441+
if sess.target.is_like_wasm {
442+
return (create_metadata_file_for_wasm(data, &section_name), MetadataPosition::First);
443+
}
444+
445+
// Targets using this branch don't have support implemented here yet or
446+
// they're not yet implemented in the `object` crate and will likely
447+
// fill out this module over time.
457448
return (data.to_vec(), MetadataPosition::Last);
458449
};
459450
let section = if file.format() == BinaryFormat::Xcoff {
@@ -532,6 +523,9 @@ pub fn create_compressed_metadata_file(
532523
packed_metadata.extend(metadata.raw_data());
533524

534525
let Some(mut file) = create_object_file(sess) else {
526+
if sess.target.is_like_wasm {
527+
return create_metadata_file_for_wasm(&packed_metadata, b".rustc");
528+
}
535529
return packed_metadata.to_vec();
536530
};
537531
if file.format() == BinaryFormat::Xcoff {
@@ -624,3 +618,57 @@ pub fn create_compressed_metadata_file_for_xcoff(
624618
file.append_section_data(section, data, 1);
625619
file.write().unwrap()
626620
}
621+
622+
/// Creates a simple WebAssembly object file, which is itself a wasm module,
623+
/// that contains a custom section of the name `section_name` with contents
624+
/// `data`.
625+
///
626+
/// NB: the `object` crate does not yet have support for writing the the wasm
627+
/// object file format. The format is simple enough that for now an extra crate
628+
/// from crates.io (such as `wasm-encoder`). The file format is:
629+
///
630+
/// * 4-byte header "\0asm"
631+
/// * 4-byte version number - 1u32 in little-endian format
632+
/// * concatenated sections, which for this object is always "custom sections"
633+
///
634+
/// Custom sections are then defined by:
635+
/// * 1-byte section identifier - 0 for a custom section
636+
/// * leb-encoded section length (size of the contents beneath this bullet)
637+
/// * leb-encoded custom section name length
638+
/// * custom section name
639+
/// * section contents
640+
///
641+
/// One custom section, `linking`, is added here in accordance with
642+
/// <https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md>
643+
/// which is required to inform LLD that this is an object file but it should
644+
/// otherwise basically ignore it if it otherwise looks at it. The linking
645+
/// section currently is defined by a single version byte (2) and then further
646+
/// sections, but we have no more sections, so it's just the byte "2".
647+
///
648+
/// The next custom section is the one we're interested in.
649+
pub fn create_metadata_file_for_wasm(data: &[u8], section_name: &[u8]) -> Vec<u8> {
650+
let mut bytes = b"\0asm\x01\0\0\0".to_vec();
651+
652+
let mut append_custom_section = |section_name: &[u8], data: &[u8]| {
653+
let mut section_name_len = [0; leb128::max_leb128_len::<usize>()];
654+
let off = leb128::write_usize_leb128(&mut section_name_len, section_name.len());
655+
let section_name_len = &section_name_len[..off];
656+
657+
let mut section_len = [0; leb128::max_leb128_len::<usize>()];
658+
let off = leb128::write_usize_leb128(
659+
&mut section_len,
660+
data.len() + section_name_len.len() + section_name.len(),
661+
);
662+
let section_len = &section_len[..off];
663+
664+
bytes.push(0u8);
665+
bytes.extend_from_slice(section_len);
666+
bytes.extend_from_slice(section_name_len);
667+
bytes.extend_from_slice(section_name);
668+
bytes.extend_from_slice(data);
669+
};
670+
671+
append_custom_section(b"linking", &[2]);
672+
append_custom_section(section_name, data);
673+
bytes
674+
}

src/tools/tidy/src/deps.rs

+17
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ const EXCEPTIONS: ExceptionList = &[
9090
("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 // cargo/... (because of serde)
9191
("self_cell", "Apache-2.0"), // rustc (fluent translations)
9292
("snap", "BSD-3-Clause"), // rustc
93+
("wasmparser", "Apache-2.0 WITH LLVM-exception"), // rustc
9394
// tidy-alphabetical-end
9495
];
9596

@@ -375,6 +376,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
375376
"valuable",
376377
"version_check",
377378
"wasi",
379+
"wasmparser",
378380
"winapi",
379381
"winapi-i686-pc-windows-gnu",
380382
"winapi-util",
@@ -559,6 +561,21 @@ fn check_runtime_license_exceptions(
559561
if pkg.name == "fortanix-sgx-abi" && pkg.license.as_deref() == Some("MPL-2.0") {
560562
continue;
561563
}
564+
565+
// This exception is due to the fact that the feature set of the
566+
// `object` crate is different between rustc and libstd. In the
567+
// standard library only a conservative set of features are enabled
568+
// which notably does not include the `wasm` feature which pulls in
569+
// this dependency. In the compiler, however, the `wasm` feature is
570+
// enabled. This exception is intended to be here so long as the
571+
// `EXCEPTIONS` above contains `wasmparser`, but once that goes away
572+
// this can be removed.
573+
if pkg.name == "wasmparser"
574+
&& pkg.license.as_deref() == Some("Apache-2.0 WITH LLVM-exception")
575+
{
576+
continue;
577+
}
578+
562579
tidy_error!(bad, "invalid license `{}` in `{}`", license, pkg.id);
563580
}
564581
}

0 commit comments

Comments
 (0)