1 //===- ObjectFileTransformer.cpp --------------------------------*- 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 #include <unordered_set> 10 11 #include "llvm/Object/ELFObjectFile.h" 12 #include "llvm/Object/MachOUniversal.h" 13 #include "llvm/Object/ObjectFile.h" 14 #include "llvm/Support/DataExtractor.h" 15 #include "llvm/Support/raw_ostream.h" 16 17 #include "llvm/DebugInfo/GSYM/GsymCreator.h" 18 #include "llvm/DebugInfo/GSYM/ObjectFileTransformer.h" 19 #include "llvm/DebugInfo/GSYM/OutputAggregator.h" 20 21 using namespace llvm; 22 using namespace gsym; 23 24 constexpr uint32_t NT_GNU_BUILD_ID_TAG = 0x03; 25 26 static std::vector<uint8_t> getUUID(const object::ObjectFile &Obj) { 27 // Extract the UUID from the object file 28 std::vector<uint8_t> UUID; 29 if (auto *MachO = dyn_cast<object::MachOObjectFile>(&Obj)) { 30 const ArrayRef<uint8_t> MachUUID = MachO->getUuid(); 31 if (!MachUUID.empty()) 32 UUID.assign(MachUUID.data(), MachUUID.data() + MachUUID.size()); 33 } else if (isa<object::ELFObjectFileBase>(&Obj)) { 34 const StringRef GNUBuildID(".note.gnu.build-id"); 35 for (const object::SectionRef &Sect : Obj.sections()) { 36 Expected<StringRef> SectNameOrErr = Sect.getName(); 37 if (!SectNameOrErr) { 38 consumeError(SectNameOrErr.takeError()); 39 continue; 40 } 41 StringRef SectName(*SectNameOrErr); 42 if (SectName != GNUBuildID) 43 continue; 44 StringRef BuildIDData; 45 Expected<StringRef> E = Sect.getContents(); 46 if (E) 47 BuildIDData = *E; 48 else { 49 consumeError(E.takeError()); 50 continue; 51 } 52 DataExtractor Decoder(BuildIDData, Obj.makeTriple().isLittleEndian(), 8); 53 uint64_t Offset = 0; 54 const uint32_t NameSize = Decoder.getU32(&Offset); 55 const uint32_t PayloadSize = Decoder.getU32(&Offset); 56 const uint32_t PayloadType = Decoder.getU32(&Offset); 57 StringRef Name(Decoder.getFixedLengthString(&Offset, NameSize)); 58 if (Name == "GNU" && PayloadType == NT_GNU_BUILD_ID_TAG) { 59 Offset = alignTo(Offset, 4); 60 StringRef UUIDBytes(Decoder.getBytes(&Offset, PayloadSize)); 61 if (!UUIDBytes.empty()) { 62 auto Ptr = reinterpret_cast<const uint8_t *>(UUIDBytes.data()); 63 UUID.assign(Ptr, Ptr + UUIDBytes.size()); 64 } 65 } 66 } 67 } 68 return UUID; 69 } 70 71 llvm::Error ObjectFileTransformer::convert(const object::ObjectFile &Obj, 72 OutputAggregator &Out, 73 GsymCreator &Gsym) { 74 using namespace llvm::object; 75 76 const bool IsMachO = isa<MachOObjectFile>(&Obj); 77 const bool IsELF = isa<ELFObjectFileBase>(&Obj); 78 79 // Read build ID. 80 Gsym.setUUID(getUUID(Obj)); 81 82 // Parse the symbol table. 83 size_t NumBefore = Gsym.getNumFunctionInfos(); 84 for (const object::SymbolRef &Sym : Obj.symbols()) { 85 Expected<SymbolRef::Type> SymType = Sym.getType(); 86 if (!SymType) { 87 consumeError(SymType.takeError()); 88 continue; 89 } 90 Expected<uint64_t> AddrOrErr = Sym.getValue(); 91 if (!AddrOrErr) 92 // TODO: Test this error. 93 return AddrOrErr.takeError(); 94 95 if (SymType.get() != SymbolRef::Type::ST_Function || 96 !Gsym.IsValidTextAddress(*AddrOrErr)) 97 continue; 98 // Function size for MachO files will be 0 99 constexpr bool NoCopy = false; 100 const uint64_t size = IsELF ? ELFSymbolRef(Sym).getSize() : 0; 101 Expected<StringRef> Name = Sym.getName(); 102 if (!Name) { 103 if (Out.GetOS()) 104 logAllUnhandledErrors(Name.takeError(), *Out.GetOS(), 105 "ObjectFileTransformer: "); 106 else 107 consumeError(Name.takeError()); 108 continue; 109 } 110 // Remove the leading '_' character in any symbol names if there is one 111 // for mach-o files. 112 if (IsMachO) 113 Name->consume_front("_"); 114 Gsym.addFunctionInfo( 115 FunctionInfo(*AddrOrErr, size, Gsym.insertString(*Name, NoCopy))); 116 } 117 size_t FunctionsAddedCount = Gsym.getNumFunctionInfos() - NumBefore; 118 if (Out.GetOS()) 119 *Out.GetOS() << "Loaded " << FunctionsAddedCount 120 << " functions from symbol table.\n"; 121 return Error::success(); 122 } 123