Skip to content

Commit 009a6b9

Browse files
authored
Rollup merge of rust-lang#89581 - jblazquez:master, r=Mark-Simulacrum
Add -Z no-unique-section-names to reduce ELF header bloat. This change adds a new compiler flag that can help reduce the size of ELF binaries that contain many functions. By default, when enabling function sections (which is the default for most targets), the LLVM backend will generate different section names for each function. For example, a function `func` would generate a section called `.text.func`. Normally this is fine because the linker will merge all those sections into a single one in the binary. However, starting with [LLVM 12](llvm/llvm-project@ee5d1a04), the backend will also generate unique section names for exception handling, resulting in thousands of `.gcc_except_table.*` sections ending up in the final binary because some linkers like LLD don't currently merge or strip these EH sections (see discussion [here](https://reviews.llvm.org/D83655)). This can bloat the ELF headers and string table significantly in binaries that contain many functions. The new option is analogous to Clang's `-fno-unique-section-names`, and instructs LLVM to generate the same `.text` and `.gcc_except_table` section for each function, resulting in a smaller final binary. The motivation to add this new option was because we have a binary that ended up with so many ELF sections (over 65,000) that it broke some existing ELF tools, which couldn't handle so many sections. Here's our old binary: ``` $ readelf --sections old.elf | head -1 There are 71746 section headers, starting at offset 0x2a246508: $ readelf --sections old.elf | grep shstrtab [71742] .shstrtab STRTAB 0000000000000000 2977204c ad44bb 00 0 0 1 ``` That's an 11MB+ string table. Here's the new binary using this option: ``` $ readelf --sections new.elf | head -1 There are 43 section headers, starting at offset 0x29143ca8: $ readelf --sections new.elf | grep shstrtab [40] .shstrtab STRTAB 0000000000000000 29143acc 0001db 00 0 0 1 ``` The whole binary size went down by over 20MB, which is quite significant.
2 parents bd61c4d + 4ed846a commit 009a6b9

File tree

6 files changed

+17
-0
lines changed

6 files changed

+17
-0
lines changed

compiler/rustc_codegen_llvm/src/back/write.rs

+2
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ pub fn target_machine_factory(
161161
let ffunction_sections =
162162
sess.opts.debugging_opts.function_sections.unwrap_or(sess.target.function_sections);
163163
let fdata_sections = ffunction_sections;
164+
let funique_section_names = !sess.opts.debugging_opts.no_unique_section_names;
164165

165166
let code_model = to_llvm_code_model(sess.code_model());
166167

@@ -205,6 +206,7 @@ pub fn target_machine_factory(
205206
use_softfp,
206207
ffunction_sections,
207208
fdata_sections,
209+
funique_section_names,
208210
trap_unreachable,
209211
singlethread,
210212
asm_comments,

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2187,6 +2187,7 @@ extern "C" {
21872187
UseSoftFP: bool,
21882188
FunctionSections: bool,
21892189
DataSections: bool,
2190+
UniqueSectionNames: bool,
21902191
TrapUnreachable: bool,
21912192
Singlethread: bool,
21922193
AsmComments: bool,

compiler/rustc_interface/src/tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,7 @@ fn test_debugging_options_tracking_hash() {
744744
tracked!(new_llvm_pass_manager, Some(true));
745745
tracked!(no_generate_arange_section, true);
746746
tracked!(no_link, true);
747+
tracked!(no_unique_section_names, true);
747748
tracked!(no_profiler_runtime, true);
748749
tracked!(osx_rpath_install_name, true);
749750
tracked!(panic_abort_tests, true);

compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
462462
LLVMRustCodeGenOptLevel RustOptLevel, bool UseSoftFloat,
463463
bool FunctionSections,
464464
bool DataSections,
465+
bool UniqueSectionNames,
465466
bool TrapUnreachable,
466467
bool Singlethread,
467468
bool AsmComments,
@@ -491,6 +492,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
491492
}
492493
Options.DataSections = DataSections;
493494
Options.FunctionSections = FunctionSections;
495+
Options.UniqueSectionNames = UniqueSectionNames;
494496
Options.MCOptions.AsmVerbose = AsmComments;
495497
Options.MCOptions.PreserveAsmComments = AsmComments;
496498
Options.MCOptions.ABIName = ABIStr;

compiler/rustc_session/src/options.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,8 @@ options! {
12141214
"compile without linking"),
12151215
no_parallel_llvm: bool = (false, parse_no_flag, [UNTRACKED],
12161216
"run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)"),
1217+
no_unique_section_names: bool = (false, parse_bool, [TRACKED],
1218+
"do not use unique names for text and data sections when -Z function-sections is used"),
12171219
no_profiler_runtime: bool = (false, parse_no_flag, [TRACKED],
12181220
"prevent automatic injection of the profiler_builtins crate"),
12191221
normalize_docs: bool = (false, parse_bool, [TRACKED],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# `no-unique-section-names`
2+
3+
------------------------
4+
5+
This flag currently applies only to ELF-based targets using the LLVM codegen backend. It prevents the generation of unique ELF section names for each separate code and data item when `-Z function-sections` is also in use, which is the default for most targets. This option can reduce the size of object files, and depending on the linker, the final ELF binary as well.
6+
7+
For example, a function `func` will by default generate a code section called `.text.func`. Normally this is fine because the linker will merge all those `.text.*` sections into a single one in the binary. However, starting with [LLVM 12](https://github.com/llvm/llvm-project/commit/ee5d1a04), the backend will also generate unique section names for exception handling, so you would see a section name of `.gcc_except_table.func` in the object file and potentially in the final ELF binary, which could add significant bloat to programs that contain many functions.
8+
9+
This flag instructs LLVM to use the same `.text` and `.gcc_except_table` section name for each function, and it is analogous to Clang's `-fno-unique-section-names` option.

0 commit comments

Comments
 (0)