diff options
| author | usamoi <usamoi@outlook.com> | 2025-07-20 21:40:36 +0800 |
|---|---|---|
| committer | usamoi <usamoi@outlook.com> | 2025-07-24 19:04:00 +0800 |
| commit | e31876c143625a23c95bdf342444f1f1f100c57f (patch) | |
| tree | 2a5474742ad952c7a7a4e66aeb8c6cffce9cda41 /compiler/rustc_codegen_ssa | |
| parent | 12865ffd0dfb4ea969e2f16eb0140238bb9dd382 (diff) | |
| download | rust-e31876c143625a23c95bdf342444f1f1f100c57f.tar.gz rust-e31876c143625a23c95bdf342444f1f1f100c57f.zip | |
generate elf symbol version in raw-dylib
Diffstat (limited to 'compiler/rustc_codegen_ssa')
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs | 134 |
1 files changed, 106 insertions, 28 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs index 74f39022afb..b9e0c957363 100644 --- a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs +++ b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs @@ -4,7 +4,7 @@ use std::path::{Path, PathBuf}; use rustc_abi::Endian; use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN}; -use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::stable_hasher::StableHasher; use rustc_hashes::Hash128; use rustc_session::Session; @@ -214,7 +214,7 @@ pub(super) fn create_raw_dylib_elf_stub_shared_objects<'a>( /// It exports all the provided symbols, but is otherwise empty. fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]) -> Vec<u8> { use object::write::elf as write; - use object::{Architecture, elf}; + use object::{AddressSize, Architecture, elf}; let mut stub_buf = Vec::new(); @@ -226,54 +226,94 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport] // It is important that the order of reservation matches the order of writing. // The object crate contains many debug asserts that fire if you get this wrong. + let Some((arch, sub_arch)) = sess.target.object_architecture(&sess.unstable_target_features) + else { + sess.dcx().fatal(format!( + "raw-dylib is not supported for the architecture `{}`", + sess.target.arch + )); + }; + let endianness = match sess.target.options.endian { Endian::Little => object::Endianness::Little, Endian::Big => object::Endianness::Big, }; - let mut stub = write::Writer::new(endianness, true, &mut stub_buf); + + let is_64 = match arch.address_size() { + Some(AddressSize::U8 | AddressSize::U16 | AddressSize::U32) => false, + Some(AddressSize::U64) => true, + _ => sess.dcx().fatal(format!( + "raw-dylib is not supported for the architecture `{}`", + sess.target.arch + )), + }; + + let mut stub = write::Writer::new(endianness, is_64, &mut stub_buf); + + let mut vers = Vec::new(); + let mut vers_map = FxHashMap::default(); + let mut syms = Vec::new(); + + for symbol in symbols { + let symbol_name = symbol.name.as_str(); + if let Some((name, version_name)) = symbol_name.split_once('@') { + assert!(!version_name.contains('@')); + let dynstr = stub.add_dynamic_string(name.as_bytes()); + let ver = if let Some(&ver_id) = vers_map.get(version_name) { + ver_id + } else { + let id = vers.len(); + vers_map.insert(version_name, id); + let dynstr = stub.add_dynamic_string(version_name.as_bytes()); + vers.push((version_name, dynstr)); + id + }; + syms.push((name, dynstr, Some(ver))); + } else { + let dynstr = stub.add_dynamic_string(symbol_name.as_bytes()); + syms.push((symbol_name, dynstr, None)); + } + } + + let soname = stub.add_dynamic_string(soname.as_bytes()); // These initial reservations don't reserve any bytes in the binary yet, // they just allocate in the internal data structures. - // First, we crate the dynamic symbol table. It starts with a null symbol + // First, we create the dynamic symbol table. It starts with a null symbol // and then all the symbols and their dynamic strings. stub.reserve_null_dynamic_symbol_index(); - let dynstrs = symbols - .iter() - .map(|sym| { - stub.reserve_dynamic_symbol_index(); - (sym, stub.add_dynamic_string(sym.name.as_str().as_bytes())) - }) - .collect::<Vec<_>>(); - - let soname = stub.add_dynamic_string(soname.as_bytes()); + for _ in syms.iter() { + stub.reserve_dynamic_symbol_index(); + } // Reserve the sections. // We have the minimal sections for a dynamic SO and .text where we point our dummy symbols to. stub.reserve_shstrtab_section_index(); let text_section_name = stub.add_section_name(".text".as_bytes()); let text_section = stub.reserve_section_index(); - stub.reserve_dynstr_section_index(); stub.reserve_dynsym_section_index(); + stub.reserve_dynstr_section_index(); + if !vers.is_empty() { + stub.reserve_gnu_versym_section_index(); + stub.reserve_gnu_verdef_section_index(); + } stub.reserve_dynamic_section_index(); // These reservations now determine the actual layout order of the object file. stub.reserve_file_header(); stub.reserve_shstrtab(); stub.reserve_section_headers(); - stub.reserve_dynstr(); stub.reserve_dynsym(); + stub.reserve_dynstr(); + if !vers.is_empty() { + stub.reserve_gnu_versym(); + stub.reserve_gnu_verdef(1 + vers.len(), 1 + vers.len()); + } stub.reserve_dynamic(2); // DT_SONAME, DT_NULL // First write the ELF header with the arch information. - let Some((arch, sub_arch)) = sess.target.object_architecture(&sess.unstable_target_features) - else { - sess.dcx().fatal(format!( - "raw-dylib is not supported for the architecture `{}`", - sess.target.arch - )); - }; let e_machine = match (arch, sub_arch) { (Architecture::Aarch64, None) => elf::EM_AARCH64, (Architecture::Aarch64_Ilp32, None) => elf::EM_AARCH64, @@ -342,18 +382,19 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport] sh_addralign: 1, sh_entsize: 0, }); - stub.write_dynstr_section_header(0); stub.write_dynsym_section_header(0, 1); + stub.write_dynstr_section_header(0); + if !vers.is_empty() { + stub.write_gnu_versym_section_header(0); + stub.write_gnu_verdef_section_header(0); + } stub.write_dynamic_section_header(0); - // .dynstr - stub.write_dynstr(); - // .dynsym stub.write_null_dynamic_symbol(); - for (_, name) in dynstrs { + for (_name, dynstr, _ver) in syms.iter().copied() { stub.write_dynamic_symbol(&write::Sym { - name: Some(name), + name: Some(dynstr), st_info: (elf::STB_GLOBAL << 4) | elf::STT_NOTYPE, st_other: elf::STV_DEFAULT, section: Some(text_section), @@ -363,10 +404,47 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport] }); } + // .dynstr + stub.write_dynstr(); + + // ld.bfd is unhappy if these sections exist without any symbols, so we only generate them when necessary. + if !vers.is_empty() { + // .gnu_version + stub.write_null_gnu_versym(); + for (_name, _dynstr, ver) in syms.iter().copied() { + stub.write_gnu_versym(if let Some(ver) = ver { + assert!((2 + ver as u16) < elf::VERSYM_HIDDEN); + elf::VERSYM_HIDDEN | (2 + ver as u16) + } else { + 1 + }); + } + + // .gnu_version_d + stub.write_align_gnu_verdef(); + stub.write_gnu_verdef(&write::Verdef { + version: elf::VER_DEF_CURRENT, + flags: elf::VER_FLG_BASE, + index: 1, + aux_count: 1, + name: soname, + }); + for (ver, (_name, dynstr)) in vers.into_iter().enumerate() { + stub.write_gnu_verdef(&write::Verdef { + version: elf::VER_DEF_CURRENT, + flags: 0, + index: 2 + ver as u16, + aux_count: 1, + name: dynstr, + }); + } + } + // .dynamic // the DT_SONAME will be used by the linker to populate DT_NEEDED // which the loader uses to find the library. // DT_NULL terminates the .dynamic table. + stub.write_align_dynamic(); stub.write_dynamic_string(elf::DT_SONAME, soname); stub.write_dynamic(elf::DT_NULL, 0); |
