Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wasm: Store rlib metadata in wasm object files #120588

Merged
merged 1 commit into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2627,6 +2627,7 @@ dependencies = [
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
"ruzstd",
"wasmparser",
]

[[package]]
Expand Down Expand Up @@ -6128,6 +6129,16 @@ version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"

[[package]]
name = "wasmparser"
version = "0.118.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77f1154f1ab868e2a01d9834a805faca7bf8b50d041b4ca714d005d0dab1c50c"
dependencies = [
"indexmap",
"semver",
]

[[package]]
name = "web-sys"
version = "0.3.68"
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ libc = "0.2.50"
[dependencies.object]
version = "0.32.1"
default-features = false
features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write"]
features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write", "wasm"]

[target.'cfg(windows)'.dependencies.windows]
version = "0.52.0"
Expand Down
88 changes: 68 additions & 20 deletions compiler/rustc_codegen_ssa/src/back/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use rustc_data_structures::owned_slice::{try_slice_owned, OwnedSlice};
use rustc_metadata::creader::MetadataLoader;
use rustc_metadata::fs::METADATA_FILENAME;
use rustc_metadata::EncodedMetadata;
use rustc_serialize::leb128;
use rustc_session::Session;
use rustc_span::sym;
use rustc_target::abi::Endian;
Expand Down Expand Up @@ -420,10 +421,9 @@ pub enum MetadataPosition {
/// it's not in an allowlist of otherwise well known dwarf section names to
/// go into the final artifact.
///
/// * WebAssembly - we actually don't have any container format for this
/// target. WebAssembly doesn't support the `dylib` crate type anyway so
/// there's no need for us to support this at this time. Consequently the
/// metadata bytes are simply stored as-is into an rlib.
/// * WebAssembly - this uses wasm files themselves as the object file format
/// so an empty file with no linking metadata but a single custom section is
/// created holding our metadata.
///
/// * COFF - Windows-like targets create an object with a section that has
/// the `IMAGE_SCN_LNK_REMOVE` flag set which ensures that if the linker
Expand All @@ -438,22 +438,13 @@ pub fn create_wrapper_file(
data: &[u8],
) -> (Vec<u8>, MetadataPosition) {
let Some(mut file) = create_object_file(sess) else {
// This is used to handle all "other" targets. This includes targets
// in two categories:
//
// * Some targets don't have support in the `object` crate just yet
// to write an object file. These targets are likely to get filled
// out over time.
//
// * Targets like WebAssembly don't support dylibs, so the purpose
// of putting metadata in object files, to support linking rlibs
// into dylibs, is moot.
//
// In both of these cases it means that linking into dylibs will
// not be supported by rustc. This doesn't matter for targets like
// WebAssembly and for targets not supported by the `object` crate
// yet it means that work will need to be done in the `object` crate
// to add a case above.
if sess.target.is_like_wasm {
return (create_metadata_file_for_wasm(data, &section_name), MetadataPosition::First);
}

// Targets using this branch don't have support implemented here yet or
// they're not yet implemented in the `object` crate and will likely
// fill out this module over time.
return (data.to_vec(), MetadataPosition::Last);
};
let section = if file.format() == BinaryFormat::Xcoff {
Expand Down Expand Up @@ -532,6 +523,9 @@ pub fn create_compressed_metadata_file(
packed_metadata.extend(metadata.raw_data());

let Some(mut file) = create_object_file(sess) else {
if sess.target.is_like_wasm {
return create_metadata_file_for_wasm(&packed_metadata, b".rustc");
}
return packed_metadata.to_vec();
};
if file.format() == BinaryFormat::Xcoff {
Expand Down Expand Up @@ -624,3 +618,57 @@ pub fn create_compressed_metadata_file_for_xcoff(
file.append_section_data(section, data, 1);
file.write().unwrap()
}

/// Creates a simple WebAssembly object file, which is itself a wasm module,
/// that contains a custom section of the name `section_name` with contents
/// `data`.
///
/// NB: the `object` crate does not yet have support for writing the the wasm
/// object file format. The format is simple enough that for now an extra crate
/// from crates.io (such as `wasm-encoder`). The file format is:
///
/// * 4-byte header "\0asm"
/// * 4-byte version number - 1u32 in little-endian format
/// * concatenated sections, which for this object is always "custom sections"
///
/// Custom sections are then defined by:
/// * 1-byte section identifier - 0 for a custom section
/// * leb-encoded section length (size of the contents beneath this bullet)
/// * leb-encoded custom section name length
/// * custom section name
/// * section contents
///
/// One custom section, `linking`, is added here in accordance with
/// <https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md>
/// which is required to inform LLD that this is an object file but it should
/// otherwise basically ignore it if it otherwise looks at it. The linking
/// section currently is defined by a single version byte (2) and then further
/// sections, but we have no more sections, so it's just the byte "2".
///
/// The next custom section is the one we're interested in.
pub fn create_metadata_file_for_wasm(data: &[u8], section_name: &[u8]) -> Vec<u8> {
let mut bytes = b"\0asm\x01\0\0\0".to_vec();

let mut append_custom_section = |section_name: &[u8], data: &[u8]| {
let mut section_name_len = [0; leb128::max_leb128_len::<usize>()];
let off = leb128::write_usize_leb128(&mut section_name_len, section_name.len());
let section_name_len = &section_name_len[..off];

let mut section_len = [0; leb128::max_leb128_len::<usize>()];
let off = leb128::write_usize_leb128(
&mut section_len,
data.len() + section_name_len.len() + section_name.len(),
);
let section_len = &section_len[..off];

bytes.push(0u8);
bytes.extend_from_slice(section_len);
bytes.extend_from_slice(section_name_len);
bytes.extend_from_slice(section_name);
bytes.extend_from_slice(data);
};

append_custom_section(b"linking", &[2]);
append_custom_section(section_name, data);
bytes
}
17 changes: 17 additions & 0 deletions src/tools/tidy/src/deps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ const EXCEPTIONS: ExceptionList = &[
("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 // cargo/... (because of serde)
("self_cell", "Apache-2.0"), // rustc (fluent translations)
("snap", "BSD-3-Clause"), // rustc
("wasmparser", "Apache-2.0 WITH LLVM-exception"), // rustc
// tidy-alphabetical-end
];

Expand Down Expand Up @@ -379,6 +380,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"valuable",
"version_check",
"wasi",
"wasmparser",
"winapi",
"winapi-i686-pc-windows-gnu",
"winapi-util",
Expand Down Expand Up @@ -564,6 +566,21 @@ fn check_runtime_license_exceptions(
if pkg.name == "fortanix-sgx-abi" && pkg.license.as_deref() == Some("MPL-2.0") {
continue;
}

// This exception is due to the fact that the feature set of the
// `object` crate is different between rustc and libstd. In the
// standard library only a conservative set of features are enabled
// which notably does not include the `wasm` feature which pulls in
// this dependency. In the compiler, however, the `wasm` feature is
// enabled. This exception is intended to be here so long as the
// `EXCEPTIONS` above contains `wasmparser`, but once that goes away
// this can be removed.
if pkg.name == "wasmparser"
&& pkg.license.as_deref() == Some("Apache-2.0 WITH LLVM-exception")
{
continue;
}

tidy_error!(bad, "invalid license `{}` in `{}`", license, pkg.id);
}
}
Expand Down
Loading