[llvm] [BOLT] Update LSDA encoding for x86-64 large code model (PR #190685)
Farid Zakaria via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 9 21:20:45 PDT 2026
https://github.com/fzakaria updated https://github.com/llvm/llvm-project/pull/190685
>From e1fe2e49038deb7a2fa62e9afa4a20a9eb0402aa Mon Sep 17 00:00:00 2001
From: Farid Zakaria <fmzakari at fb.com>
Date: Thu, 9 Apr 2026 21:19:44 -0700
Subject: [PATCH] [BOLT] Update LSDA encoding for x86-64 large code model
BOLT hardcoded 4-byte LSDA (exception table) encoding for x86-64.
This is insufficient for large code model binaries where functions
in .ltext sections may be placed at addresses above 2GB, exceeding
the range of DW_EH_PE_udata4/DW_EH_PE_sdata4 encodings.
Detect large code model by the presence of .ltext sections, and add
a --large-code-model flag for manual override (e.g. for compilers
that don't emit .ltext). Update LSDAEncoding to use 8-byte pointers:
- Non-PIC: DW_EH_PE_absptr (8-byte absolute)
- PIC: DW_EH_PE_pcrel | DW_EH_PE_sdata8 (8-byte PC-relative)
This is the first step toward full large code model support in BOLT.
Without this change, BOLT-processed binaries with C++ exceptions in
.ltext would produce incorrect exception handling tables.
---
bolt/include/bolt/Core/BinaryContext.h | 9 ++
bolt/lib/Core/BinaryContext.cpp | 30 ++++---
bolt/lib/Rewrite/RewriteInstance.cpp | 13 +++
bolt/lib/Utils/CommandLineOpts.cpp | 6 ++
bolt/test/X86/lsda-encoding.s | 110 +++++++++++++++++++++++++
5 files changed, 157 insertions(+), 11 deletions(-)
create mode 100644 bolt/test/X86/lsda-encoding.s
diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h
index f8a0be0418433..b5932c09efdaf 100644
--- a/bolt/include/bolt/Core/BinaryContext.h
+++ b/bolt/include/bolt/Core/BinaryContext.h
@@ -726,6 +726,11 @@ class BinaryContext {
/// FunctionFragment::getFragmentNum() == FragmentNum::warm()
bool HasWarmSection{false};
+ /// Indicates if the binary should assume large code model
+ /// Can be triggered by the presence of .ltext sections if
+ // unspecified.
+ bool UseLargeCodeModel{false};
+
/// Is the binary always loaded at a fixed address. Shared objects and
/// position-independent executables (PIEs) are examples of binaries that
/// will have HasFixedLoadAddress set to false.
@@ -851,6 +856,10 @@ class BinaryContext {
/// enum Constants, e.g. DW_EH_PE_omit.
unsigned LSDAEncoding = dwarf::DW_EH_PE_omit;
+ /// Update LSDAEncoding for the binary taking into account
+ /// large code model and position-independent executables.
+ void updateLSDAEncoding();
+
BinaryContext(std::unique_ptr<MCContext> Ctx,
std::unique_ptr<DWARFContext> DwCtx,
std::unique_ptr<Triple> TheTriple,
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index 5b3baf1669624..278a0f2e555df 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -47,6 +47,8 @@ using namespace llvm;
namespace opts {
+extern cl::opt<bool> LargeCodeModel;
+
static cl::opt<bool>
NoHugePages("no-huge-pages",
cl::desc("use regular size pages for code alignment"),
@@ -256,16 +258,6 @@ Expected<std::unique_ptr<BinaryContext>> BinaryContext::createBinaryContext(
std::unique_ptr<MCObjectFileInfo> MOFI(
TheTarget->createMCObjectFileInfo(*Ctx, IsPIC));
Ctx->setObjectFileInfo(MOFI.get());
- // We do not support X86 Large code model. Change this in the future.
- bool Large = false;
- if (TheTriple.getArch() == llvm::Triple::aarch64)
- Large = true;
- unsigned LSDAEncoding =
- Large ? dwarf::DW_EH_PE_absptr : dwarf::DW_EH_PE_udata4;
- if (IsPIC) {
- LSDAEncoding = dwarf::DW_EH_PE_pcrel |
- (Large ? dwarf::DW_EH_PE_sdata8 : dwarf::DW_EH_PE_sdata4);
- }
std::unique_ptr<MCDisassembler> DisAsm(
TheTarget->createMCDisassembler(*STI, *Ctx));
@@ -303,7 +295,13 @@ Expected<std::unique_ptr<BinaryContext>> BinaryContext::createBinaryContext(
std::move(InstructionPrinter), std::move(MIA), nullptr, std::move(MRI),
std::move(DisAsm), Logger);
- BC->LSDAEncoding = LSDAEncoding;
+ // Use large code model encoding for AArch64 (always). For X86, this is
+ // updated after detecting .ltext if unset.
+ // Otherwise allow the user to force it via `--large-code-model` flag.
+ if (TheTriple.getArch() == llvm::Triple::aarch64)
+ BC->UseLargeCodeModel = true;
+ else if (opts::LargeCodeModel.getNumOccurrences())
+ BC->UseLargeCodeModel = opts::LargeCodeModel;
BC->MAB = std::unique_ptr<MCAsmBackend>(
BC->TheTarget->createMCAsmBackend(*BC->STI, *BC->MRI, MCTargetOptions()));
@@ -315,6 +313,8 @@ Expected<std::unique_ptr<BinaryContext>> BinaryContext::createBinaryContext(
BC->SymbolicDisAsm = std::unique_ptr<MCDisassembler>(
BC->TheTarget->createMCDisassembler(*BC->STI, *BC->Ctx));
+ BC->updateLSDAEncoding();
+
if (!BC->SymbolicDisAsm)
return createStringError(
make_error_code(std::errc::not_supported),
@@ -390,6 +390,14 @@ bool BinaryContext::validateHoles() const {
return Valid;
}
+void BinaryContext::updateLSDAEncoding() {
+ LSDAEncoding = HasFixedLoadAddress
+ ? dwarf::DW_EH_PE_absptr
+ : (dwarf::DW_EH_PE_pcrel |
+ (this->UseLargeCodeModel ? dwarf::DW_EH_PE_sdata8
+ : dwarf::DW_EH_PE_sdata4));
+}
+
void BinaryContext::updateObjectNesting(BinaryDataMapType::iterator GAI) {
const uint64_t Address = GAI->second->getAddress();
const uint64_t Size = GAI->second->getSize();
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index b3208ac024ae6..42363fccc849f 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -82,6 +82,7 @@ extern cl::opt<bool> Hugify;
extern cl::opt<bool> Instrument;
extern cl::opt<uint32_t> InstrumentationSleepTime;
extern cl::opt<bool> KeepNops;
+extern cl::opt<bool> LargeCodeModel;
extern cl::opt<bool> Lite;
extern cl::list<std::string> PrintOnly;
extern cl::opt<std::string> PrintOnlyFile;
@@ -2237,6 +2238,18 @@ Error RewriteInstance::readSpecialSections() {
"Use -update-debug-sections to keep it.\n";
}
+ // Detect large code model by the presence of .ltext sections.
+ if (opts::LargeCodeModel.getNumOccurrences() == 0 &&
+ BC->getUniqueSectionByName(".ltext")) {
+ BC->UseLargeCodeModel = true;
+ }
+
+ // Placed in a separate block so that we can check for it in lit tests
+ if (BC->TheTriple->getArch() == llvm::Triple::x86_64 && BC->UseLargeCodeModel) {
+ BC->outs() << "BOLT-INFO: large code model detected\n";
+ BC->updateLSDAEncoding();
+ }
+
HasTextRelocations = (bool)BC->getUniqueSectionByName(
".rela" + std::string(BC->getMainCodeSectionName()));
HasSymbolTable = (bool)BC->getUniqueSectionByName(".symtab");
diff --git a/bolt/lib/Utils/CommandLineOpts.cpp b/bolt/lib/Utils/CommandLineOpts.cpp
index b7eb209af8aca..cc00d5522a3f0 100644
--- a/bolt/lib/Utils/CommandLineOpts.cpp
+++ b/bolt/lib/Utils/CommandLineOpts.cpp
@@ -221,6 +221,12 @@ cl::opt<bool>
cl::desc("instrument code to generate accurate profile data"),
cl::cat(BoltOptCategory));
+cl::opt<bool> LargeCodeModel(
+ "large-code-model",
+ cl::desc("use large code model for exception handling encodings. "
+ "Auto-detected by the presence of .ltext sections otherwise."),
+ cl::cat(BoltCategory));
+
cl::opt<bool> Lite("lite", cl::desc("skip processing of cold functions"),
cl::cat(BoltCategory));
diff --git a/bolt/test/X86/lsda-encoding.s b/bolt/test/X86/lsda-encoding.s
new file mode 100644
index 0000000000000..9cdb2cfa8d401
--- /dev/null
+++ b/bolt/test/X86/lsda-encoding.s
@@ -0,0 +1,110 @@
+## Test that BOLT handles large code model LSDA encoding correctly:
+## 1. Auto-detection via .ltext sections
+## 2. Disabling auto-detection with --large-code-model=0
+## 3. Forcing large code model with --large-code-model flag
+
+# REQUIRES: system-linux
+
+## Build two variants: one with .ltext section, one without.
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux \
+# RUN: --defsym LTEXT=1 %s -o %t.ltext.o
+# RUN: ld.lld --no-pie %t.ltext.o -o %t.ltext.exe -q -e _start
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux \
+# RUN: --defsym LTEXT=0 %s -o %t.text.o
+# RUN: ld.lld --no-pie %t.text.o -o %t.text.exe -q -e _start
+
+## Test 1: Auto-detection via .ltext section.
+# RUN: llvm-bolt %t.ltext.exe -o %t.ltext.bolt --reorder-blocks=none 2>&1 \
+# RUN: | FileCheck %s --check-prefix=CHECK-BOLT
+# CHECK-BOLT: large code model detected
+
+# RUN: llvm-dwarfdump --eh-frame %t.ltext.bolt \
+# RUN: | FileCheck %s --check-prefix=CHECK-EH
+
+## Test 2: Disable large code model with --large-code-model=0, overriding
+## auto-detection even though .ltext is present.
+# RUN: llvm-bolt %t.ltext.exe -o %t.ltext.bolt2 --reorder-blocks=none \
+# RUN: --large-code-model=0 2>&1 \
+# RUN: | FileCheck %s --check-prefix=CHECK-DISABLED
+# CHECK-DISABLED-NOT: large code model detected
+
+# RUN: llvm-dwarfdump --eh-frame %t.ltext.bolt2 \
+# RUN: | FileCheck %s --check-prefix=CHECK-EH
+
+## Test 3: --large-code-model flag forces large code model on a binary
+## without .ltext sections.
+# RUN: llvm-bolt %t.text.exe -o %t.text.bolt --reorder-blocks=none \
+# RUN: --large-code-model 2>&1 \
+# RUN: | FileCheck %s --check-prefix=CHECK-BOLT
+
+# RUN: llvm-dwarfdump --eh-frame %t.text.bolt \
+# RUN: | FileCheck %s --check-prefix=CHECK-EH
+
+## Verify the BOLT-emitted CIE uses 8-byte LSDA encoding (DW_EH_PE_absptr
+## = 0x00) instead of the default 4-byte encoding (DW_EH_PE_sdata4 = 0x1B).
+## In the "zLR" augmentation data: [L-enc] [R-enc].
+# CHECK-EH: Augmentation: "zLR"
+# CHECK-EH: Augmentation data: 00 1B
+
+ .text
+ .globl 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.
+.LLP0:
+ ret
+.LLP1:
+ ret
+
+ .cfi_endproc
+.Lfunc_end0:
+ .size _start, .Lfunc_end0-_start
+
+## Exception table.
+ .section .gcc_except_table,"a", at progbits
+ .p2align 2
+.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
+ .uleb128 .LLP0-.Lfunc_begin0 # landing pad
+ .byte 0 # action: cleanup
+ .uleb128 .Ltmp0-.Lfunc_begin0 # Call Site 2
+ .uleb128 .Ltmp1-.Ltmp0
+ .uleb128 .LLP1-.Lfunc_begin0 # landing pad
+ .byte 0 # action: cleanup
+.Lcst_end0:
+
+## When LTEXT=1, emit large_func in .ltext to trigger auto-detection.
+.if LTEXT
+ .section .ltext,"axl", at progbits
+ .globl large_func
+ .type large_func, @function
+large_func:
+ .cfi_startproc
+ movl $42, %eax
+ retq
+ .cfi_endproc
+ .size large_func, .-large_func
+.endif
More information about the llvm-commits
mailing list