Skip to content

Commit e5ad5d2

Browse files
committed
generate elf symbol version in raw-dylib
1 parent 12865ff commit e5ad5d2

File tree

4 files changed

+159
-18
lines changed

4 files changed

+159
-18
lines changed

compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs

Lines changed: 77 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
44

55
use rustc_abi::Endian;
66
use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN};
7-
use rustc_data_structures::fx::FxIndexMap;
7+
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
88
use rustc_data_structures::stable_hasher::StableHasher;
99
use rustc_hashes::Hash128;
1010
use rustc_session::Session;
@@ -230,40 +230,67 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]
230230
Endian::Little => object::Endianness::Little,
231231
Endian::Big => object::Endianness::Big,
232232
};
233+
233234
let mut stub = write::Writer::new(endianness, true, &mut stub_buf);
234235

236+
let mut vers = Vec::new();
237+
let mut vers_map = FxHashMap::default();
238+
let mut syms = Vec::new();
239+
240+
for symbol in symbols {
241+
let symbol_name = symbol.name.as_str();
242+
if let Some((name, version_name)) = symbol_name.split_once('@') {
243+
if !version_name.contains('@') {
244+
let dynstr = stub.add_dynamic_string(name.as_bytes());
245+
let ver = if let Some(&ver_id) = vers_map.get(version_name) {
246+
ver_id
247+
} else {
248+
let id = vers.len();
249+
vers_map.insert(version_name, id);
250+
let dynstr = stub.add_dynamic_string(version_name.as_bytes());
251+
vers.push((version_name, dynstr));
252+
id
253+
};
254+
syms.push((name, dynstr, Some(ver)));
255+
}
256+
} else {
257+
let dynstr = stub.add_dynamic_string(symbol_name.as_bytes());
258+
syms.push((symbol_name, dynstr, None));
259+
}
260+
}
261+
262+
let soname = stub.add_dynamic_string(soname.as_bytes());
263+
235264
// These initial reservations don't reserve any bytes in the binary yet,
236265
// they just allocate in the internal data structures.
237266

238267
// First, we crate the dynamic symbol table. It starts with a null symbol
239268
// and then all the symbols and their dynamic strings.
240269
stub.reserve_null_dynamic_symbol_index();
241270

242-
let dynstrs = symbols
243-
.iter()
244-
.map(|sym| {
245-
stub.reserve_dynamic_symbol_index();
246-
(sym, stub.add_dynamic_string(sym.name.as_str().as_bytes()))
247-
})
248-
.collect::<Vec<_>>();
249-
250-
let soname = stub.add_dynamic_string(soname.as_bytes());
271+
for _ in syms.iter() {
272+
stub.reserve_dynamic_symbol_index();
273+
}
251274

252275
// Reserve the sections.
253276
// We have the minimal sections for a dynamic SO and .text where we point our dummy symbols to.
254277
stub.reserve_shstrtab_section_index();
255278
let text_section_name = stub.add_section_name(".text".as_bytes());
256279
let text_section = stub.reserve_section_index();
257-
stub.reserve_dynstr_section_index();
258280
stub.reserve_dynsym_section_index();
281+
stub.reserve_dynstr_section_index();
282+
stub.reserve_gnu_versym_section_index();
283+
stub.reserve_gnu_verdef_section_index();
259284
stub.reserve_dynamic_section_index();
260285

261286
// These reservations now determine the actual layout order of the object file.
262287
stub.reserve_file_header();
263288
stub.reserve_shstrtab();
264289
stub.reserve_section_headers();
265-
stub.reserve_dynstr();
266290
stub.reserve_dynsym();
291+
stub.reserve_dynstr();
292+
stub.reserve_gnu_versym();
293+
stub.reserve_gnu_verdef(1 + vers.len(), 1 + vers.len());
267294
stub.reserve_dynamic(2); // DT_SONAME, DT_NULL
268295

269296
// First write the ELF header with the arch information.
@@ -342,18 +369,17 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]
342369
sh_addralign: 1,
343370
sh_entsize: 0,
344371
});
345-
stub.write_dynstr_section_header(0);
346372
stub.write_dynsym_section_header(0, 1);
373+
stub.write_dynstr_section_header(0);
374+
stub.write_gnu_versym_section_header(0);
375+
stub.write_gnu_verdef_section_header(0);
347376
stub.write_dynamic_section_header(0);
348377

349-
// .dynstr
350-
stub.write_dynstr();
351-
352378
// .dynsym
353379
stub.write_null_dynamic_symbol();
354-
for (_, name) in dynstrs {
380+
for (_name, dynstr, _ver) in syms.iter().copied() {
355381
stub.write_dynamic_symbol(&write::Sym {
356-
name: Some(name),
382+
name: Some(dynstr),
357383
st_info: (elf::STB_GLOBAL << 4) | elf::STT_NOTYPE,
358384
st_other: elf::STV_DEFAULT,
359385
section: Some(text_section),
@@ -363,10 +389,43 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]
363389
});
364390
}
365391

