Skip to content

Commit 9230118

Browse files
authored
[BOLT] Use compact EH format for fixed-address executables (#117274)
Use ULEB128 format for emitting LSDAs for fixed-address executables, similar to what we use for PIEs/DSOs. Main difference is that we don't use landing pad trampolines when landing pads are not contained in a single fragment. Instead, we fallback to emitting larger fixed-address LSDAs, which is still better than adding trampoline instructions.
1 parent 562c93a commit 9230118

File tree

3 files changed

+98
-10
lines changed

3 files changed

+98
-10
lines changed

bolt/lib/Core/BinaryEmitter.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -923,9 +923,21 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {
923923
// As a solution, for fixed-address binaries we set LPStart to 0, and for
924924
// position-independent binaries we offset LP start by one byte.
925925
bool NeedsLPAdjustment = false;
926-
const MCSymbol *LPStartSymbol = nullptr;
927926
std::function<void(const MCSymbol *)> emitLandingPad;
928-
if (BC.HasFixedLoadAddress) {
927+
928+
// Check if there's a symbol associated with a landing pad fragment.
929+
const MCSymbol *LPStartSymbol = BF.getLPStartSymbol(FF.getFragmentNum());
930+
if (!LPStartSymbol) {
931+
// Since landing pads are not in the same fragment, we fall back to emitting
932+
// absolute addresses for this FDE.
933+
if (opts::Verbosity >= 2) {
934+
BC.outs() << "BOLT-INFO: falling back to generating absolute-address "
935+
<< "exception ranges for " << BF << '\n';
936+
}
937+
938+
assert(BC.HasFixedLoadAddress &&
939+
"Cannot emit absolute-address landing pads for PIE/DSO");
940+
929941
Streamer.emitIntValue(dwarf::DW_EH_PE_udata4, 1); // LPStart format
930942
Streamer.emitIntValue(0, 4); // LPStart
931943
emitLandingPad = [&](const MCSymbol *LPSymbol) {
@@ -936,9 +948,6 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {
936948
};
937949
} else {
938950
std::optional<FragmentNum> LPFN = BF.getLPFragment(FF.getFragmentNum());
939-
LPStartSymbol = BF.getLPStartSymbol(FF.getFragmentNum());
940-
assert(LPFN && LPStartSymbol && "Expected LPStart symbol to be set");
941-
942951
const FunctionFragment &LPFragment = BF.getLayout().getFragment(*LPFN);
943952
NeedsLPAdjustment =
944953
(!LPFragment.empty() && LPFragment.front()->isLandingPad());
@@ -987,7 +996,7 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {
987996

988997
// Emit encoding of entries in the call site table. The format is used for the
989998
// call site start, length, and corresponding landing pad.
990-
if (BC.HasFixedLoadAddress)
999+
if (!LPStartSymbol)
9911000
Streamer.emitIntValue(dwarf::DW_EH_PE_sdata4, 1);
9921001
else
9931002
Streamer.emitIntValue(dwarf::DW_EH_PE_uleb128, 1);
@@ -1007,7 +1016,7 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {
10071016

10081017
// Start of the range is emitted relative to the start of current
10091018
// function split part.
1010-
if (BC.HasFixedLoadAddress) {
1019+
if (!LPStartSymbol) {
10111020
Streamer.emitAbsoluteSymbolDiff(BeginLabel, StartSymbol, 4);
10121021
Streamer.emitAbsoluteSymbolDiff(EndLabel, BeginLabel, 4);
10131022
} else {

bolt/lib/Passes/SplitFunctions.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -901,7 +901,7 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
901901
// have to be placed in the same fragment. When we split them, create
902902
// trampoline landing pads that will redirect the execution to real LPs.
903903
TrampolineSetType Trampolines;
904-
if (!BC.HasFixedLoadAddress && BF.hasEHRanges() && BF.isSplit()) {
904+
if (BF.hasEHRanges() && BF.isSplit()) {
905905
// If all landing pads for this fragment are grouped in one (potentially
906906
// different) fragment, we can set LPStart to the start of that fragment
907907
// and avoid trampoline code.
@@ -925,8 +925,12 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
925925
} else if (LandingPadFragments.size() == 1) {
926926
BF.setLPFragment(FF.getFragmentNum(), LandingPadFragments.front());
927927
} else {
928-
NeedsTrampolines = true;
929-
break;
928+
if (!BC.HasFixedLoadAddress) {
929+
NeedsTrampolines = true;
930+
break;
931+
} else {
932+
BF.setLPFragment(FF.getFragmentNum(), std::nullopt);
933+
}
930934
}
931935
}
932936

bolt/test/X86/exceptions-compact.s

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
## Check that llvm-bolt is able to overwrite LSDA in ULEB128 format in-place for
2+
## all types of binaries.
3+
4+
# REQUIRES: system-linux
5+
6+
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %s -o %t.o
7+
# RUN: ld.lld --no-pie %t.o -o %t.exe -q
8+
# RUN: ld.lld --pie %t.o -o %t.pie -q
9+
# RUN: ld.lld --shared %t.o -o %t.so -q
10+
# RUN: llvm-bolt %t.exe -o %t.bolt --strict \
11+
# RUN: | FileCheck --check-prefix=CHECK-BOLT %s
12+
# RUN: llvm-bolt %t.pie -o %t.pie.bolt --strict \
13+
# RUN: | FileCheck --check-prefix=CHECK-BOLT %s
14+
# RUN: llvm-bolt %t.so -o %t.so.bolt --strict \
15+
# RUN: | FileCheck --check-prefix=CHECK-BOLT %s
16+
17+
# CHECK-BOLT: rewriting .gcc_except_table in-place
18+
19+
# RUN: llvm-readelf -WS %t.bolt | FileCheck --check-prefix=CHECK-ELF %s
20+
# RUN: llvm-readelf -WS %t.pie.bolt | FileCheck --check-prefix=CHECK-ELF %s
21+
# RUN: llvm-readelf -WS %t.so.bolt | FileCheck --check-prefix=CHECK-ELF %s
22+
23+
# CHECK-ELF-NOT: .bolt.org.gcc_except_table
24+
25+
.text
26+
.global foo
27+
.type foo, %function
28+
foo:
29+
.cfi_startproc
30+
ret
31+
.cfi_endproc
32+
.size foo, .-foo
33+
34+
.globl _start
35+
.type _start, %function
36+
_start:
37+
.Lfunc_begin0:
38+
.cfi_startproc
39+
.cfi_lsda 27, .Lexception0
40+
call foo
41+
.Ltmp0:
42+
call foo
43+
.Ltmp1:
44+
ret
45+
46+
## Landing pads.
47+
.LLP1:
48+
ret
49+
.LLP0:
50+
ret
51+
52+
.cfi_endproc
53+
.Lfunc_end0:
54+
.size _start, .-_start
55+
56+
## EH table.
57+
.section .gcc_except_table,"a",@progbits
58+
.p2align 2
59+
GCC_except_table0:
60+
.Lexception0:
61+
.byte 255 # @LPStart Encoding = omit
62+
.byte 255 # @TType Encoding = omit
63+
.byte 1 # Call site Encoding = uleb128
64+
.uleb128 .Lcst_end0-.Lcst_begin0
65+
.Lcst_begin0:
66+
.uleb128 .Lfunc_begin0-.Lfunc_begin0 # >> Call Site 1 <<
67+
.uleb128 .Ltmp0-.Lfunc_begin0 # Call between .Lfunc_begin0 and .Ltmp0
68+
.uleb128 .LLP0-.Lfunc_begin0 # jumps to .LLP0
69+
.byte 0 # On action: cleanup
70+
.uleb128 .Ltmp0-.Lfunc_begin0 # >> Call Site 2 <<
71+
.uleb128 .Ltmp1-.Ltmp0 # Call between .Ltmp0 and .Ltmp1
72+
.uleb128 .LLP1-.Lfunc_begin0 # jumps to .LLP1
73+
.byte 0 # On action: cleanup
74+
.Lcst_end0:
75+

0 commit comments

Comments
 (0)