Skip to content

Staticlib rename internal symbols#156950

Merged
rust-bors[bot] merged 1 commit into
rust-lang:mainfrom
cezarbbb:staticlib-rename-internal-symbols
Jun 16, 2026
Merged

Staticlib rename internal symbols#156950
rust-bors[bot] merged 1 commit into
rust-lang:mainfrom
cezarbbb:staticlib-rename-internal-symbols

Conversation

@cezarbbb

@cezarbbb cezarbbb commented May 26, 2026

Copy link
Copy Markdown
Contributor

View all comments

Follow-up to #155338.

-Zstaticlib-rename-internal-symbols appends a crate-specific suffix (_rs{StableCrateId}) to non-exported symbols, resolving duplicate symbol conflicts when linking multiple Rust staticlibs into the same binary.

The implementation collects all defined GLOBAL/WEAK symbol names not in the exported set across all .o files, then renames them by extending the strtab and patching symbol name offsets. When combined with -Zstaticlib-hide-internal-symbols, the renamed symbols also receive STV_HIDDEN visibility.

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.

r?@bjorn3 @petrochenkov

@rustbot rustbot added A-run-make Area: port run-make Makefiles to rmake.rs S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels May 26, 2026
@petrochenkov petrochenkov self-assigned this May 26, 2026
@petrochenkov

Copy link
Copy Markdown
Contributor

Blocked on #155338.
@rustbot blocked

@rustbot rustbot added S-blocked Status: Blocked on something else such as an RFC or other implementation work. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 26, 2026
@MicroDroid

Copy link
Copy Markdown

btw if this underwent a lot of changes and needs re-testing, I'm up for it (I compile Rust to 6 targets).

@rust-bors

This comment has been minimized.

@cezarbbb

cezarbbb commented Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

btw if this underwent a lot of changes and needs re-testing, I'm up for it (I compile Rust to 6 targets).

Functionally, there shouldn't be any changes; it's mostly more "Rusty" modifications. After #155338 is landed, I will rebase the current PR, and you can test it after that.

jhpratt added a commit to jhpratt/rust that referenced this pull request Jun 5, 2026
… r=petrochenkov

Staticlib hide internal symbols

According to issue rust-lang#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#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
GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this pull request Jun 5, 2026
… r=petrochenkov

Staticlib hide internal symbols

According to issue rust-lang#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#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
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Jun 5, 2026
… r=petrochenkov

Staticlib hide internal symbols

According to issue rust-lang#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#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
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Jun 8, 2026
… r=petrochenkov

Staticlib hide internal symbols

According to issue rust-lang#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#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
rust-timer added a commit that referenced this pull request Jun 8, 2026
Rollup merge of #155338 - cezarbbb:staticlib-symbol-hygiene, r=petrochenkov

Staticlib hide internal symbols

According to issue #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 #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
@cezarbbb cezarbbb force-pushed the staticlib-rename-internal-symbols branch from 2445a49 to 55c16ed Compare June 9, 2026 03:19
@rustbot

This comment has been minimized.

@cezarbbb

cezarbbb commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Rebase the current PR to the branch after successfully merging PR #155338, and optimize and improve upon the issues encountered in PR #155338.

@cezarbbb

cezarbbb commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Can we now remove the S-blocked label? @petrochenkov

@petrochenkov

Copy link
Copy Markdown
Contributor

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-blocked Status: Blocked on something else such as an RFC or other implementation work. labels Jun 9, 2026
Comment thread compiler/rustc_codegen_ssa/src/back/archive.rs Outdated
@cezarbbb cezarbbb force-pushed the staticlib-rename-internal-symbols branch from 55c16ed to a86abb9 Compare June 9, 2026 11:36
Comment thread compiler/rustc_codegen_ssa/src/back/mod.rs Outdated
@cezarbbb cezarbbb force-pushed the staticlib-rename-internal-symbols branch from a86abb9 to 0762f85 Compare June 9, 2026 11:47
Comment thread tests/run-make/staticlib-rename-internal-symbols/lib.rs Outdated
Comment thread compiler/rustc_session/src/options.rs Outdated
Comment thread compiler/rustc_codegen_ssa/src/back/archive.rs Outdated
Comment thread compiler/rustc_codegen_ssa/src/base.rs Outdated
Comment thread compiler/rustc_codegen_ssa/src/back/archive.rs Outdated
Comment thread compiler/rustc_codegen_ssa/src/back/symbol_edit.rs Outdated
Comment thread compiler/rustc_codegen_ssa/src/back/symbol_edit.rs Outdated
Comment thread compiler/rustc_codegen_ssa/src/back/symbol_edit.rs Outdated
Comment thread compiler/rustc_codegen_ssa/src/back/symbol_edit.rs Outdated
Comment thread compiler/rustc_codegen_ssa/src/back/symbol_edit.rs
@petrochenkov

Copy link
Copy Markdown
Contributor

I didn't look at the strtab building code in detail yet, probably in the next review round.
@rustbot author

@JonathanBrouwer

Copy link
Copy Markdown
Contributor

💔 I suspect this PR failed tests as part of a rollup
@bors r-

After fixing the problem, consider running a try job for the failed job before re-approving.

Link to failure: #157815 (comment)

@rust-bors rust-bors Bot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Jun 12, 2026
@rust-bors

rust-bors Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

This pull request was unapproved.

