Rollup merge of #121464 - alexcrichton:fix-wasm64, r=wesleywiser

rustc: Fix wasm64 metadata object files

It looks like LLD will detect object files being either 32 or 64-bit depending on any memory present. LLD will additionally reject 32-bit objects during a 64-bit link. Previously metadata objects did not have any memories in them which led LLD to conclude they were 32-bit objects which broke 64-bit targets for wasm.

This commit fixes this by ensuring that for 64-bit targets there's a memory object present to get LLD to detect it's a 64-bit target. Additionally this commit moves away from a hand-crafted wasm encoder to the `wasm-encoder` crate on crates.io as the complexity grows for the generated object file.

Closes #121460
This commit is contained in:
Matthias Krüger 2024-02-29 20:50:03 +01:00 committed by GitHub
commit 509972089b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 59 additions and 49 deletions

View File

@ -3646,6 +3646,7 @@ dependencies = [
"thin-vec", "thin-vec",
"thorin-dwp", "thorin-dwp",
"tracing", "tracing",
"wasm-encoder",
"windows", "windows",
] ]
@ -6142,6 +6143,15 @@ version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
[[package]]
name = "wasm-encoder"
version = "0.200.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e3fb0c8fbddd78aa6095b850dfeedbc7506cf5f81e633f69cf8f2333ab84b9"
dependencies = [
"leb128",
]
[[package]] [[package]]
name = "wasmparser" name = "wasmparser"
version = "0.118.2" version = "0.118.2"

View File

@ -38,6 +38,7 @@ tempfile = "3.2"
thin-vec = "0.2.12" thin-vec = "0.2.12"
thorin-dwp = "0.7" thorin-dwp = "0.7"
tracing = "0.1" tracing = "0.1"
wasm-encoder = "0.200.0"
# tidy-alphabetical-end # tidy-alphabetical-end
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]

View File

