llvm-project
563 строки · 21.6 Кб
1//===- llvm-cxxdump.cpp - Dump C++ data in an Object File -------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Dumps C++ data resident in object files and archives.
10//
11//===----------------------------------------------------------------------===//
12
13#include "llvm-cxxdump.h"14#include "Error.h"15#include "llvm/ADT/ArrayRef.h"16#include "llvm/MC/TargetRegistry.h"17#include "llvm/Object/Archive.h"18#include "llvm/Object/ObjectFile.h"19#include "llvm/Object/SymbolSize.h"20#include "llvm/Support/Debug.h"21#include "llvm/Support/Endian.h"22#include "llvm/Support/FileSystem.h"23#include "llvm/Support/InitLLVM.h"24#include "llvm/Support/TargetSelect.h"25#include "llvm/Support/WithColor.h"26#include "llvm/Support/raw_ostream.h"27#include <map>28#include <string>29#include <system_error>30
31using namespace llvm;32using namespace llvm::object;33using namespace llvm::support;34
35namespace opts {36cl::OptionCategory CXXDumpCategory("CXX Dump Options");37cl::list<std::string> InputFilenames(cl::Positional,38cl::desc("<input object files>"),39cl::cat(CXXDumpCategory));40} // namespace opts41
42namespace llvm {43
44static void error(std::error_code EC) {45if (!EC)46return;47WithColor::error(outs(), "") << "reading file: " << EC.message() << ".\n";48outs().flush();49exit(1);50}
51
52[[noreturn]] static void error(Error Err) {53logAllUnhandledErrors(std::move(Err), WithColor::error(outs()),54"reading file: ");55outs().flush();56exit(1);57}
58
59template <typename T>60T unwrapOrError(Expected<T> EO) {61if (!EO)62error(EO.takeError());63return std::move(*EO);64}
65
66} // namespace llvm67
68static void reportError(StringRef Input, StringRef Message) {69if (Input == "-")70Input = "<stdin>";71WithColor::error(errs(), Input) << Message << "\n";72errs().flush();73exit(1);74}
75
76static void reportError(StringRef Input, std::error_code EC) {77reportError(Input, EC.message());78}
79
80static std::map<SectionRef, SmallVector<SectionRef, 1>> SectionRelocMap;81
82static void collectRelocatedSymbols(const ObjectFile *Obj,83const SectionRef &Sec, uint64_t SecAddress,84uint64_t SymAddress, uint64_t SymSize,85StringRef *I, StringRef *E) {86uint64_t SymOffset = SymAddress - SecAddress;87uint64_t SymEnd = SymOffset + SymSize;88for (const SectionRef &SR : SectionRelocMap[Sec]) {89for (const object::RelocationRef &Reloc : SR.relocations()) {90if (I == E)91break;92const object::symbol_iterator RelocSymI = Reloc.getSymbol();93if (RelocSymI == Obj->symbol_end())94continue;95Expected<StringRef> RelocSymName = RelocSymI->getName();96error(errorToErrorCode(RelocSymName.takeError()));97uint64_t Offset = Reloc.getOffset();98if (Offset >= SymOffset && Offset < SymEnd) {99*I = *RelocSymName;100++I;101}102}103}104}
105
106static void collectRelocationOffsets(107const ObjectFile *Obj, const SectionRef &Sec, uint64_t SecAddress,108uint64_t SymAddress, uint64_t SymSize, StringRef SymName,109std::map<std::pair<StringRef, uint64_t>, StringRef> &Collection) {110uint64_t SymOffset = SymAddress - SecAddress;111uint64_t SymEnd = SymOffset + SymSize;112for (const SectionRef &SR : SectionRelocMap[Sec]) {113for (const object::RelocationRef &Reloc : SR.relocations()) {114const object::symbol_iterator RelocSymI = Reloc.getSymbol();115if (RelocSymI == Obj->symbol_end())116continue;117Expected<StringRef> RelocSymName = RelocSymI->getName();118error(errorToErrorCode(RelocSymName.takeError()));119uint64_t Offset = Reloc.getOffset();120if (Offset >= SymOffset && Offset < SymEnd)121Collection[std::make_pair(SymName, Offset - SymOffset)] = *RelocSymName;122}123}124}
125
126static void dumpCXXData(const ObjectFile *Obj) {127struct CompleteObjectLocator {128StringRef Symbols[2];129ArrayRef<little32_t> Data;130};131struct ClassHierarchyDescriptor {132StringRef Symbols[1];133ArrayRef<little32_t> Data;134};135struct BaseClassDescriptor {136StringRef Symbols[2];137ArrayRef<little32_t> Data;138};139struct TypeDescriptor {140StringRef Symbols[1];141uint64_t AlwaysZero;142StringRef MangledName;143};144struct ThrowInfo {145uint32_t Flags;146};147struct CatchableTypeArray {148uint32_t NumEntries;149};150struct CatchableType {151uint32_t Flags;152uint32_t NonVirtualBaseAdjustmentOffset;153int32_t VirtualBasePointerOffset;154uint32_t VirtualBaseAdjustmentOffset;155uint32_t Size;156StringRef Symbols[2];157};158std::map<std::pair<StringRef, uint64_t>, StringRef> VFTableEntries;159std::map<std::pair<StringRef, uint64_t>, StringRef> TIEntries;160std::map<std::pair<StringRef, uint64_t>, StringRef> CTAEntries;161std::map<StringRef, ArrayRef<little32_t>> VBTables;162std::map<StringRef, CompleteObjectLocator> COLs;163std::map<StringRef, ClassHierarchyDescriptor> CHDs;164std::map<std::pair<StringRef, uint64_t>, StringRef> BCAEntries;165std::map<StringRef, BaseClassDescriptor> BCDs;166std::map<StringRef, TypeDescriptor> TDs;167std::map<StringRef, ThrowInfo> TIs;168std::map<StringRef, CatchableTypeArray> CTAs;169std::map<StringRef, CatchableType> CTs;170
171std::map<std::pair<StringRef, uint64_t>, StringRef> VTableSymEntries;172std::map<std::pair<StringRef, uint64_t>, int64_t> VTableDataEntries;173std::map<std::pair<StringRef, uint64_t>, StringRef> VTTEntries;174std::map<StringRef, StringRef> TINames;175
176SectionRelocMap.clear();177for (const SectionRef &Section : Obj->sections()) {178Expected<section_iterator> ErrOrSec = Section.getRelocatedSection();179if (!ErrOrSec)180error(ErrOrSec.takeError());181
182section_iterator Sec2 = *ErrOrSec;183if (Sec2 != Obj->section_end())184SectionRelocMap[*Sec2].push_back(Section);185}186
187uint8_t BytesInAddress = Obj->getBytesInAddress();188
189std::vector<std::pair<SymbolRef, uint64_t>> SymAddr =190object::computeSymbolSizes(*Obj);191
192for (auto &P : SymAddr) {193object::SymbolRef Sym = P.first;194uint64_t SymSize = P.second;195Expected<StringRef> SymNameOrErr = Sym.getName();196error(errorToErrorCode(SymNameOrErr.takeError()));197StringRef SymName = *SymNameOrErr;198Expected<object::section_iterator> SecIOrErr = Sym.getSection();199error(errorToErrorCode(SecIOrErr.takeError()));200object::section_iterator SecI = *SecIOrErr;201// Skip external symbols.202if (SecI == Obj->section_end())203continue;204const SectionRef &Sec = *SecI;205// Skip virtual or BSS sections.206if (Sec.isBSS() || Sec.isVirtual())207continue;208StringRef SecContents = unwrapOrError(Sec.getContents());209Expected<uint64_t> SymAddressOrErr = Sym.getAddress();210error(errorToErrorCode(SymAddressOrErr.takeError()));211uint64_t SymAddress = *SymAddressOrErr;212uint64_t SecAddress = Sec.getAddress();213uint64_t SecSize = Sec.getSize();214uint64_t SymOffset = SymAddress - SecAddress;215StringRef SymContents = SecContents.substr(SymOffset, SymSize);216
217// VFTables in the MS-ABI start with '??_7' and are contained within their218// own COMDAT section. We then determine the contents of the VFTable by219// looking at each relocation in the section.220if (SymName.starts_with("??_7")) {221// Each relocation either names a virtual method or a thunk. We note the222// offset into the section and the symbol used for the relocation.223collectRelocationOffsets(Obj, Sec, SecAddress, SecAddress, SecSize,224SymName, VFTableEntries);225}226// VBTables in the MS-ABI start with '??_8' and are filled with 32-bit227// offsets of virtual bases.228else if (SymName.starts_with("??_8")) {229ArrayRef<little32_t> VBTableData(230reinterpret_cast<const little32_t *>(SymContents.data()),231SymContents.size() / sizeof(little32_t));232VBTables[SymName] = VBTableData;233}234// Complete object locators in the MS-ABI start with '??_R4'235else if (SymName.starts_with("??_R4")) {236CompleteObjectLocator COL;237COL.Data =238ArrayRef(reinterpret_cast<const little32_t *>(SymContents.data()), 3);239StringRef *I = std::begin(COL.Symbols), *E = std::end(COL.Symbols);240collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E);241COLs[SymName] = COL;242}243// Class hierarchy descriptors in the MS-ABI start with '??_R3'244else if (SymName.starts_with("??_R3")) {245ClassHierarchyDescriptor CHD;246CHD.Data =247ArrayRef(reinterpret_cast<const little32_t *>(SymContents.data()), 3);248StringRef *I = std::begin(CHD.Symbols), *E = std::end(CHD.Symbols);249collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E);250CHDs[SymName] = CHD;251}252// Class hierarchy descriptors in the MS-ABI start with '??_R2'253else if (SymName.starts_with("??_R2")) {254// Each relocation names a base class descriptor. We note the offset into255// the section and the symbol used for the relocation.256collectRelocationOffsets(Obj, Sec, SecAddress, SymAddress, SymSize,257SymName, BCAEntries);258}259// Base class descriptors in the MS-ABI start with '??_R1'260else if (SymName.starts_with("??_R1")) {261BaseClassDescriptor BCD;262BCD.Data = ArrayRef(263reinterpret_cast<const little32_t *>(SymContents.data()) + 1, 5);264StringRef *I = std::begin(BCD.Symbols), *E = std::end(BCD.Symbols);265collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E);266BCDs[SymName] = BCD;267}268// Type descriptors in the MS-ABI start with '??_R0'269else if (SymName.starts_with("??_R0")) {270const char *DataPtr = SymContents.drop_front(BytesInAddress).data();271TypeDescriptor TD;272if (BytesInAddress == 8)273TD.AlwaysZero = *reinterpret_cast<const little64_t *>(DataPtr);274else275TD.AlwaysZero = *reinterpret_cast<const little32_t *>(DataPtr);276TD.MangledName = SymContents.drop_front(BytesInAddress * 2);277StringRef *I = std::begin(TD.Symbols), *E = std::end(TD.Symbols);278collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E);279TDs[SymName] = TD;280}281// Throw descriptors in the MS-ABI start with '_TI'282else if (SymName.starts_with("_TI") || SymName.starts_with("__TI")) {283ThrowInfo TI;284TI.Flags = *reinterpret_cast<const little32_t *>(SymContents.data());285collectRelocationOffsets(Obj, Sec, SecAddress, SymAddress, SymSize,286SymName, TIEntries);287TIs[SymName] = TI;288}289// Catchable type arrays in the MS-ABI start with _CTA or __CTA.290else if (SymName.starts_with("_CTA") || SymName.starts_with("__CTA")) {291CatchableTypeArray CTA;292CTA.NumEntries =293*reinterpret_cast<const little32_t *>(SymContents.data());294collectRelocationOffsets(Obj, Sec, SecAddress, SymAddress, SymSize,295SymName, CTAEntries);296CTAs[SymName] = CTA;297}298// Catchable types in the MS-ABI start with _CT or __CT.299else if (SymName.starts_with("_CT") || SymName.starts_with("__CT")) {300const little32_t *DataPtr =301reinterpret_cast<const little32_t *>(SymContents.data());302CatchableType CT;303CT.Flags = DataPtr[0];304CT.NonVirtualBaseAdjustmentOffset = DataPtr[2];305CT.VirtualBasePointerOffset = DataPtr[3];306CT.VirtualBaseAdjustmentOffset = DataPtr[4];307CT.Size = DataPtr[5];308StringRef *I = std::begin(CT.Symbols), *E = std::end(CT.Symbols);309collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E);310CTs[SymName] = CT;311}312// Construction vtables in the Itanium ABI start with '_ZTT' or '__ZTT'.313else if (SymName.starts_with("_ZTT") || SymName.starts_with("__ZTT")) {314collectRelocationOffsets(Obj, Sec, SecAddress, SymAddress, SymSize,315SymName, VTTEntries);316}317// Typeinfo names in the Itanium ABI start with '_ZTS' or '__ZTS'.318else if (SymName.starts_with("_ZTS") || SymName.starts_with("__ZTS")) {319TINames[SymName] = SymContents.slice(0, SymContents.find('\0'));320}321// Vtables in the Itanium ABI start with '_ZTV' or '__ZTV'.322else if (SymName.starts_with("_ZTV") || SymName.starts_with("__ZTV")) {323collectRelocationOffsets(Obj, Sec, SecAddress, SymAddress, SymSize,324SymName, VTableSymEntries);325for (uint64_t SymOffI = 0; SymOffI < SymSize; SymOffI += BytesInAddress) {326auto Key = std::make_pair(SymName, SymOffI);327if (VTableSymEntries.count(Key))328continue;329const char *DataPtr =330SymContents.substr(SymOffI, BytesInAddress).data();331int64_t VData;332if (BytesInAddress == 8)333VData = *reinterpret_cast<const little64_t *>(DataPtr);334else335VData = *reinterpret_cast<const little32_t *>(DataPtr);336VTableDataEntries[Key] = VData;337}338}339// Typeinfo structures in the Itanium ABI start with '_ZTI' or '__ZTI'.340else if (SymName.starts_with("_ZTI") || SymName.starts_with("__ZTI")) {341// FIXME: Do something with these!342}343}344for (const auto &VFTableEntry : VFTableEntries) {345StringRef VFTableName = VFTableEntry.first.first;346uint64_t Offset = VFTableEntry.first.second;347StringRef SymName = VFTableEntry.second;348outs() << VFTableName << '[' << Offset << "]: " << SymName << '\n';349}350for (const auto &VBTable : VBTables) {351StringRef VBTableName = VBTable.first;352uint32_t Idx = 0;353for (little32_t Offset : VBTable.second) {354outs() << VBTableName << '[' << Idx << "]: " << Offset << '\n';355Idx += sizeof(Offset);356}357}358for (const auto &COLPair : COLs) {359StringRef COLName = COLPair.first;360const CompleteObjectLocator &COL = COLPair.second;361outs() << COLName << "[IsImageRelative]: " << COL.Data[0] << '\n';362outs() << COLName << "[OffsetToTop]: " << COL.Data[1] << '\n';363outs() << COLName << "[VFPtrOffset]: " << COL.Data[2] << '\n';364outs() << COLName << "[TypeDescriptor]: " << COL.Symbols[0] << '\n';365outs() << COLName << "[ClassHierarchyDescriptor]: " << COL.Symbols[1]366<< '\n';367}368for (const auto &CHDPair : CHDs) {369StringRef CHDName = CHDPair.first;370const ClassHierarchyDescriptor &CHD = CHDPair.second;371outs() << CHDName << "[AlwaysZero]: " << CHD.Data[0] << '\n';372outs() << CHDName << "[Flags]: " << CHD.Data[1] << '\n';373outs() << CHDName << "[NumClasses]: " << CHD.Data[2] << '\n';374outs() << CHDName << "[BaseClassArray]: " << CHD.Symbols[0] << '\n';375}376for (const auto &BCAEntry : BCAEntries) {377StringRef BCAName = BCAEntry.first.first;378uint64_t Offset = BCAEntry.first.second;379StringRef SymName = BCAEntry.second;380outs() << BCAName << '[' << Offset << "]: " << SymName << '\n';381}382for (const auto &BCDPair : BCDs) {383StringRef BCDName = BCDPair.first;384const BaseClassDescriptor &BCD = BCDPair.second;385outs() << BCDName << "[TypeDescriptor]: " << BCD.Symbols[0] << '\n';386outs() << BCDName << "[NumBases]: " << BCD.Data[0] << '\n';387outs() << BCDName << "[OffsetInVBase]: " << BCD.Data[1] << '\n';388outs() << BCDName << "[VBPtrOffset]: " << BCD.Data[2] << '\n';389outs() << BCDName << "[OffsetInVBTable]: " << BCD.Data[3] << '\n';390outs() << BCDName << "[Flags]: " << BCD.Data[4] << '\n';391outs() << BCDName << "[ClassHierarchyDescriptor]: " << BCD.Symbols[1]392<< '\n';393}394for (const auto &TDPair : TDs) {395StringRef TDName = TDPair.first;396const TypeDescriptor &TD = TDPair.second;397outs() << TDName << "[VFPtr]: " << TD.Symbols[0] << '\n';398outs() << TDName << "[AlwaysZero]: " << TD.AlwaysZero << '\n';399outs() << TDName << "[MangledName]: ";400outs().write_escaped(TD.MangledName.rtrim(StringRef("\0", 1)),401/*UseHexEscapes=*/true)402<< '\n';403}404for (const auto &TIPair : TIs) {405StringRef TIName = TIPair.first;406const ThrowInfo &TI = TIPair.second;407auto dumpThrowInfoFlag = [&](const char *Name, uint32_t Flag) {408outs() << TIName << "[Flags." << Name409<< "]: " << (TI.Flags & Flag ? "true" : "false") << '\n';410};411auto dumpThrowInfoSymbol = [&](const char *Name, int Offset) {412outs() << TIName << '[' << Name << "]: ";413auto Entry = TIEntries.find(std::make_pair(TIName, Offset));414outs() << (Entry == TIEntries.end() ? "null" : Entry->second) << '\n';415};416outs() << TIName << "[Flags]: " << TI.Flags << '\n';417dumpThrowInfoFlag("Const", 1);418dumpThrowInfoFlag("Volatile", 2);419dumpThrowInfoSymbol("CleanupFn", 4);420dumpThrowInfoSymbol("ForwardCompat", 8);421dumpThrowInfoSymbol("CatchableTypeArray", 12);422}423for (const auto &CTAPair : CTAs) {424StringRef CTAName = CTAPair.first;425const CatchableTypeArray &CTA = CTAPair.second;426
427outs() << CTAName << "[NumEntries]: " << CTA.NumEntries << '\n';428
429unsigned Idx = 0;430for (auto I = CTAEntries.lower_bound(std::make_pair(CTAName, 0)),431E = CTAEntries.upper_bound(std::make_pair(CTAName, UINT64_MAX));432I != E; ++I)433outs() << CTAName << '[' << Idx++ << "]: " << I->second << '\n';434}435for (const auto &CTPair : CTs) {436StringRef CTName = CTPair.first;437const CatchableType &CT = CTPair.second;438auto dumpCatchableTypeFlag = [&](const char *Name, uint32_t Flag) {439outs() << CTName << "[Flags." << Name440<< "]: " << (CT.Flags & Flag ? "true" : "false") << '\n';441};442outs() << CTName << "[Flags]: " << CT.Flags << '\n';443dumpCatchableTypeFlag("ScalarType", 1);444dumpCatchableTypeFlag("VirtualInheritance", 4);445outs() << CTName << "[TypeDescriptor]: " << CT.Symbols[0] << '\n';446outs() << CTName << "[NonVirtualBaseAdjustmentOffset]: "447<< CT.NonVirtualBaseAdjustmentOffset << '\n';448outs() << CTName449<< "[VirtualBasePointerOffset]: " << CT.VirtualBasePointerOffset450<< '\n';451outs() << CTName << "[VirtualBaseAdjustmentOffset]: "452<< CT.VirtualBaseAdjustmentOffset << '\n';453outs() << CTName << "[Size]: " << CT.Size << '\n';454outs() << CTName455<< "[CopyCtor]: " << (CT.Symbols[1].empty() ? "null" : CT.Symbols[1])456<< '\n';457}458for (const auto &VTTPair : VTTEntries) {459StringRef VTTName = VTTPair.first.first;460uint64_t VTTOffset = VTTPair.first.second;461StringRef VTTEntry = VTTPair.second;462outs() << VTTName << '[' << VTTOffset << "]: " << VTTEntry << '\n';463}464for (const auto &TIPair : TINames) {465StringRef TIName = TIPair.first;466outs() << TIName << ": " << TIPair.second << '\n';467}468auto VTableSymI = VTableSymEntries.begin();469auto VTableSymE = VTableSymEntries.end();470auto VTableDataI = VTableDataEntries.begin();471auto VTableDataE = VTableDataEntries.end();472for (;;) {473bool SymDone = VTableSymI == VTableSymE;474bool DataDone = VTableDataI == VTableDataE;475if (SymDone && DataDone)476break;477if (!SymDone && (DataDone || VTableSymI->first < VTableDataI->first)) {478StringRef VTableName = VTableSymI->first.first;479uint64_t Offset = VTableSymI->first.second;480StringRef VTableEntry = VTableSymI->second;481outs() << VTableName << '[' << Offset << "]: ";482outs() << VTableEntry;483outs() << '\n';484++VTableSymI;485continue;486}487if (!DataDone && (SymDone || VTableDataI->first < VTableSymI->first)) {488StringRef VTableName = VTableDataI->first.first;489uint64_t Offset = VTableDataI->first.second;490int64_t VTableEntry = VTableDataI->second;491outs() << VTableName << '[' << Offset << "]: ";492outs() << VTableEntry;493outs() << '\n';494++VTableDataI;495continue;496}497}498}
499
500static void dumpArchive(const Archive *Arc) {501Error Err = Error::success();502for (const auto &ArcC : Arc->children(Err)) {503Expected<std::unique_ptr<Binary>> ChildOrErr = ArcC.getAsBinary();504if (!ChildOrErr) {505// Ignore non-object files.506if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) {507std::string Buf;508raw_string_ostream OS(Buf);509logAllUnhandledErrors(std::move(E), OS);510OS.flush();511reportError(Arc->getFileName(), Buf);512}513consumeError(ChildOrErr.takeError());514continue;515}516
517if (ObjectFile *Obj = dyn_cast<ObjectFile>(&*ChildOrErr.get()))518dumpCXXData(Obj);519else520reportError(Arc->getFileName(), cxxdump_error::unrecognized_file_format);521}522if (Err)523error(std::move(Err));524}
525
526static void dumpInput(StringRef File) {527// Attempt to open the binary.528Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(File);529if (!BinaryOrErr) {530auto EC = errorToErrorCode(BinaryOrErr.takeError());531reportError(File, EC);532return;533}534Binary &Binary = *BinaryOrErr.get().getBinary();535
536if (Archive *Arc = dyn_cast<Archive>(&Binary))537dumpArchive(Arc);538else if (ObjectFile *Obj = dyn_cast<ObjectFile>(&Binary))539dumpCXXData(Obj);540else541reportError(File, cxxdump_error::unrecognized_file_format);542}
543
544int main(int argc, const char *argv[]) {545InitLLVM X(argc, argv);546
547// Initialize targets.548llvm::InitializeAllTargetInfos();549
550// Register the target printer for --version.551cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);552
553cl::HideUnrelatedOptions({&opts::CXXDumpCategory, &getColorCategory()});554cl::ParseCommandLineOptions(argc, argv, "LLVM C++ ABI Data Dumper\n");555
556// Default to stdin if no filename is specified.557if (opts::InputFilenames.size() == 0)558opts::InputFilenames.push_back("-");559
560llvm::for_each(opts::InputFilenames, dumpInput);561
562return EXIT_SUCCESS;563}
564