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