LLVM 24.0.0git
ObjectFileTransformer.cpp
Go to the documentation of this file.
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
12#include "llvm/Object/MachO.h"
18
22
23using namespace llvm;
24using namespace gsym;
25
27
28static std::vector<uint8_t> getUUID(const object::ObjectFile &Obj) {
29 // Extract the UUID from the object file
30 std::vector<uint8_t> UUID;
31 if (auto *MachO = dyn_cast<object::MachOObjectFile>(&Obj)) {
32 const ArrayRef<uint8_t> MachUUID = MachO->getUuid();
33 if (!MachUUID.empty())
34 UUID.assign(MachUUID.data(), MachUUID.data() + MachUUID.size());
35 } else if (isa<object::ELFObjectFileBase>(&Obj)) {
36 const StringRef GNUBuildID(".note.gnu.build-id");
37 for (const object::SectionRef &Sect : Obj.sections()) {
38 Expected<StringRef> SectNameOrErr = Sect.getName();
39 if (!SectNameOrErr) {
40 consumeError(SectNameOrErr.takeError());
41 continue;
42 }
43 StringRef SectName(*SectNameOrErr);
44 if (SectName != GNUBuildID)
45 continue;
46 StringRef BuildIDData;
47 Expected<StringRef> E = Sect.getContents();
48 if (E)
49 BuildIDData = *E;
50 else {
51 consumeError(E.takeError());
52 continue;
53 }
54 DataExtractor Decoder(BuildIDData, Obj.makeTriple().isLittleEndian());
55 uint64_t Offset = 0;
56 const uint32_t NameSize = Decoder.getU32(&Offset);
57 const uint32_t PayloadSize = Decoder.getU32(&Offset);
58 const uint32_t PayloadType = Decoder.getU32(&Offset);
59 StringRef Name(Decoder.getFixedLengthString(&Offset, NameSize));
60 if (Name == "GNU" && PayloadType == NT_GNU_BUILD_ID_TAG) {
61 Offset = alignTo(Offset, 4);
62 StringRef UUIDBytes(Decoder.getBytes(&Offset, PayloadSize));
63 if (!UUIDBytes.empty()) {
64 auto Ptr = reinterpret_cast<const uint8_t *>(UUIDBytes.data());
65 UUID.assign(Ptr, Ptr + UUIDBytes.size());
66 }
67 }
68 }
69 }
70 return UUID;
71}
72
73/// Create function information entries for Mach-O symbol stubs.
74///
75/// Symbol stubs are small chunks of code, all with the same fixed size, that
76/// resolves a function pointer on first call and then jumps to the resolved
77/// function on subsequent calls for functions that live in another shared
78/// library. The stubs are stored in sections whose type is S_SYMBOL_STUBS and
79/// the size of a single stub is stored in the "reserved2" field of the section
80/// header. These stubs have no entries in the symbol table of their own, so
81/// they end up being attributed to whatever function precedes them unless they
82/// are synthesized here.
83///
84/// The name of the function a stub jumps to is found using the indirect symbol
85/// table from the LC_DYSYMTAB load command. The "reserved1" field of the
86/// section header contains the index of the indirect symbol table entry that
87/// describes the first stub in the section, and each subsequent stub is
88/// described by the entry that follows. Each indirect symbol table entry is an
89/// index into the symbol table where the matching undefined (N_UNDF) symbol
90/// supplies the name to use for the stub. Each name gets a "symbol stub for: "
91/// prefix prepended to it so that symbolication makes it clear that the address
92/// is the stub for a function and not the function itself.
93///
94/// \returns The number of function infos that were added to \a Gsym.
96 OutputAggregator &Out, GsymCreator &Gsym) {
97 const MachO::dysymtab_command Dysymtab = MachO.getDysymtabLoadCommand();
98 if (Dysymtab.nindirectsyms == 0)
99 return 0;
100 const uint32_t NumSyms = MachO.getSymtabLoadCommand().nsyms;
101 const bool Is64Bit = MachO.is64Bit();
102
103 size_t NumBefore = Gsym.getNumFunctionInfos();
104 for (const object::SectionRef &Sect : MachO.sections()) {
105 const object::DataRefImpl SectDRI = Sect.getRawDataRefImpl();
106 uint32_t SectFlags, IndirectSymIdxStart, StubByteSize;
107 if (Is64Bit) {
108 const MachO::section_64 S = MachO.getSection64(SectDRI);
109 SectFlags = S.flags;
110 IndirectSymIdxStart = S.reserved1;
111 StubByteSize = S.reserved2;
112 } else {
113 const MachO::section S = MachO.getSection(SectDRI);
114 SectFlags = S.flags;
115 IndirectSymIdxStart = S.reserved1;
116 StubByteSize = S.reserved2;
117 }
118 if ((SectFlags & MachO::SECTION_TYPE) != MachO::S_SYMBOL_STUBS)
119 continue;
120 if (StubByteSize == 0)
121 continue;
122
123 const uint64_t SectAddr = Sect.getAddress();
124 const uint64_t NumStubs = Sect.getSize() / StubByteSize;
125 for (uint64_t StubIdx = 0; StubIdx < NumStubs; ++StubIdx) {
126 const uint64_t StubAddr = SectAddr + StubIdx * StubByteSize;
127 if (!Gsym.IsValidTextAddress(StubAddr))
128 continue;
129 const uint64_t IndirectSymIdx = IndirectSymIdxStart + StubIdx;
130 if (IndirectSymIdx >= Dysymtab.nindirectsyms)
131 continue;
132 const uint32_t SymIdx =
133 MachO.getIndirectSymbolTableEntry(Dysymtab, IndirectSymIdx);
134 // Entries that are absolute or local don't refer to a symbol table entry.
136 continue;
137 if (SymIdx >= NumSyms)
138 continue;
139 const object::symbol_iterator SymIt = MachO.getSymbolByIndex(SymIdx);
140 const object::DataRefImpl SymDRI = SymIt->getRawDataRefImpl();
141 const uint8_t NType = Is64Bit ? MachO.getSymbol64TableEntry(SymDRI).n_type
142 : MachO.getSymbolTableEntry(SymDRI).n_type;
143 // Only undefined symbols name a stub, any other symbol type means the
144 // function itself is in this file and already has a symbol table entry.
145 if ((NType & MachO::N_TYPE) != MachO::N_UNDF)
146 continue;
147 Expected<StringRef> Name = SymIt->getName();
148 if (!Name) {
149 if (Out.GetOS())
150 logAllUnhandledErrors(Name.takeError(), *Out.GetOS(),
151 "ObjectFileTransformer: ");
152 else
153 consumeError(Name.takeError());
154 continue;
155 }
156 // Remove the leading '_' character in any symbol names if there is one
157 // for mach-o files.
158 Name->consume_front("_");
159 if (Name->empty())
160 continue;
161 // Append a "symbol stub for: " prefix so it is clear when symbolicating
162 // that the address is the stub for the function and not the function
163 // itself. The string must be copied into the string table since it is
164 // created here and, unlike the symbol names, has no backing storage in
165 // the object file.
166 constexpr bool Copy = true;
167 const std::string StubName = "symbol stub for: " + Name->str();
168 Gsym.addFunctionInfo(FunctionInfo(StubAddr, StubByteSize,
169 Gsym.insertString(StubName, Copy)));
170 }
171 }
172 return Gsym.getNumFunctionInfos() - NumBefore;
173}
174
175static Expected<size_t>
178 OutputAggregator &Out, GsymCreator &Gsym, bool CopyStrings) {
179 using namespace llvm::object;
180
181 const bool IsMachO = isa<MachOObjectFile>(&Obj);
182 const bool IsELF = isa<ELFObjectFileBase>(&Obj);
183 const size_t NumBefore = Gsym.getNumFunctionInfos();
184
185 for (const object::SymbolRef &Sym : Symbols) {
186 Expected<SymbolRef::Type> SymType = Sym.getType();
187 if (!SymType) {
188 consumeError(SymType.takeError());
189 continue;
190 }
191 Expected<uint64_t> AddrOrErr = Sym.getValue();
192 if (!AddrOrErr)
193 // TODO: Test this error.
194 return AddrOrErr.takeError();
195
196 if (SymType.get() != SymbolRef::Type::ST_Function ||
197 !Gsym.IsValidTextAddress(*AddrOrErr))
198 continue;
199 // Function size for MachO files will be 0
200 const uint64_t size = IsELF ? ELFSymbolRef(Sym).getSize() : 0;
201 Expected<StringRef> Name = Sym.getName();
202 if (!Name) {
203 if (Out.GetOS())
204 logAllUnhandledErrors(Name.takeError(), *Out.GetOS(),
205 "ObjectFileTransformer: ");
206 else
207 consumeError(Name.takeError());
208 continue;
209 }
210 // Remove the leading '_' character in any symbol names if there is one
211 // for mach-o files.
212 if (IsMachO)
213 Name->consume_front("_");
214 Gsym.addFunctionInfo(
215 FunctionInfo(*AddrOrErr, size, Gsym.insertString(*Name, CopyStrings)));
216 }
217 return Gsym.getNumFunctionInfos() - NumBefore;
218}
219
220// Add function symbols from the .gnu_debugdata section (MiniDebugInfo), which
221// contains an xz-compressed ELF file with a .symtab. Errors reading the section
222// are reported as warnings.
224 OutputAggregator &Out,
225 GsymCreator &Gsym) {
226 using namespace llvm::object;
227
228 std::optional<SectionRef> DebugDataSect;
229 for (const SectionRef &Sect : Obj.sections()) {
230 Expected<StringRef> SectNameOrErr = Sect.getName();
231 if (!SectNameOrErr) {
232 consumeError(SectNameOrErr.takeError());
233 continue;
234 }
235 if (*SectNameOrErr == ".gnu_debugdata") {
236 DebugDataSect = Sect;
237 break;
238 }
239 }
240 if (!DebugDataSect)
241 return Error::success();
242
243 // MiniDebugInfo leaves out symbols that are in .dynsym, so add those too.
244 Expected<size_t> DynFunctionsAdded = loadSymbols(
245 Obj, Obj.getDynamicSymbolIterators(), Out, Gsym, /*CopyStrings=*/false);
246 if (!DynFunctionsAdded)
247 return DynFunctionsAdded.takeError();
248 if (Out.GetOS())
249 *Out.GetOS() << "Loaded " << *DynFunctionsAdded
250 << " functions from dynamic symbol table.\n";
251
252 auto Warn = [&](const Twine &Msg) {
253 Out.Report(
254 "Failed to load the .gnu_debugdata section", [&](raw_ostream &OS) {
255 OS << "warning: unable to read the .gnu_debugdata section: " << Msg
256 << "\n";
257 });
258 return Error::success();
259 };
260
262 return Warn("missing LZMA support (LLVM_ENABLE_LZMA)");
263 Expected<StringRef> Contents = DebugDataSect->getContents();
264 if (!Contents)
265 return Warn(toString(Contents.takeError()));
266 SmallVector<uint8_t, 0> Decompressed;
268 Decompressed))
269 return Warn("failed to decompress: " + toString(std::move(E)));
272 MemoryBufferRef(toStringRef(Decompressed), Obj.getFileName()));
273 if (!DebugObj)
274 return Warn("failed to parse the embedded ELF object: " +
275 toString(DebugObj.takeError()));
276
277 // Decompressed is freed when we return, so the names need to be copied.
278 Expected<size_t> DebugFunctionsAdded =
279 loadSymbols(**DebugObj, (*DebugObj)->symbols(), Out, Gsym,
280 /*CopyStrings=*/true);
281 if (!DebugFunctionsAdded)
282 return Warn(toString(DebugFunctionsAdded.takeError()));
283 if (Out.GetOS())
284 *Out.GetOS() << "Loaded " << *DebugFunctionsAdded
285 << " functions from .gnu_debugdata symbol table.\n";
286 return Error::success();
287}
288
290 OutputAggregator &Out,
291 GsymCreator &Gsym) {
292 using namespace llvm::object;
293
294 // Read build ID.
295 Gsym.setUUID(getUUID(Obj));
296
297 // Parse the symbol table.
298 Expected<size_t> FunctionsAddedCount =
299 loadSymbols(Obj, Obj.symbols(), Out, Gsym, /*CopyStrings=*/false);
300 if (!FunctionsAddedCount)
301 return FunctionsAddedCount.takeError();
302 if (Out.GetOS())
303 *Out.GetOS() << "Loaded " << *FunctionsAddedCount
304 << " functions from symbol table.\n";
305
306 // Mach-O symbol stubs have no symbol table entries of their own, so
307 // synthesize function infos for them using the indirect symbol table.
308 if (const auto *MachO = dyn_cast<MachOObjectFile>(&Obj)) {
309 const uint64_t StubsAddedCount = addMachOSymbolStubs(*MachO, Out, Gsym);
310 if (Out.GetOS())
311 *Out.GetOS() << "Loaded " << StubsAddedCount
312 << " functions from symbol stubs.\n";
313 }
314
315 if (const auto *ELFObj = dyn_cast<ELFObjectFileBase>(&Obj))
316 return loadGnuDebugDataSymbols(*ELFObj, Out, Gsym);
317 return Error::success();
318}
unsigned uint64_t
unsigned const MCDisassembler * Decoder
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static uint64_t addMachOSymbolStubs(const object::MachOObjectFile &MachO, OutputAggregator &Out, GsymCreator &Gsym)
Create function information entries for Mach-O symbol stubs.
static std::vector< uint8_t > getUUID(const object::ObjectFile &Obj)
static Expected< size_t > loadSymbols(const object::ObjectFile &Obj, object::ObjectFile::symbol_iterator_range Symbols, OutputAggregator &Out, GsymCreator &Gsym, bool CopyStrings)
constexpr uint32_t NT_GNU_BUILD_ID_TAG
static llvm::Error loadGnuDebugDataSymbols(const object::ELFObjectFileBase &Obj, OutputAggregator &Out, GsymCreator &Gsym)
const char * Msg
This file contains some functions that are useful when dealing with strings.
std::pair< llvm::MachO::Target, std::string > UUID
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
size_t size() const
Get the array size.
Definition ArrayRef.h:141
bool empty() const
Check if the array is empty.
Definition ArrayRef.h:136
const T * data() const
Definition ArrayRef.h:138
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Error takeError()
Take ownership of the stored error.
Definition Error.h:612
reference get()
Returns a reference to the stored T value.
Definition Error.h:582
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
constexpr bool empty() const
Check if the string is empty.
Definition StringRef.h:141
constexpr size_t size() const
Get the string size.
Definition StringRef.h:144
constexpr const char * data() const
Get a pointer to the start of the string (which may not be null terminated).
Definition StringRef.h:138
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
GsymCreator is used to emit GSYM data to a stand alone file or section within a file.
LLVM_ABI void addFunctionInfo(FunctionInfo &&FI)
Add a function info to this GSYM creator.
LLVM_ABI gsym_strp_t insertString(StringRef S, bool Copy=true)
Insert a string into the GSYM string table.
void setUUID(llvm::ArrayRef< uint8_t > UUIDBytes)
Set the UUID value.
LLVM_ABI size_t getNumFunctionInfos() const
Get the current number of FunctionInfo objects contained in this object.
LLVM_ABI bool IsValidTextAddress(uint64_t Addr) const
Check if an address is a valid code address.
static LLVM_ABI llvm::Error convert(const object::ObjectFile &Obj, OutputAggregator &Output, GsymCreator &Gsym)
Extract any object file data that is needed by the GsymCreator.
DataRefImpl getRawDataRefImpl() const
This class is the base class for all object file types.
Definition ObjectFile.h:231
static Expected< std::unique_ptr< ObjectFile > > createELFObjectFile(MemoryBufferRef Object, bool InitContent=true)
iterator_range< symbol_iterator > symbol_iterator_range
Definition ObjectFile.h:322
This is a value type class that represents a single section in the list of sections in the object fil...
Definition ObjectFile.h:83
This is a value type class that represents a single symbol in the list of symbols in the object file.
Definition ObjectFile.h:170
Expected< StringRef > getName() const
Definition ObjectFile.h:465
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
@ SECTION_TYPE
Definition MachO.h:114
@ S_SYMBOL_STUBS
S_SYMBOL_STUBS - Section with symbol stubs, byte size of stub in the Reserved2 field.
Definition MachO.h:144
constexpr uint32_t INDIRECT_SYMBOL_ABS
Definition MachO.h:219
constexpr uint32_t INDIRECT_SYMBOL_LOCAL
Definition MachO.h:218
constexpr size_t NameSize
Definition XCOFF.h:30
LLVM_ABI bool isAvailable()
Return true if LLVM was built with LZMA support (LLVM_ENABLE_LZMA).
LLVM_ABI Error decompress(ArrayRef< uint8_t > Input, SmallVectorImpl< uint8_t > &Output)
Decompress an xz stream.
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:577
LLVM_ABI void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner={})
Log all errors (if any) in E to OS.
Definition Error.cpp:61
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
Definition STLExtras.h:1685
ArrayRef< CharT > arrayRefFromStringRef(StringRef Input)
Construct an array ref of bytes from a string ref.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
constexpr uint64_t alignTo(uint64_t Size, Align A)
Returns a multiple of A needed to store Size bytes.
Definition Alignment.h:149
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
std::string toString(const APInt &I, unsigned Radix, bool Signed, bool formatAsCLiteral=false, bool UpperCase=true, bool InsertSeparators=false)
void consumeError(Error Err)
Consume a Error without doing anything.
Definition Error.h:1106
StringRef toStringRef(bool B)
Construct a string ref from a boolean.
uint32_t reserved2
Definition MachO.h:630
uint32_t reserved1
Definition MachO.h:629