Unnamed repository; edit this file 'description' to name the repository.
Rollup merge of #155338 - cezarbbb:staticlib-symbol-hygiene, r=petrochenkov
Staticlib hide internal symbols According to issue rust-lang/rust#104707, when building a staticlib, all Rust internal symbols — mangled symbols, `#[rustc_std_internal_symbol]` items, allocator shims, etc. — leak out of the static archive. In contrast, cdylib correctly exports only `#[no_mangle]` symbols via a linker version script. `-Zstaticlib-hide-internal-symbols` directly post-processes ELF object files in the archive: parsing the `SHT_SYMTAB` sections and setting `STV_HIDDEN` visibility on any `GLOBAL/WEAK` defined symbol that is not in the exported symbol set, without changing the binding. This is an in-place modification (only writing the st_other byte per matching entry), with zero overhead. Supported on ELF targets (Linux, BSD, etc.) and Apple targets (macOS, iOS, etc.). On unsupported targets (Windows), a warning is emitted and the flag has no effect. **Update**: The rename counterpart (`-Zstaticlib-rename-internal-symbols`) is in rust-lang/rust#156950. The test code are as follows: 1.a std rust staticlib: ```rust use std::collections::HashMap; use std::panic::{catch_unwind, AssertUnwindSafe}; #[no_mangle] pub extern "C" fn my_add(a: i32, b: i32) -> i32 { a + b } #[no_mangle] pub extern "C" fn my_hash_lookup(key: u64) -> u64 { let mut map = HashMap::new(); for i in 0..100u64 { map.insert(i, i.wrapping_mul(2654435761)); } *map.get(&key).unwrap_or(&0) } pub fn internal_reverse(s: &str) -> String { s.chars().rev().collect() } #[no_mangle] pub extern "C" fn my_format_number(n: i32) -> i32 { let s = format!("number: {}", n); s.len() as i32 } #[no_mangle] pub extern "C" fn my_safe_div(a: i32, b: i32) -> i32 { match catch_unwind(AssertUnwindSafe(|| { if b == 0 { panic!("division by zero!"); } a / b })) { Ok(result) => result, Err(_) => -1, } } #[no_mangle] pub extern "C" fn my_uncaught_panic() { panic!("uncaught panic across FFI"); } ``` 1.b downstream c program: ```c extern int my_add(int a, int b); extern unsigned long my_hash_lookup(unsigned long key); extern int my_format_number(int n); extern int my_safe_div(int a, int b); extern void my_uncaught_panic(void); int main() { int failures = 0; if (my_add(10, 20) != 30) failures++; if (my_hash_lookup(5) != 5UL * 2654435761UL) failures++; if (my_format_number(42) != 10) failures++; if (my_safe_div(100, 5) != 20) failures++; if (my_safe_div(100, 0) != -1) failures++; pid_t pid = fork(); if (pid == 0) { alarm(5); my_uncaught_panic(); _exit(0); } else { waitpid(pid, &status, 0); } return failures; } ``` The test results with different compiler flags(which might cause binary size reduction) are as follows: 1.c result with `-Zstaticlib-hide-internal-symbols` ``` settings OFF ON -Zsave ALL OFF.dynsym ON.dynsym ------------------------------------------------------------------------ default 1.7M 1.5M 204K (12%) 1735 5 1730 lto_thin 616K 584K 33K (5%) 246 5 241 lto_fat 525K 525K 0 (0%) 6 5 1 opt_s 1.7M 1.5M 204K (12%) 1735 5 1730 opt_z 1.7M 1.5M 204K (12%) 1735 5 1730 lto_thin_z 602K 570K 32K (5%) 246 5 241 lto_fat_z 514K 514K 0 (0%) 6 5 1 full 514K 514K 0 (0%) 6 5 1 ``` 1.d result with `-Zstaticlib-hide-internal-symbols + -Zstaticlib-rename-internal-symbols` ``` settings OFF ON -Zsave ALL OFF.dynsym ON.dynsym ------------------------------------------------------------------------ default 1.7M 1.5M 162K (9%) 1735 5 1730 lto_thin 616K 599K 18K (2%) 246 5 241 lto_fat 525K 535K -1% (-1%) 6 5 1 opt_s 1.7M 1.5M 162K (9%) 1735 5 1730 opt_z 1.7M 1.5M 162K (9%) 1735 5 1730 lto_thin_z 602K 585K 18K (2%) 246 5 241 lto_fat_z 514K 524K -1% (-1%) 6 5 1 full 514K 523K -1% (-1%) 6 5 1 ``` 2.a no_std rust staticlib ```rust #![no_std] #![feature(core_intrinsics)] use core::panic::PanicInfo; #[panic_handler] fn panic(_info: &PanicInfo) -> ! { loop {} } #[no_mangle] pub extern "C" fn embedded_add(a: i32, b: i32) -> i32 { a.wrapping_add(b) } #[no_mangle] pub extern "C" fn embedded_checksum(data: *const u8, len: usize) -> u8 { if data.is_null() { return 0; } let slice = unsafe { core::slice::from_raw_parts(data, len) }; let mut sum: u8 = 0; for &byte in slice { sum = sum.wrapping_add(byte); } sum } fn internal_helper() -> i32 { 42 } #[no_mangle] pub extern "C" fn call_internal() -> i32 { internal_helper() } #[no_mangle] pub extern "C" fn embedded_trigger_abort() { core::intrinsics::abort(); } ``` 2.b downstream c program ```c extern int embedded_add(int a, int b); extern unsigned char embedded_checksum(const unsigned char *data, unsigned long len); extern int call_internal(void); extern void embedded_trigger_abort(void); int main() { int failures = 0; if (embedded_add(10, 20) != 30) failures++; unsigned char data[] = {1, 2, 3}; if (embedded_checksum(data, 3) != 6) failures++; if (call_internal() != 42) failures++; pid_t pid = fork(); if (pid == 0) { embedded_trigger_abort(); _exit(0); } else { waitpid(pid, &status, 0); } return failures; } ``` The test results with different compiler flags(which might cause binary size reduction) are as follows: 2.c result with `-Zstaticlib-hide-internal-symbols` ``` settings OFF ON -Zsave ALL OFF.dynsym ON.dynsym ------------------------------------------------------------------------ default 485K 429K 56K (11%) 490 4 486 lto_thin 180K 180K 0 (0%) 4 4 0 lto_fat 179K 179K 0 (0%) 4 4 0 opt_s 485K 429K 56K (11%) 490 4 486 opt_z 485K 429K 56K (11%) 490 4 486 lto_thin_z 180K 180K 0 (0%) 4 4 0 lto_fat_z 179K 179K 0 (0%) 4 4 0 full 179K 179K 0 (0%) 4 4 0 ``` 2.d result with `-Zstaticlib-hide-internal-symbols + -Zstaticlib-rename-internal-symbols` ``` settings OFF ON -Zsave ALL OFF.dynsym ON.dynsym ------------------------------------------------------------------------ default 485K 447K 39K (7%) 490 4 486 lto_thin 180K 189K -5% (-5%) 4 4 0 lto_fat 179K 189K -5% (-5%) 4 4 0 opt_s 485K 448K 38K (7%) 490 4 486 opt_z 485K 448K 38K (7%) 490 4 486 lto_thin_z 180K 189K -5% (-5%) 4 4 0 lto_fat_z 179K 189K -5% (-5%) 4 4 0 full 179K 189K -5% (-5%) 4 4 0 ``` Test results show that this compiler option is beneficial for scenarios where LTO cannot be enabled. r? @bjorn3 @petrochenkov
Jonathan Brouwer 6 weeks ago
parent 5406ce2 · parent d62283f · commit bc38ad6
0 files changed, 0 insertions, 0 deletions