@ -315,8 +315,11 @@ fn link_rlib<'a>(
let trailing_metadata = match flavor { let trailing_metadata = match flavor {
RlibFlavor::Normal => { RlibFlavor::Normal => {
let (metadata, metadata_position) = let (metadata, metadata_position) = create_wrapper_file(
create_wrapper_file(sess, b".rmeta".to_vec(), codegen_results.metadata.raw_data()); sess,
".rmeta".to_string(),
codegen_results.metadata.raw_data(),
);
let metadata = emit_wrapper_file(sess, &metadata, tmpdir, METADATA_FILENAME); let metadata = emit_wrapper_file(sess, &metadata, tmpdir, METADATA_FILENAME);
match metadata_position { match metadata_position {
MetadataPosition::First => { MetadataPosition::First => {
@ -384,7 +387,7 @@ fn link_rlib<'a>(
let path = find_native_static_library(filename.as_str(), true, &lib_search_paths, sess); let path = find_native_static_library(filename.as_str(), true, &lib_search_paths, sess);
let src = read(path) let src = read(path)
.map_err(|e| sess.dcx().emit_fatal(errors::ReadFileError { message: e }))?; .map_err(|e| sess.dcx().emit_fatal(errors::ReadFileError { message: e }))?;
let (data, _) = create_wrapper_file(sess, b".bundled_lib".to_vec(), &src); let (data, _) = create_wrapper_file(sess, ".bundled_lib".to_string(), &src);
let wrapper_file = emit_wrapper_file(sess, &data, tmpdir, filename.as_str()); let wrapper_file = emit_wrapper_file(sess, &data, tmpdir, filename.as_str());
packed_bundled_libs.push(wrapper_file); packed_bundled_libs.push(wrapper_file);
} else { } else {

View File

@ -1,5 +1,6 @@
//! Reading of the rustc metadata for rlibs and dylibs //! Reading of the rustc metadata for rlibs and dylibs
use std::borrow::Cow;
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
use std::path::Path; use std::path::Path;
@ -15,7 +16,6 @@ use rustc_data_structures::owned_slice::{try_slice_owned, OwnedSlice};
use rustc_metadata::creader::MetadataLoader; use rustc_metadata::creader::MetadataLoader;
use rustc_metadata::fs::METADATA_FILENAME; use rustc_metadata::fs::METADATA_FILENAME;
use rustc_metadata::EncodedMetadata; use rustc_metadata::EncodedMetadata;
use rustc_serialize::leb128;
use rustc_session::Session; use rustc_session::Session;
use rustc_span::sym; use rustc_span::sym;
use rustc_target::abi::Endian; use rustc_target::abi::Endian;
@ -434,12 +434,15 @@ pub enum MetadataPosition {
/// automatically removed from the final output. /// automatically removed from the final output.
pub fn create_wrapper_file( pub fn create_wrapper_file(
sess: &Session, sess: &Session,
section_name: Vec<u8>, section_name: String,
data: &[u8], data: &[u8],
) -> (Vec<u8>, MetadataPosition) { ) -> (Vec<u8>, MetadataPosition) {
let Some(mut file) = create_object_file(sess) else { let Some(mut file) = create_object_file(sess) else {
if sess.target.is_like_wasm { if sess.target.is_like_wasm {
return (create_metadata_file_for_wasm(data, &section_name), MetadataPosition::First); return (
create_metadata_file_for_wasm(sess, data, &section_name),
MetadataPosition::First,
);
} }
// Targets using this branch don't have support implemented here yet or // Targets using this branch don't have support implemented here yet or
@ -452,7 +455,7 @@ pub fn create_wrapper_file(
} else { } else {
file.add_section( file.add_section(
file.segment_name(StandardSegment::Debug).to_vec(), file.segment_name(StandardSegment::Debug).to_vec(),
section_name, section_name.into_bytes(),
SectionKind::Debug, SectionKind::Debug,
) )
}; };
@ -524,7 +527,7 @@ pub fn create_compressed_metadata_file(
let Some(mut file) = create_object_file(sess) else { let Some(mut file) = create_object_file(sess) else {
if sess.target.is_like_wasm { if sess.target.is_like_wasm {
return create_metadata_file_for_wasm(&packed_metadata, b".rustc"); return create_metadata_file_for_wasm(sess, &packed_metadata, ".rustc");
} }
return packed_metadata.to_vec(); return packed_metadata.to_vec();
}; };
@ -624,51 +627,41 @@ pub fn create_compressed_metadata_file_for_xcoff(
/// `data`. /// `data`.
/// ///
/// NB: the `object` crate does not yet have support for writing the wasm /// NB: the `object` crate does not yet have support for writing the wasm
/// object file format. The format is simple enough that for now an extra crate /// object file format. In lieu of that the `wasm-encoder` crate is used to
/// from crates.io (such as `wasm-encoder`). The file format is: /// build a wasm file by hand.
/// ///
/// * 4-byte header "\0asm" /// The wasm object file format is defined at
/// * 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> /// <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 /// and mainly consists of a `linking` custom section. In this case the custom
/// otherwise basically ignore it if it otherwise looks at it. The linking /// section there is empty except for a version marker indicating what format
/// section currently is defined by a single version byte (2) and then further /// it's in.
/// 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. /// The main purpose of this is to contain a custom section with `section_name`,
pub fn create_metadata_file_for_wasm(data: &[u8], section_name: &[u8]) -> Vec<u8> { /// which is then appended after `linking`.
let mut bytes = b"\0asm\x01\0\0\0".to_vec(); ///
/// As a further detail the object needs to have a 64-bit memory if `wasm64` is
/// the target or otherwise it's interpreted as a 32-bit object which is
/// incompatible with 64-bit ones.
pub fn create_metadata_file_for_wasm(sess: &Session, data: &[u8], section_name: &str) -> Vec<u8> {
assert!(sess.target.is_like_wasm);
let mut module = wasm_encoder::Module::new();
let mut imports = wasm_encoder::ImportSection::new();
let mut append_custom_section = |section_name: &[u8], data: &[u8]| { if sess.target.pointer_width == 64 {
let mut section_name_len = [0; leb128::max_leb128_len::<usize>()]; imports.import(
let off = leb128::write_usize_leb128(&mut section_name_len, section_name.len()); "env",
let section_name_len = &section_name_len[..off]; "__linear_memory",
wasm_encoder::MemoryType { minimum: 0, maximum: None, memory64: true, shared: false },
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); if imports.len() > 0 {
bytes.extend_from_slice(section_len); module.section(&imports);
bytes.extend_from_slice(section_name_len); }
bytes.extend_from_slice(section_name); module.section(&wasm_encoder::CustomSection {
bytes.extend_from_slice(data); name: "linking".into(),
}; data: Cow::Borrowed(&[2]),
});
append_custom_section(b"linking", &[2]); module.section(&wasm_encoder::CustomSection { name: section_name.into(), data: data.into() });
append_custom_section(section_name, data); module.finish()
bytes
} }

View File

@ -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) ("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) ("self_cell", "Apache-2.0"), // rustc (fluent translations)
("snap", "BSD-3-Clause"), // rustc ("snap", "BSD-3-Clause"), // rustc
("wasm-encoder", "Apache-2.0 WITH LLVM-exception"), // rustc
("wasmparser", "Apache-2.0 WITH LLVM-exception"), // rustc ("wasmparser", "Apache-2.0 WITH LLVM-exception"), // rustc
// tidy-alphabetical-end // tidy-alphabetical-end
]; ];
@ -267,6 +268,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"jemalloc-sys", "jemalloc-sys",
"jobserver", "jobserver",
"lazy_static", "lazy_static",
"leb128",
"libc", "libc",
"libloading", "libloading",
"linux-raw-sys", "linux-raw-sys",
@ -380,6 +382,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"valuable", "valuable",
"version_check", "version_check",
"wasi", "wasi",
"wasm-encoder",
"wasmparser", "wasmparser",
"winapi", "winapi",
"winapi-i686-pc-windows-gnu", "winapi-i686-pc-windows-gnu",