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