| //===- ObjectFileTransformer.cpp --------------------------------*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include <unordered_set> | |
| #include "llvm/Object/ELFObjectFile.h" | |
| #include "llvm/Object/MachOUniversal.h" | |
| #include "llvm/Object/ObjectFile.h" | |
| #include "llvm/Support/DataExtractor.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/DebugInfo/GSYM/ObjectFileTransformer.h" | |
| #include "llvm/DebugInfo/GSYM/GsymCreator.h" | |
| using namespace llvm; | |
| using namespace gsym; | |
| constexpr uint32_t NT_GNU_BUILD_ID_TAG = 0x03; | |
| static std::vector<uint8_t> getUUID(const object::ObjectFile &Obj) { | |
| // Extract the UUID from the object file | |
| std::vector<uint8_t> UUID; | |
| if (auto *MachO = dyn_cast<object::MachOObjectFile>(&Obj)) { | |
| const ArrayRef<uint8_t> MachUUID = MachO->getUuid(); | |
| if (!MachUUID.empty()) | |
| UUID.assign(MachUUID.data(), MachUUID.data() + MachUUID.size()); | |
| } else if (isa<object::ELFObjectFileBase>(&Obj)) { | |
| const StringRef GNUBuildID(".note.gnu.build-id"); | |
| for (const object::SectionRef &Sect : Obj.sections()) { | |
| Expected<StringRef> SectNameOrErr = Sect.getName(); | |
| if (!SectNameOrErr) { | |
| consumeError(SectNameOrErr.takeError()); | |
| continue; | |
| } | |
| StringRef SectName(*SectNameOrErr); | |
| if (SectName != GNUBuildID) | |
| continue; | |
| StringRef BuildIDData; | |
| Expected<StringRef> E = Sect.getContents(); | |
| if (E) | |
| BuildIDData = *E; | |
| else { | |
| consumeError(E.takeError()); | |
| continue; | |
| } | |
| DataExtractor Decoder(BuildIDData, Obj.makeTriple().isLittleEndian(), 8); | |
| uint64_t Offset = 0; | |
| const uint32_t NameSize = Decoder.getU32(&Offset); | |
| const uint32_t PayloadSize = Decoder.getU32(&Offset); | |
| const uint32_t PayloadType = Decoder.getU32(&Offset); | |
| StringRef Name(Decoder.getFixedLengthString(&Offset, NameSize)); | |
| if (Name == "GNU" && PayloadType == NT_GNU_BUILD_ID_TAG) { | |
| Offset = alignTo(Offset, 4); | |
| StringRef UUIDBytes(Decoder.getBytes(&Offset, PayloadSize)); | |
| if (!UUIDBytes.empty()) { | |
| auto Ptr = reinterpret_cast<const uint8_t *>(UUIDBytes.data()); | |
| UUID.assign(Ptr, Ptr + UUIDBytes.size()); | |
| } | |
| } | |
| } | |
| } | |
| return UUID; | |
| } | |
| llvm::Error ObjectFileTransformer::convert(const object::ObjectFile &Obj, | |
| raw_ostream &Log, | |
| GsymCreator &Gsym) { | |
| using namespace llvm::object; | |
| const bool IsMachO = isa<MachOObjectFile>(&Obj); | |
| const bool IsELF = isa<ELFObjectFileBase>(&Obj); | |
| // Read build ID. | |
| Gsym.setUUID(getUUID(Obj)); | |
| // Parse the symbol table. | |
| size_t NumBefore = Gsym.getNumFunctionInfos(); | |
| for (const object::SymbolRef &Sym : Obj.symbols()) { | |
| Expected<SymbolRef::Type> SymType = Sym.getType(); | |
| if (!SymType) { | |
| consumeError(SymType.takeError()); | |
| continue; | |
| } | |
| Expected<uint64_t> AddrOrErr = Sym.getValue(); | |
| if (!AddrOrErr) | |
| // TODO: Test this error. | |
| return AddrOrErr.takeError(); | |
| if (SymType.get() != SymbolRef::Type::ST_Function || | |
| !Gsym.IsValidTextAddress(*AddrOrErr) || | |
| Gsym.hasFunctionInfoForAddress(*AddrOrErr)) | |
| continue; | |
| // Function size for MachO files will be 0 | |
| constexpr bool NoCopy = false; | |
| const uint64_t size = IsELF ? ELFSymbolRef(Sym).getSize() : 0; | |
| Expected<StringRef> Name = Sym.getName(); | |
| if (!Name) { | |
| logAllUnhandledErrors(Name.takeError(), Log, "ObjectFileTransformer: "); | |
| continue; | |
| } | |
| // Remove the leading '_' character in any symbol names if there is one | |
| // for mach-o files. | |
| if (IsMachO) | |
| Name->consume_front("_"); | |
| Gsym.addFunctionInfo( | |
| FunctionInfo(*AddrOrErr, size, Gsym.insertString(*Name, NoCopy))); | |
| } | |
| size_t FunctionsAddedCount = Gsym.getNumFunctionInfos() - NumBefore; | |
| Log << "Loaded " << FunctionsAddedCount << " functions from symbol table.\n"; | |
| return Error::success(); | |
| } |