[BOLT] Preserve original LSDA type encoding

In non-pie binaries BOLT unconditionally converted type encoding
from indirect to absptr, which broke std exceptions since pointers
to their typeinfo were only assigned at runtime in .data section.
In this patch we preserve original encoding so that indirect
remains indirect and can be resolved at runtime, and absolute remains absolute.

Reviewed By: rafauler, maksfb

Differential Revision: https://reviews.llvm.org/D132484
This commit is contained in:
revunov.denis@huawei.com 2022-09-14 16:29:48 +00:00
parent f1848b0a0e
commit 553c238952
6 changed files with 55 additions and 11 deletions

View File

@ -388,6 +388,8 @@ public:
}
unsigned getDWARFEncodingSize(unsigned Encoding) {
if (Encoding == dwarf::DW_EH_PE_omit)
return 0;
switch (Encoding & 0x0f) {
default:
llvm_unreachable("unknown encoding");
@ -671,7 +673,6 @@ public:
/// DWARF encoding. Available encoding types defined in BinaryFormat/Dwarf.h
/// enum Constants, e.g. DW_EH_PE_omit.
unsigned TTypeEncoding = dwarf::DW_EH_PE_omit;
unsigned LSDAEncoding = dwarf::DW_EH_PE_omit;
BinaryContext(std::unique_ptr<MCContext> Ctx,

View File

@ -388,6 +388,9 @@ private:
/// Original LSDA address for the function.
uint64_t LSDAAddress{0};
/// Original LSDA type encoding
unsigned LSDATypeEncoding{dwarf::DW_EH_PE_omit};
/// Containing compilation unit for the function.
DWARFUnit *DwarfUnit{nullptr};
@ -1438,6 +1441,8 @@ public:
const LSDATypeTableTy &getLSDATypeTable() const { return LSDATypeTable; }
unsigned getLSDATypeEncoding() const { return LSDATypeEncoding; }
const LSDATypeTableTy &getLSDATypeAddressTable() const {
return LSDATypeAddressTable;
}

View File

@ -186,13 +186,9 @@ BinaryContext::createBinaryContext(const ObjectFile *File, bool IsPIC,
Large = true;
unsigned LSDAEncoding =
Large ? dwarf::DW_EH_PE_absptr : dwarf::DW_EH_PE_udata4;
unsigned TTypeEncoding =
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);
TTypeEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel |
(Large ? dwarf::DW_EH_PE_sdata8 : dwarf::DW_EH_PE_sdata4);
}
std::unique_ptr<MCDisassembler> DisAsm(
@ -236,7 +232,6 @@ BinaryContext::createBinaryContext(const ObjectFile *File, bool IsPIC,
std::move(InstructionPrinter), std::move(MIA), nullptr, std::move(MRI),
std::move(DisAsm));
BC->TTypeEncoding = TTypeEncoding;
BC->LSDAEncoding = LSDAEncoding;
BC->MAB = std::unique_ptr<MCAsmBackend>(

View File

@ -897,7 +897,7 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {
Streamer.switchSection(BC.MOFI->getLSDASection());
const unsigned TTypeEncoding = BC.TTypeEncoding;
const unsigned TTypeEncoding = BF.getLSDATypeEncoding();
const unsigned TTypeEncodingSize = BC.getDWARFEncodingSize(TTypeEncoding);
const uint16_t TTypeAlignment = 4;
@ -971,10 +971,11 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {
TTypeBaseOffset; // TType base offset
unsigned SizeAlign = (4 - TotalSize) & 3;
// Account for any extra padding that will be added to the call site table
// length.
Streamer.emitULEB128IntValue(TTypeBaseOffset,
/*PadTo=*/TTypeBaseOffsetSize + SizeAlign);
if (TTypeEncoding != dwarf::DW_EH_PE_omit)
// Account for any extra padding that will be added to the call site table
// length.
Streamer.emitULEB128IntValue(TTypeBaseOffset,
/*PadTo=*/TTypeBaseOffsetSize + SizeAlign);
// Emit the landing pad call site table. We use signed data4 since we can emit
// a landing pad in a different part of the split function that could appear

View File

@ -121,6 +121,7 @@ void BinaryFunction::parseLSDA(ArrayRef<uint8_t> LSDASectionData,
: *MaybeLPStart - Address;
const uint8_t TTypeEncoding = Data.getU8(&Offset);
LSDATypeEncoding = TTypeEncoding;
size_t TTypeEncodingSize = 0;
uintptr_t TTypeEnd = 0;
if (TTypeEncoding != DW_EH_PE_omit) {

View File

@ -0,0 +1,41 @@
// REQUIRES: system-linux
// RUN: %clangxx -no-pie -Wl,-q %s -o %t.exe
// RUN: llvm-bolt %t.exe -o %t.bolt.exe -lite=false
// RUN: not --crash %t.bolt.exe 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL
// CHECK-FAIL: Should pass one argument
// RUN: not %t.bolt.exe -1 | FileCheck %s --check-prefix=CHECK-BAD
// CHECK-BAD: Bad value
// RUN: not %t.bolt.exe 0 | FileCheck %s --check-prefix=CHECK-ZERO
// CHECK-ZERO: Value is zero
// RUN: %t.bolt.exe 1 | FileCheck %s --check-prefix=CHECK-GOOD
// CHECK-GOOD: Good value
#include <exception>
#include <iostream>
struct ValIsZero {
const char *error = "Value is zero\n";
};
int dummy(int arg) {
if (arg == 0)
throw ValIsZero();
if (arg > 0)
return 0;
else
throw std::out_of_range("Bad value");
}
int main(int argc, char **argv) {
if (argc != 2)
throw std::invalid_argument("Should pass one argument");
try {
dummy(std::strtol(argv[1], nullptr, 10));
} catch (std::out_of_range &e) {
std::cout << e.what() << "\n";
return 1;
} catch (ValIsZero &e) {
std::cout << e.error;
return 1;
}
std::cout << "Good value\n";
return 0;
}