xref: /freebsd/contrib/llvm-project/llvm/lib/DebugInfo/GSYM/ObjectFileTransformer.cpp (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
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