This PR was contained in a rollup (#157815), which was unapproved.

View changes since this unapproval

@petrochenkov

Copy link
Copy Markdown
Contributor

@bors delegate=try

@rust-bors

rust-bors Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

✌️ @cezarbbb, you can now perform try builds on this pull request!

You can now post @bors try to start a try build.

@cezarbbb cezarbbb force-pushed the staticlib-rename-internal-symbols branch from 7478e5c to 796bc58 Compare June 16, 2026 01:54
@cezarbbb

Copy link
Copy Markdown
Contributor Author

@bors try jobs=aarch64-apple

@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Jun 16, 2026
…=<try>

Staticlib rename internal symbols


try-job: aarch64-apple
@rust-bors

rust-bors Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: cbbb804 (cbbb8043a35561da5354ebe3d716a838f08b8628, parent: 01dfd79246f1b2d5f146616deff08223a840a9ae)

@cezarbbb

Copy link
Copy Markdown
Contributor Author

It has now been clarified that the issue encountered by aarch64-apple was that the MachO test forgot to handle rcgu.o in the same way as ELF. This has been fixed.
@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jun 16, 2026
@petrochenkov

Copy link
Copy Markdown
Contributor

@bors r+

@rust-bors

rust-bors Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

📌 Commit 796bc58 has been approved by petrochenkov

It is now in the queue for this repository.

@rust-bors rust-bors Bot added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 16, 2026
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Jun 16, 2026
…-symbols, r=petrochenkov

Staticlib rename internal symbols

Follow-up to rust-lang#155338.

`-Zstaticlib-rename-internal-symbols` appends a crate-specific suffix (`_rs{StableCrateId}`) to non-exported symbols, resolving duplicate symbol conflicts when linking multiple Rust staticlibs into the same binary.

The implementation collects all defined `GLOBAL/WEAK` symbol names not in the exported set across all .o files, then renames them by extending the strtab and patching symbol name offsets. When combined with `-Zstaticlib-hide-internal-symbols`, the renamed symbols also receive `STV_HIDDEN` visibility.

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.

r?@bjorn3 @petrochenkov
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Jun 16, 2026
…-symbols, r=petrochenkov

Staticlib rename internal symbols

Follow-up to rust-lang#155338.

`-Zstaticlib-rename-internal-symbols` appends a crate-specific suffix (`_rs{StableCrateId}`) to non-exported symbols, resolving duplicate symbol conflicts when linking multiple Rust staticlibs into the same binary.

The implementation collects all defined `GLOBAL/WEAK` symbol names not in the exported set across all .o files, then renames them by extending the strtab and patching symbol name offsets. When combined with `-Zstaticlib-hide-internal-symbols`, the renamed symbols also receive `STV_HIDDEN` visibility.

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.

r?@bjorn3 @petrochenkov
rust-bors Bot pushed a commit that referenced this pull request Jun 16, 2026
…uwer

Rollup of 10 pull requests

Successful merges:

 - #155535 (export symbols: support macos/windows(32/64))
 - #156538 (Refactor `AliasTy`. `AliasTerm` & `UnevaluatedConst` to use `Alias`)
 - #156807 (Add `T: PartialEq` bounds to derived `StructuralPartialEq` impls.)
 - #156950 (Staticlib rename internal symbols)
 - #157702 (Add expansion info to implied bounds)
 - #155616 (constify `TryFrom<Vec>` for array)
 - #156226 (diagnostics: point to coroutine body on higher-ranked auto trait errors)
 - #157873 (mips: set llvm_args -mno-check-zero-division for all mips targets)
 - #157953 (fix(rustc_codegen_ssa): Use cg_operand for Freeze check)
 - #157958 (doc: Document `-Zlint-rust-version`)
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Jun 16, 2026
…-symbols, r=petrochenkov

Staticlib rename internal symbols

Follow-up to rust-lang#155338.

`-Zstaticlib-rename-internal-symbols` appends a crate-specific suffix (`_rs{StableCrateId}`) to non-exported symbols, resolving duplicate symbol conflicts when linking multiple Rust staticlibs into the same binary.

The implementation collects all defined `GLOBAL/WEAK` symbol names not in the exported set across all .o files, then renames them by extending the strtab and patching symbol name offsets. When combined with `-Zstaticlib-hide-internal-symbols`, the renamed symbols also receive `STV_HIDDEN` visibility.

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.

r?@bjorn3 @petrochenkov
rust-bors Bot pushed a commit that referenced this pull request Jun 16, 2026
…uwer

Rollup of 13 pull requests

Successful merges:

 - #156538 (Refactor `AliasTy`. `AliasTerm` & `UnevaluatedConst` to use `Alias`)
 - #156807 (Add `T: PartialEq` bounds to derived `StructuralPartialEq` impls.)
 - #156950 (Staticlib rename internal symbols)
 - #156983 (Add `io::Read::read_le` and `io::Read::read_be`)
 - #157306 (tests: codegen-llvm: Expect the new mangling scheme in bpf-abi-indirect-return)
 - #157702 (Add expansion info to implied bounds)
 - #155616 (constify `TryFrom<Vec>` for array)
 - #156226 (diagnostics: point to coroutine body on higher-ranked auto trait errors)
 - #157870 (std: sys: solid: clamp connect_timeout tv_sec instead of truncating)
 - #157952 (Configure Renovate for GitHub Actions)
 - #157953 (fix(rustc_codegen_ssa): Use cg_operand for Freeze check)
 - #157958 (doc: Document `-Zlint-rust-version`)
 - #157974 (Rename `errors.rs` file to `diagnostics.rs` (11/N))
@rust-bors rust-bors Bot merged commit 3aa45f9 into rust-lang:main Jun 16, 2026
14 checks passed
@rustbot rustbot added this to the 1.98.0 milestone Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-run-make Area: port run-make Makefiles to rmake.rs S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants