Rollup merge of #144221 - usamoi:versym, r=bjorn3
generate elf symbol version in raw-dylib For link names like `aaa@bbb`, it generates a symbol named `aaa` and a version named `bbb`. For link names like `aaa\0bbb`, `aaa@`@bbb`` or `aa@bb@cc`, it emits errors. It adds a test that the executable is linked with glibc using raw-dylib. cc rust-lang/rust#135694
This commit is contained in:
@@ -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,47 +226,6 @@ 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 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);
|
||||
|
||||
// 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
|
||||
// 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());
|
||||
|
||||
// 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_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_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!(
|
||||
@@ -274,6 +233,87 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]
|
||||
sess.target.arch
|
||||
));
|
||||
};
|
||||
|
||||
let endianness = match sess.target.options.endian {
|
||||
Endian::Little => object::Endianness::Little,
|
||||
Endian::Big => object::Endianness::Big,
|
||||
};
|
||||
|
||||
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 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();
|
||||
|
||||
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_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_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 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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user