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
21using namespace llvm;
22using namespace gsym;
23
24constexpr uint32_t NT_GNU_BUILD_ID_TAG = 0x03;
25
26static 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>(Val: &Obj)) {
30 const ArrayRef<uint8_t> MachUUID = MachO->getUuid();
31 if (!MachUUID.empty())
32 UUID.assign(first: MachUUID.data(), last: MachUUID.data() + MachUUID.size());
33 } else if (isa<object::ELFObjectFileBase>(Val: &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(Err: 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(Err: 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_ptr: &Offset);
55 const uint32_t PayloadSize = Decoder.getU32(offset_ptr: &Offset);
56 const uint32_t PayloadType = Decoder.getU32(offset_ptr: &Offset);
57 StringRef Name(Decoder.getFixedLengthString(OffsetPtr: &Offset, Length: NameSize));
58 if (Name == "GNU" && PayloadType == NT_GNU_BUILD_ID_TAG) {
59 Offset = alignTo(Value: Offset, Align: 4);
60 StringRef UUIDBytes(Decoder.getBytes(OffsetPtr: &Offset, Length: PayloadSize));
61 if (!UUIDBytes.empty()) {
62 auto Ptr = reinterpret_cast<const uint8_t *>(UUIDBytes.data());
63 UUID.assign(first: Ptr, last: Ptr + UUIDBytes.size());
64 }
65 }
66 }
67 }
68 return UUID;
69}
70
71llvm::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>(Val: &Obj);
77 const bool IsELF = isa<ELFObjectFileBase>(Val: &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(Err: 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(Addr: *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(E: Name.takeError(), OS&: *Out.GetOS(),
105 ErrorBanner: "ObjectFileTransformer: ");
106 else
107 consumeError(Err: 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(Prefix: "_");
114 Gsym.addFunctionInfo(
115 FI: FunctionInfo(*AddrOrErr, size, Gsym.insertString(S: *Name, Copy: 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