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
20using namespace llvm;
21using namespace gsym;
22
23constexpr uint32_t NT_GNU_BUILD_ID_TAG = 0x03;
24
25static 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
70llvm::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      continue;
97    // Function size for MachO files will be 0
98    constexpr bool NoCopy = false;
99    const uint64_t size = IsELF ? ELFSymbolRef(Sym).getSize() : 0;
100    Expected<StringRef> Name = Sym.getName();
101    if (!Name) {
102      if (Log)
103        logAllUnhandledErrors(Name.takeError(), *Log,
104                              "ObjectFileTransformer: ");
105      else
106        consumeError(Name.takeError());
107      continue;
108    }
109    // Remove the leading '_' character in any symbol names if there is one
110    // for mach-o files.
111    if (IsMachO)
112      Name->consume_front("_");
113    Gsym.addFunctionInfo(
114        FunctionInfo(*AddrOrErr, size, Gsym.insertString(*Name, NoCopy)));
115  }
116  size_t FunctionsAddedCount = Gsym.getNumFunctionInfos() - NumBefore;
117  if (Log)
118    *Log << "Loaded " << FunctionsAddedCount
119         << " functions from symbol table.\n";
120  return Error::success();
121}
122