[BOLT] Fix C++ exceptions for shared objects

Summary:
Fix several issues to make C++ exceptions work in shared objects:
  * Set MCObjectFileInfo PIC type based on the input binary type.
  * Support indirect (DW_EH_PE_indirect) encoding while writing
    exception Type Table.
  * Use different LPStart value and landing pad encoding for .so's.
  * Disable splitting of exception-handling code for .so's because of
    the new encoding.

(cherry picked from FBD24698765)
This commit is contained in:
Maksim Panchenko
2020-11-04 11:44:02 -08:00
parent c1bb4dcb2b
commit 4f4239ceba
7 changed files with 124 additions and 72 deletions

View File

@@ -160,7 +160,7 @@ MCPlusBuilder *createMCPlusBuilder(const Triple::ArchType Arch,
/// Create BinaryContext for a given architecture \p ArchName and
/// triple \p TripleName.
std::unique_ptr<BinaryContext>
BinaryContext::createBinaryContext(ObjectFile *File,
BinaryContext::createBinaryContext(ObjectFile *File, bool IsPIC,
std::unique_ptr<DWARFContext> DwCtx) {
StringRef ArchName = "";
StringRef FeaturesStr = "";
@@ -223,7 +223,7 @@ BinaryContext::createBinaryContext(ObjectFile *File,
llvm::make_unique<MCObjectFileInfo>();
std::unique_ptr<MCContext> Ctx =
llvm::make_unique<MCContext>(AsmInfo.get(), MRI.get(), MOFI.get());
MOFI->InitMCObjectFileInfo(*TheTriple, /*PIC=*/false, *Ctx);
MOFI->InitMCObjectFileInfo(*TheTriple, IsPIC, *Ctx);
std::unique_ptr<MCDisassembler> DisAsm(
TheTarget->createMCDisassembler(*STI, *Ctx));
@@ -279,6 +279,8 @@ BinaryContext::createBinaryContext(ObjectFile *File,
BC->setFilename(File->getFileName());
BC->HasFixedLoadAddress = !IsPIC;
return BC;
}

View File

@@ -206,7 +206,8 @@ class BinaryContext {
public:
static std::unique_ptr<BinaryContext>
createBinaryContext(ObjectFile *File, std::unique_ptr<DWARFContext> DwCtx);
createBinaryContext(ObjectFile *File, bool IsPIC,
std::unique_ptr<DWARFContext> DwCtx);
/// [start memory address] -> [segment info] mapping.
std::map<uint64_t, SegmentInfo> SegmentMapInfo;
@@ -513,7 +514,9 @@ public:
/// Indicates if relocations are available for usage.
bool HasRelocations{false};
/// Is the binary always loaded at a fixed address.
/// 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.
bool HasFixedLoadAddress{true};
/// True if the binary has no dynamic dependencies, i.e., if it was statically

View File

@@ -792,28 +792,49 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, bool EmitColdPart) {
Streamer.EmitLabel(LSDASymbol);
// Corresponding FDE start.
const auto *StartSymbol = EmitColdPart ? BF.getColdSymbol() : BF.getSymbol();
const MCSymbol *StartSymbol = EmitColdPart ? BF.getColdSymbol()
: BF.getSymbol();
// Emit the LSDA header.
// If LPStart is omitted, then the start of the FDE is used as a base for
// landing pad displacements. Then if a cold fragment starts with
// a landing pad, this means that the first landing pad offset will be 0.
// As a result, an exception handling runtime will ignore this landing pad,
// As a result, the exception handling runtime will ignore this landing pad
// because zero offset denotes the absence of a landing pad.
// For this reason, we emit LPStart value of 0 and output an absolute value
// of the landing pad in the table.
// For this reason, when the binary has fixed starting address we emit LPStart
// as 0 and output the absolute value of the landing pad in the table.
//
// FIXME: this may break PIEs and DSOs where the base address is not 0.
Streamer.EmitIntValue(dwarf::DW_EH_PE_udata4, 1); // LPStart format
Streamer.EmitIntValue(0, 4);
auto emitLandingPad = [&](const MCSymbol *LPSymbol) {
if (!LPSymbol) {
Streamer.EmitIntValue(0, 4);
return;
}
Streamer.EmitSymbolValue(LPSymbol, 4);
};
// If the base address can change, we cannot use absolute addresses for
// landing pads (at least not without runtime relocations). Hence, we fall
// back to emitting landing pads relative to the FDE start.
// As we are emitting label differences, we have to guarantee both labels are
// defined in the same section and hence cannot place the landing pad into a
// cold fragment when the corresponding call site is in the hot fragment.
// Because of this issue and the previously described issue of possible
// zero-offset landing pad we disable splitting of exception-handling
// code for shared objects.
std::function<void(const MCSymbol *)> emitLandingPad;
if (BC.HasFixedLoadAddress) {
Streamer.EmitIntValue(dwarf::DW_EH_PE_udata4, 1); // LPStart format
Streamer.EmitIntValue(0, 4); // LPStart
emitLandingPad = [&](const MCSymbol *LPSymbol) {
if (!LPSymbol)
Streamer.EmitIntValue(0, 4);
else
Streamer.EmitSymbolValue(LPSymbol, 4);
};
} else {
assert(!EmitColdPart &&
"cannot have exceptions in cold fragment for shared object");
Streamer.EmitIntValue(dwarf::DW_EH_PE_omit, 1); // LPStart format
emitLandingPad = [&](const MCSymbol *LPSymbol) {
if (!LPSymbol)
Streamer.EmitIntValue(0, 4);
else
Streamer.emitAbsoluteSymbolDiff(LPSymbol, StartSymbol, 4);
};
}
Streamer.EmitIntValue(TTypeEncoding, 1); // TType format
@@ -873,25 +894,26 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, bool EmitColdPart) {
for (auto const &Byte : BF.getLSDAActionTable()) {
Streamer.EmitIntValue(Byte, 1);
}
assert(!(TTypeEncoding & dwarf::DW_EH_PE_indirect) &&
"indirect type info encoding is not supported yet");
for (int Index = BF.getLSDATypeTable().size() - 1; Index >= 0; --Index) {
// Note: the address could be an indirect one.
const auto TypeAddress = BF.getLSDATypeTable()[Index];
const auto &TypeTable = (TTypeEncoding & dwarf::DW_EH_PE_indirect)
? BF.getLSDATypeAddressTable()
: BF.getLSDATypeTable();
assert(TypeTable.size() == BF.getLSDATypeTable().size() &&
"indirect type table size mismatch");
for (int Index = TypeTable.size() - 1; Index >= 0; --Index) {
const uint64_t TypeAddress = TypeTable[Index];
switch (TTypeEncoding & 0x70) {
default:
llvm_unreachable("unsupported TTypeEncoding");
case 0:
case dwarf::DW_EH_PE_absptr:
Streamer.EmitIntValue(TypeAddress, TTypeEncodingSize);
break;
case dwarf::DW_EH_PE_pcrel: {
if (TypeAddress) {
const auto *TypeSymbol =
BC.getOrCreateGlobalSymbol(TypeAddress,
"TI",
TTypeEncodingSize,
TTypeAlignment);
auto *DotSymbol = BC.Ctx->createTempSymbol();
const MCSymbol *TypeSymbol =
BC.getOrCreateGlobalSymbol(TypeAddress, "TI", 0, TTypeAlignment);
MCSymbol *DotSymbol = BC.Ctx->createTempSymbol();
Streamer.EmitLabel(DotSymbol);
const auto *SubDotExpr = MCBinaryExpr::createSub(
MCSymbolRefExpr::create(TypeSymbol, *BC.Ctx),

View File

@@ -481,12 +481,20 @@ private:
std::vector<CallSite> CallSites;
std::vector<CallSite> ColdCallSites;
/// Binary blobs reprsenting action, type, and type index tables for this
/// Binary blobs representing action, type, and type index tables for this
/// function' LSDA (exception handling).
ArrayRef<uint8_t> LSDAActionTable;
std::vector<uint64_t> LSDATypeTable;
ArrayRef<uint8_t> LSDATypeIndexTable;
using LSDATypeTableTy = std::vector<uint64_t>;
/// Vector of addresses of types referenced by LSDA.
LSDATypeTableTy LSDATypeTable;
/// Vector of addresses of entries in LSDATypeTable used for indirect
/// addressing.
LSDATypeTableTy LSDATypeAddressTable;
/// Marking for the beginning of language-specific data area for the function.
MCSymbol *LSDASymbol{nullptr};
MCSymbol *ColdLSDASymbol{nullptr};
@@ -692,6 +700,7 @@ private:
clearList(CallSites);
clearList(ColdCallSites);
clearList(LSDATypeTable);
clearList(LSDATypeAddressTable);
clearList(MoveRelocations);
@@ -1487,10 +1496,14 @@ public:
return LSDAActionTable;
}
const std::vector<uint64_t> &getLSDATypeTable() const {
const LSDATypeTableTy &getLSDATypeTable() const {
return LSDATypeTable;
}
const LSDATypeTableTy &getLSDATypeAddressTable() const {
return LSDATypeAddressTable;
}
const ArrayRef<uint8_t> getLSDATypeIndexTable() const {
return LSDATypeIndexTable;
}

View File

@@ -170,11 +170,11 @@ void BinaryFunction::parseLSDA(ArrayRef<uint8_t> LSDASectionData,
outs() << '\n';
}
HasEHRanges = CallSitePtr < CallSiteTableEnd;
uint64_t RangeBase = getAddress();
this->HasEHRanges = CallSitePtr < CallSiteTableEnd;
const uint64_t RangeBase = getAddress();
while (CallSitePtr < CallSiteTableEnd) {
uint64_t Start = *Data.getEncodedPointer(&CallSitePtr, CallSiteEncoding,
CallSitePtr + LSDASectionAddress);
CallSitePtr + LSDASectionAddress);
uint64_t Length = *Data.getEncodedPointer(
&CallSitePtr, CallSiteEncoding, CallSitePtr + LSDASectionAddress);
uint64_t LandingPad = *Data.getEncodedPointer(
@@ -229,14 +229,13 @@ void BinaryFunction::parseLSDA(ArrayRef<uint8_t> LSDASectionData,
} while (II != IE && II->first < Start + Length);
if (ActionEntry != 0) {
auto printType = [&] (int Index, raw_ostream &OS) {
auto printType = [&](int Index, raw_ostream &OS) {
assert(Index > 0 && "only positive indices are valid");
uint32_t TTEntry = TypeTableStart - Index * TTypeEncodingSize;
const auto TTEntryAddress = TTEntry + LSDASectionAddress;
uint32_t TypeAddress =
const uint64_t TTEntryAddress = TTEntry + LSDASectionAddress;
uint64_t TypeAddress =
*Data.getEncodedPointer(&TTEntry, TTypeEncoding, TTEntryAddress);
if ((TTypeEncoding & DW_EH_PE_pcrel) &&
(TypeAddress == TTEntryAddress)) {
if ((TTypeEncoding & DW_EH_PE_pcrel) && TypeAddress == TTEntryAddress) {
TypeAddress = 0;
}
if (TypeAddress == 0) {
@@ -257,12 +256,12 @@ void BinaryFunction::parseLSDA(ArrayRef<uint8_t> LSDASectionData,
if (opts::PrintExceptions)
outs() << " actions: ";
uint32_t ActionPtr = ActionTableStart + ActionEntry - 1;
long long ActionType;
long long ActionNext;
int64_t ActionType;
int64_t ActionNext;
auto Sep = "";
do {
ActionType = Data.getSLEB128(&ActionPtr);
auto Self = ActionPtr;
const uint32_t Self = ActionPtr;
ActionNext = Data.getSLEB128(&ActionPtr);
if (opts::PrintExceptions)
outs() << Sep << "(" << ActionType << ", " << ActionNext << ") ";
@@ -324,13 +323,15 @@ void BinaryFunction::parseLSDA(ArrayRef<uint8_t> LSDASectionData,
const auto TTEntryAddress = TTEntry + LSDASectionAddress;
uint64_t TypeAddress =
*Data.getEncodedPointer(&TTEntry, TTypeEncoding, TTEntryAddress);
if ((TTypeEncoding & DW_EH_PE_pcrel) && (TypeAddress == TTEntryAddress)) {
if ((TTypeEncoding & DW_EH_PE_pcrel) && (TypeAddress == TTEntryAddress))
TypeAddress = 0;
}
if (TypeAddress && (TTypeEncoding & DW_EH_PE_indirect)) {
auto PointerOrErr = BC.getPointerAtAddress(TypeAddress);
assert(PointerOrErr && "failed to decode indirect address");
TypeAddress = *PointerOrErr;
if (TTypeEncoding & DW_EH_PE_indirect) {
LSDATypeAddressTable.emplace_back(TypeAddress);
if (TypeAddress) {
auto PointerOrErr = BC.getPointerAtAddress(TypeAddress);
assert(PointerOrErr && "failed to decode indirect address");
TypeAddress = *PointerOrErr;
}
}
LSDATypeTable.emplace_back(TypeAddress);
}

View File

@@ -58,9 +58,10 @@ MachORewriteInstance::MachORewriteInstance(object::MachOObjectFile *InputFile,
StringRef ToolPath)
: InputFile(InputFile), ToolPath(ToolPath),
BC(BinaryContext::createBinaryContext(
InputFile, DWARFContext::create(*InputFile, nullptr,
DWARFContext::defaultErrorHandler, "",
false))) {}
InputFile, /* IsPIC */ true,
DWARFContext::create(*InputFile, nullptr,
DWARFContext::defaultErrorHandler, "",
false))) {}
void MachORewriteInstance::readSpecialSections() {
for (const auto &Section : InputFile->sections()) {

View File

@@ -448,18 +448,32 @@ bool refersToReorderedSection(ErrorOr<BinarySection &> Section) {
RewriteInstance::RewriteInstance(ELFObjectFileBase *File, const int Argc,
const char *const *Argv, StringRef ToolPath)
: InputFile(File), Argc(Argc), Argv(Argv), ToolPath(ToolPath),
BC(BinaryContext::createBinaryContext(
File,
DWARFContext::create(*File, nullptr,
DWARFContext::defaultErrorHandler, "", false))),
BAT(llvm::make_unique<BoltAddressTranslation>(*BC)),
SHStrTab(StringTableBuilder::ELF) {
if (opts::UpdateDebugSections) {
auto ELF64LEFile = dyn_cast<ELF64LEObjectFile>(InputFile);
if (!ELF64LEFile) {
errs() << "BOLT-ERROR: only 64-bit LE ELF binaries are supported\n";
exit(1);
}
bool IsPIC = false;
const ELFFile<ELF64LE> *Obj = ELF64LEFile->getELFFile();
if (Obj->getHeader()->e_type != ELF::ET_EXEC) {
outs() << "BOLT-INFO: shared object or position-independent executable "
"detected\n";
IsPIC = true;
}
BC = BinaryContext::createBinaryContext(File,IsPIC,
DWARFContext::create(*File, nullptr,
DWARFContext::defaultErrorHandler, "", false));
BAT = llvm::make_unique<BoltAddressTranslation>(*BC);
if (opts::UpdateDebugSections)
DebugInfoRewriter = llvm::make_unique<DWARFRewriter>(*BC, SectionPatchers);
}
if (opts::Hugify) {
if (opts::Hugify)
BC->setRuntimeLibrary(llvm::make_unique<HugifyRuntimeLibrary>());
}
}
RewriteInstance::~RewriteInstance() {}
@@ -509,16 +523,7 @@ void RewriteInstance::discoverStorage() {
BC->EFMM.reset(new ExecutableFileMemoryManager(*BC, /*AllowStubs*/ false));
auto ELF64LEFile = dyn_cast<ELF64LEObjectFile>(InputFile);
if (!ELF64LEFile) {
errs() << "BOLT-ERROR: only 64-bit LE ELF binaries are supported\n";
exit(1);
}
auto Obj = ELF64LEFile->getELFFile();
if (Obj->getHeader()->e_type != ELF::ET_EXEC) {
outs() << "BOLT-INFO: shared object or position-independent executable "
"detected\n";
BC->HasFixedLoadAddress = false;
}
const auto *Obj = ELF64LEFile->getELFFile();
BC->StartFunctionAddress = Obj->getHeader()->e_entry;
@@ -1650,6 +1655,11 @@ void RewriteInstance::adjustCommandLineOptions() {
opts::SplitEH = false;
}
if (opts::SplitEH && !BC->HasFixedLoadAddress) {
errs() << "BOLT-WARNING: disabling -split-eh for shared object\n";
opts::SplitEH = false;
}
if (opts::StrictMode && !BC->HasRelocations) {
errs() << "BOLT-WARNING: disabling strict mode (-strict) in non-relocation "
"mode\n";