Skip to content

[BOLT] Use compact EH format for fixed-address executables #117274

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions bolt/lib/Core/BinaryEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -923,9 +923,21 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {
// As a solution, for fixed-address binaries we set LPStart to 0, and for
// position-independent binaries we offset LP start by one byte.
bool NeedsLPAdjustment = false;
const MCSymbol *LPStartSymbol = nullptr;
std::function<void(const MCSymbol *)> emitLandingPad;
if (BC.HasFixedLoadAddress) {

// Check if there's a symbol associated with a landing pad fragment.
const MCSymbol *LPStartSymbol = BF.getLPStartSymbol(FF.getFragmentNum());
if (!LPStartSymbol) {
// Since landing pads are not in the same fragment, we fall back to emitting
// absolute addresses for this FDE.
if (opts::Verbosity >= 2) {
BC.outs() << "BOLT-INFO: falling back to generating absolute-address "
<< "exception ranges for " << BF << '\n';
}

assert(BC.HasFixedLoadAddress &&
"Cannot emit absolute-address landing pads for PIE/DSO");

Streamer.emitIntValue(dwarf::DW_EH_PE_udata4, 1); // LPStart format
Streamer.emitIntValue(0, 4); // LPStart
emitLandingPad = [&](const MCSymbol *LPSymbol) {
Expand All @@ -936,9 +948,6 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {
};
} else {
std::optional<FragmentNum> LPFN = BF.getLPFragment(FF.getFragmentNum());
LPStartSymbol = BF.getLPStartSymbol(FF.getFragmentNum());
assert(LPFN && LPStartSymbol && "Expected LPStart symbol to be set");

const FunctionFragment &LPFragment = BF.getLayout().getFragment(*LPFN);
NeedsLPAdjustment =
(!LPFragment.empty() && LPFragment.front()->isLandingPad());
Expand Down Expand Up @@ -987,7 +996,7 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {

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

// Start of the range is emitted relative to the start of current
// function split part.
if (BC.HasFixedLoadAddress) {
if (!LPStartSymbol) {
Streamer.emitAbsoluteSymbolDiff(BeginLabel, StartSymbol, 4);
Streamer.emitAbsoluteSymbolDiff(EndLabel, BeginLabel, 4);
} else {
Expand Down
10 changes: 7 additions & 3 deletions bolt/lib/Passes/SplitFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -901,7 +901,7 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
// have to be placed in the same fragment. When we split them, create
// trampoline landing pads that will redirect the execution to real LPs.
TrampolineSetType Trampolines;
if (!BC.HasFixedLoadAddress && BF.hasEHRanges() && BF.isSplit()) {
if (BF.hasEHRanges() && BF.isSplit()) {
// If all landing pads for this fragment are grouped in one (potentially
// different) fragment, we can set LPStart to the start of that fragment
// and avoid trampoline code.
Expand All @@ -925,8 +925,12 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
} else if (LandingPadFragments.size() == 1) {
BF.setLPFragment(FF.getFragmentNum(), LandingPadFragments.front());
} else {
NeedsTrampolines = true;
break;
if (!BC.HasFixedLoadAddress) {
NeedsTrampolines = true;
break;
} else {
BF.setLPFragment(FF.getFragmentNum(), std::nullopt);
}
}
}

Expand Down
75 changes: 75 additions & 0 deletions bolt/test/X86/exceptions-compact.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
## Check that llvm-bolt is able to overwrite LSDA in ULEB128 format in-place for
## all types of binaries.

# REQUIRES: system-linux

# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %s -o %t.o
# RUN: ld.lld --no-pie %t.o -o %t.exe -q
# RUN: ld.lld --pie %t.o -o %t.pie -q
# RUN: ld.lld --shared %t.o -o %t.so -q
# RUN: llvm-bolt %t.exe -o %t.bolt --strict \
# RUN: | FileCheck --check-prefix=CHECK-BOLT %s
# RUN: llvm-bolt %t.pie -o %t.pie.bolt --strict \
# RUN: | FileCheck --check-prefix=CHECK-BOLT %s
# RUN: llvm-bolt %t.so -o %t.so.bolt --strict \
# RUN: | FileCheck --check-prefix=CHECK-BOLT %s

# CHECK-BOLT: rewriting .gcc_except_table in-place

# RUN: llvm-readelf -WS %t.bolt | FileCheck --check-prefix=CHECK-ELF %s
# RUN: llvm-readelf -WS %t.pie.bolt | FileCheck --check-prefix=CHECK-ELF %s
# RUN: llvm-readelf -WS %t.so.bolt | FileCheck --check-prefix=CHECK-ELF %s

# CHECK-ELF-NOT: .bolt.org.gcc_except_table

.text
.global foo
.type foo, %function
foo:
.cfi_startproc
ret
.cfi_endproc
.size foo, .-foo

.globl _start
.type _start, %function
_start:
.Lfunc_begin0:
.cfi_startproc
.cfi_lsda 27, .Lexception0
call foo
.Ltmp0:
call foo
.Ltmp1:
ret

## Landing pads.
.LLP1:
ret
.LLP0:
ret

.cfi_endproc
.Lfunc_end0:
.size _start, .-_start

## EH table.
.section .gcc_except_table,"a",@progbits
.p2align 2
GCC_except_table0:
.Lexception0:
.byte 255 # @LPStart Encoding = omit
.byte 255 # @TType Encoding = omit
.byte 1 # Call site Encoding = uleb128
.uleb128 .Lcst_end0-.Lcst_begin0
.Lcst_begin0:
.uleb128 .Lfunc_begin0-.Lfunc_begin0 # >> Call Site 1 <<
.uleb128 .Ltmp0-.Lfunc_begin0 # Call between .Lfunc_begin0 and .Ltmp0
.uleb128 .LLP0-.Lfunc_begin0 # jumps to .LLP0
.byte 0 # On action: cleanup
.uleb128 .Ltmp0-.Lfunc_begin0 # >> Call Site 2 <<
.uleb128 .Ltmp1-.Ltmp0 # Call between .Ltmp0 and .Ltmp1
.uleb128 .LLP1-.Lfunc_begin0 # jumps to .LLP1
.byte 0 # On action: cleanup
.Lcst_end0:

Loading