1 //===- ObjectFileTransformer.cpp --------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include <unordered_set>
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"
17 #include "llvm/DebugInfo/GSYM/ObjectFileTransformer.h"
18 #include "llvm/DebugInfo/GSYM/GsymCreator.h"
23 constexpr uint32_t NT_GNU_BUILD_ID_TAG
= 0x03;
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();
37 consumeError(SectNameOrErr
.takeError());
40 StringRef
SectName(*SectNameOrErr
);
41 if (SectName
!= GNUBuildID
)
43 StringRef BuildIDData
;
44 Expected
<StringRef
> E
= Sect
.getContents();
48 consumeError(E
.takeError());
51 DataExtractor
Decoder(BuildIDData
, Obj
.makeTriple().isLittleEndian(), 8);
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());
70 llvm::Error
ObjectFileTransformer::convert(const object::ObjectFile
&Obj
,
73 using namespace llvm::object
;
75 const bool IsMachO
= isa
<MachOObjectFile
>(&Obj
);
76 const bool IsELF
= isa
<ELFObjectFileBase
>(&Obj
);
79 Gsym
.setUUID(getUUID(Obj
));
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();
86 consumeError(SymType
.takeError());
89 Expected
<uint64_t> AddrOrErr
= Sym
.getValue();
91 // TODO: Test this error.
92 return AddrOrErr
.takeError();
94 if (SymType
.get() != SymbolRef::Type::ST_Function
||
95 !Gsym
.IsValidTextAddress(*AddrOrErr
) ||
96 Gsym
.hasFunctionInfoForAddress(*AddrOrErr
))
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();
103 logAllUnhandledErrors(Name
.takeError(), Log
, "ObjectFileTransformer: ");
106 // Remove the leading '_' character in any symbol names if there is one
109 Name
->consume_front("_");
110 Gsym
.addFunctionInfo(
111 FunctionInfo(*AddrOrErr
, size
, Gsym
.insertString(*Name
, NoCopy
)));
113 size_t FunctionsAddedCount
= Gsym
.getNumFunctionInfos() - NumBefore
;
114 Log
<< "Loaded " << FunctionsAddedCount
<< " functions from symbol table.\n";
115 return Error::success();