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