392+
// .dynstr
393+
stub.write_dynstr();
394+
395+
// .gnu_version
396+
stub.write_null_gnu_versym();
397+
for (_name, _dynstr, ver) in syms.iter().copied() {
398+
stub.write_gnu_versym(if let Some(ver) = ver {
399+
elf::VERSYM_HIDDEN | (2 + ver as u16)
400+
} else {
401+
1
402+
});
403+
}
404+
405+
// .gnu_version_d
406+
stub.write_align_gnu_verdef();
407+
stub.write_gnu_verdef(&write::Verdef {
408+
version: elf::VER_DEF_CURRENT,
409+
flags: elf::VER_FLG_BASE,
410+
index: 1,
411+
aux_count: 1,
412+
name: soname,
413+
});
414+
for (ver, (_name, dynstr)) in vers.into_iter().enumerate() {
415+
stub.write_gnu_verdef(&write::Verdef {
416+
version: elf::VER_DEF_CURRENT,
417+
flags: 0,
418+
index: 2 + ver as u16,
419+
aux_count: 1,
420+
name: dynstr,
421+
});
422+
}
423+
366424
// .dynamic
367425
// the DT_SONAME will be used by the linker to populate DT_NEEDED
368426
// which the loader uses to find the library.
369427
// DT_NULL terminates the .dynamic table.
428+
stub.write_align_dynamic();
370429
stub.write_dynamic_string(elf::DT_SONAME, soname);
371430
stub.write_dynamic(elf::DT_NULL, 0);
372431

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//@ only-x86_64-unknown-linux-gnu
2+
3+
use run_make_support::serde_json::{self, Value};
4+
use run_make_support::{cargo, llvm_nm, rfs, rustc};
5+
6+
fn main() {
7+
cargo()
8+
.args([
9+
"r",
10+
"--manifest-path",
11+
"t/Cargo.toml",
12+
"-Zbuild-std",
13+
"-Zbuild-std-features=compiler_builtins/mem",
14+
])
15+
.run();
16+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "t"
3+
version = "0.0.0"
4+
edition = "2024"
5+
6+
[profile.dev]
7+
panic = "abort"
8+
9+
[profile.release]
10+
panic = "abort"
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#![allow(incomplete_features)]
2+
#![feature(raw_dylib_elf)]
3+
#![no_std]
4+
#![no_main]
5+
6+
use core::ffi::{c_char, c_int};
7+
8+
extern "C" fn callback(
9+
_fpath: *const c_char,
10+
_sb: *const (),
11+
_tflag: c_int,
12+
_ftwbuf: *const (),
13+
) -> c_int {
14+
0
15+
}
16+
17+
#[link(name = "libc.so.6", kind = "raw-dylib", modifiers = "+verbatim")]
18+
unsafe extern "C" {
19+
#[link_name = "nftw@GLIBC_2.2.5"]
20+
unsafe fn nftw_2_2_5(
21+
dirpath: *const c_char,
22+
f: extern "C" fn(*const c_char, *const (), c_int, *const ()) -> c_int,
23+
nopenfd: c_int,
24+
flags: c_int,
25+
) -> c_int;
26+
#[link_name = "nftw@GLIBC_2.3.3"]
27+
unsafe fn nftw_2_3_3(
28+
dirpath: *const c_char,
29+
f: extern "C" fn(*const c_char, *const (), c_int, *const ()) -> c_int,
30+
nopenfd: c_int,
31+
flags: c_int,
32+
) -> c_int;
33+
safe fn exit(status: i32) -> !;
34+
unsafe fn __libc_start_main() -> c_int;
35+
}
36+
37+
#[unsafe(no_mangle)]
38+
extern "C" fn main() -> ! {
39+
unsafe {
40+
// The old `nftw` does not check whether unknown flags are set.
41+
let res = nftw_2_2_5(c".".as_ptr(), callback, 20, 32);
42+
assert_eq!(res, 0);
43+
}
44+
unsafe {
45+
// The new `nftw` does.
46+
let res = nftw_2_3_3(c".".as_ptr(), callback, 20, 32);
47+
assert_eq!(res, -1);
48+
}
49+
exit(0);
50+
}
51+
52+
#[cfg(not(test))]
53+
#[panic_handler]
54+
fn panic_handler(_: &core::panic::PanicInfo<'_>) -> ! {
55+
exit(1);
56+
}

0 commit comments

Comments
 (0)