1//===-- ELFDump.cpp - ELF-specific dumper -----------------------*- 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/// \file
10/// This file implements the ELF-specific dumper for llvm-objdump.
11///
12//===----------------------------------------------------------------------===//
13
14#include "ELFDump.h"
15
16#include "llvm-objdump.h"
17#include "llvm/Demangle/Demangle.h"
18#include "llvm/Object/ELFObjectFile.h"
19#include "llvm/Support/Format.h"
20#include "llvm/Support/raw_ostream.h"
21
22using namespace llvm;
23using namespace llvm::object;
24using namespace llvm::objdump;
25
26namespace {
27template <typename ELFT> class ELFDumper : public Dumper {
28public:
29 ELFDumper(const ELFObjectFile<ELFT> &O) : Dumper(O), Obj(O) {}
30 void printPrivateHeaders() override;
31 void printDynamicRelocations() override;
32
33private:
34 const ELFObjectFile<ELFT> &Obj;
35
36 const ELFFile<ELFT> &getELFFile() const { return Obj.getELFFile(); }
37 void printDynamicSection();
38 void printProgramHeaders();
39 void printSymbolVersion();
40 void printSymbolVersionDependency(const typename ELFT::Shdr &Sec);
41};
42} // namespace
43
44template <class ELFT>
45static std::unique_ptr<Dumper> createDumper(const ELFObjectFile<ELFT> &Obj) {
46 return std::make_unique<ELFDumper<ELFT>>(Obj);
47}
48
49std::unique_ptr<Dumper>
50objdump::createELFDumper(const object::ELFObjectFileBase &Obj) {
51 if (const auto *O = dyn_cast<ELF32LEObjectFile>(Val: &Obj))
52 return createDumper(Obj: *O);
53 if (const auto *O = dyn_cast<ELF32BEObjectFile>(Val: &Obj))
54 return createDumper(Obj: *O);
55 if (const auto *O = dyn_cast<ELF64LEObjectFile>(Val: &Obj))
56 return createDumper(Obj: *O);
57 return createDumper(Obj: cast<ELF64BEObjectFile>(Val: Obj));
58}
59
60template <class ELFT>
61static Expected<StringRef> getDynamicStrTab(const ELFFile<ELFT> &Elf) {
62 auto DynamicEntriesOrError = Elf.dynamicEntries();
63 if (!DynamicEntriesOrError)
64 return DynamicEntriesOrError.takeError();
65
66 typename ELFT::Xword StringTableSize{0};
67 const uint8_t *MappedAddr = nullptr;
68 for (const typename ELFT::Dyn &Dyn : *DynamicEntriesOrError) {
69 if (Dyn.d_tag == ELF::DT_STRTAB) {
70 auto MappedAddrOrError = Elf.toMappedAddr(Dyn.getPtr());
71 if (!MappedAddrOrError)
72 return MappedAddrOrError.takeError();
73 MappedAddr = *MappedAddrOrError;
74 }
75 if (Dyn.d_tag == ELF::DT_STRSZ)
76 StringTableSize = Dyn.getVal();
77 }
78 if (MappedAddr && StringTableSize)
79 return StringRef(reinterpret_cast<const char *>(MappedAddr),
80 StringTableSize);
81
82 // If the dynamic segment is not present, or is missing the important tags, we
83 // fall back on the sections.
84 auto SectionsOrError = Elf.sections();
85 if (!SectionsOrError)
86 return SectionsOrError.takeError();
87
88 for (const typename ELFT::Shdr &Sec : *SectionsOrError) {
89 if (Sec.sh_type == ELF::SHT_DYNAMIC)
90 return Elf.getLinkAsStrtab(Sec);
91 }
92
93 return createError(Err: "dynamic string table not found");
94}
95
96template <class ELFT>
97static Error getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
98 const RelocationRef &RelRef,
99 SmallVectorImpl<char> &Result) {
100 const ELFFile<ELFT> &EF = Obj->getELFFile();
101 DataRefImpl Rel = RelRef.getRawDataRefImpl();
102 auto SecOrErr = EF.getSection(Rel.d.a);
103 if (!SecOrErr)
104 return SecOrErr.takeError();
105
106 int64_t Addend = 0;
107 // If there is no Symbol associated with the relocation, we set the undef
108 // boolean value to 'true'. This will prevent us from calling functions that
109 // requires the relocation to be associated with a symbol.
110 //
111 // In SHT_REL case we would need to read the addend from section data.
112 // GNU objdump does not do that and we just follow for simplicity atm.
113 bool Undef = false;
114 if ((*SecOrErr)->sh_type == ELF::SHT_CREL) {
115 auto ERela = Obj->getCrel(Rel);
116 Addend = ERela.r_addend;
117 Undef = ERela.getSymbol(false) == 0;
118 } else if ((*SecOrErr)->sh_type == ELF::SHT_RELA) {
119 const typename ELFT::Rela *ERela = Obj->getRela(Rel);
120 Addend = ERela->r_addend;
121 Undef = ERela->getSymbol(false) == 0;
122 } else if ((*SecOrErr)->sh_type == ELF::SHT_REL) {
123 const typename ELFT::Rel *ERel = Obj->getRel(Rel);
124 Undef = ERel->getSymbol(false) == 0;
125 } else {
126 return make_error<BinaryError>();
127 }
128
129 // Default scheme is to print Target, as well as "+ <addend>" for nonzero
130 // addend. Should be acceptable for all normal purposes.
131 std::string FmtBuf;
132 raw_string_ostream Fmt(FmtBuf);
133
134 if (!Undef) {
135 symbol_iterator SI = RelRef.getSymbol();
136 Expected<const typename ELFT::Sym *> SymOrErr =
137 Obj->getSymbol(SI->getRawDataRefImpl());
138 // TODO: test this error.
139 if (!SymOrErr)
140 return SymOrErr.takeError();
141
142 if ((*SymOrErr)->getType() == ELF::STT_SECTION) {
143 Expected<section_iterator> SymSI = SI->getSection();
144 if (!SymSI)
145 return SymSI.takeError();
146 const typename ELFT::Shdr *SymSec =
147 Obj->getSection((*SymSI)->getRawDataRefImpl());
148 auto SecName = EF.getSectionName(*SymSec);
149 if (!SecName)
150 return SecName.takeError();
151 Fmt << *SecName;
152 } else {
153 Expected<StringRef> SymName = SI->getName();
154 if (!SymName)
155 return SymName.takeError();
156 Fmt << (Demangle ? demangle(MangledName: *SymName) : *SymName);
157 }
158 } else {
159 Fmt << "*ABS*";
160 }
161 if (Addend != 0) {
162 Fmt << (Addend < 0
163 ? "-"
164 : "+") << format(Fmt: "0x%" PRIx64,
165 Vals: (Addend < 0 ? -(uint64_t)Addend : (uint64_t)Addend));
166 }
167 Fmt.flush();
168 Result.append(in_start: FmtBuf.begin(), in_end: FmtBuf.end());
169 return Error::success();
170}
171
172Error objdump::getELFRelocationValueString(const ELFObjectFileBase *Obj,
173 const RelocationRef &Rel,
174 SmallVectorImpl<char> &Result) {
175 if (auto *ELF32LE = dyn_cast<ELF32LEObjectFile>(Val: Obj))
176 return getRelocationValueString(Obj: ELF32LE, RelRef: Rel, Result);
177 if (auto *ELF64LE = dyn_cast<ELF64LEObjectFile>(Val: Obj))
178 return getRelocationValueString(Obj: ELF64LE, RelRef: Rel, Result);
179 if (auto *ELF32BE = dyn_cast<ELF32BEObjectFile>(Val: Obj))
180 return getRelocationValueString(Obj: ELF32BE, RelRef: Rel, Result);
181 auto *ELF64BE = cast<ELF64BEObjectFile>(Val: Obj);
182 return getRelocationValueString(Obj: ELF64BE, RelRef: Rel, Result);
183}
184
185template <class ELFT>
186static uint64_t getSectionLMA(const ELFFile<ELFT> &Obj,
187 const object::ELFSectionRef &Sec) {
188 auto PhdrRangeOrErr = Obj.program_headers();
189 if (!PhdrRangeOrErr)
190 report_fatal_error(reason: Twine(toString(PhdrRangeOrErr.takeError())));
191
192 // Search for a PT_LOAD segment containing the requested section. Use this
193 // segment's p_addr to calculate the section's LMA.
194 for (const typename ELFT::Phdr &Phdr : *PhdrRangeOrErr)
195 if ((Phdr.p_type == ELF::PT_LOAD) &&
196 (isSectionInSegment<ELFT>(
197 Phdr, *cast<const ELFObjectFile<ELFT>>(Sec.getObject())
198 ->getSection(Sec.getRawDataRefImpl()))))
199 return Sec.getAddress() - Phdr.p_vaddr + Phdr.p_paddr;
200
201 // Return section's VMA if it isn't in a PT_LOAD segment.
202 return Sec.getAddress();
203}
204
205uint64_t objdump::getELFSectionLMA(const object::ELFSectionRef &Sec) {
206 if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Val: Sec.getObject()))
207 return getSectionLMA(Obj: ELFObj->getELFFile(), Sec);
208 else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Val: Sec.getObject()))
209 return getSectionLMA(Obj: ELFObj->getELFFile(), Sec);
210 else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Val: Sec.getObject()))
211 return getSectionLMA(Obj: ELFObj->getELFFile(), Sec);
212 const auto *ELFObj = cast<ELF64BEObjectFile>(Val: Sec.getObject());
213 return getSectionLMA(Obj: ELFObj->getELFFile(), Sec);
214}
215
216template <class ELFT> void ELFDumper<ELFT>::printDynamicSection() {
217 const ELFFile<ELFT> &Elf = getELFFile();
218 auto DynamicEntriesOrErr = Elf.dynamicEntries();
219 if (!DynamicEntriesOrErr) {
220 reportWarning(toString(DynamicEntriesOrErr.takeError()), Obj.getFileName());
221 return;
222 }
223 ArrayRef<typename ELFT::Dyn> DynamicEntries = *DynamicEntriesOrErr;
224
225 // Find the maximum tag name length to format the value column properly.
226 size_t MaxLen = 0;
227 for (const typename ELFT::Dyn &Dyn : DynamicEntries)
228 MaxLen = std::max(MaxLen, Elf.getDynamicTagAsString(Dyn.d_tag).size());
229 std::string TagFmt = " %-" + std::to_string(val: MaxLen) + "s ";
230
231 outs() << "\nDynamic Section:\n";
232
233 for (const typename ELFT::Dyn &Dyn : DynamicEntries) {
234 if (Dyn.d_tag == ELF::DT_NULL)
235 continue;
236
237 std::string Str = Elf.getDynamicTagAsString(Dyn.d_tag);
238
239 const char *Fmt =
240 ELFT::Is64Bits ? "0x%016" PRIx64 "\n" : "0x%08" PRIx64 "\n";
241 if (Dyn.d_tag == ELF::DT_NEEDED || Dyn.d_tag == ELF::DT_RPATH ||
242 Dyn.d_tag == ELF::DT_RUNPATH || Dyn.d_tag == ELF::DT_SONAME ||
243 Dyn.d_tag == ELF::DT_AUXILIARY || Dyn.d_tag == ELF::DT_FILTER) {
244 Expected<StringRef> StrTabOrErr = getDynamicStrTab(Elf);
245 if (StrTabOrErr) {
246 const char *Data = StrTabOrErr->data();
247 if (Dyn.getVal() >= StrTabOrErr->size()) {
248 reportWarning("invalid string table offset, string table size: 0x" +
249 Twine::utohexstr(Val: StrTabOrErr->size()),
250 Obj.getFileName());
251 outs() << format(Fmt: TagFmt.c_str(), Vals: Str.c_str())
252 << format(Fmt, Vals: (uint64_t)Dyn.getVal());
253 continue;
254 }
255 outs() << format(Fmt: TagFmt.c_str(), Vals: Str.c_str()) << Data + Dyn.getVal()
256 << "\n";
257 continue;
258 }
259 reportWarning(toString(E: StrTabOrErr.takeError()), Obj.getFileName());
260 consumeError(Err: StrTabOrErr.takeError());
261 }
262 outs() << format(Fmt: TagFmt.c_str(), Vals: Str.c_str())
263 << format(Fmt, Vals: (uint64_t)Dyn.getVal());
264 }
265}
266
267template <class ELFT> void ELFDumper<ELFT>::printProgramHeaders() {
268 outs() << "\nProgram Header:\n";
269 auto ProgramHeaderOrError = getELFFile().program_headers();
270 if (!ProgramHeaderOrError) {
271 reportWarning("unable to read program headers: " +
272 toString(ProgramHeaderOrError.takeError()),
273 Obj.getFileName());
274 return;
275 }
276
277 for (const typename ELFT::Phdr &Phdr : *ProgramHeaderOrError) {
278 switch (Phdr.p_type) {
279 case ELF::PT_DYNAMIC:
280 outs() << " DYNAMIC ";
281 break;
282 case ELF::PT_GNU_EH_FRAME:
283 outs() << "EH_FRAME ";
284 break;
285 case ELF::PT_GNU_RELRO:
286 outs() << " RELRO ";
287 break;
288 case ELF::PT_GNU_PROPERTY:
289 outs() << "PROPERTY ";
290 break;
291 case ELF::PT_GNU_STACK:
292 outs() << " STACK ";
293 break;
294 case ELF::PT_INTERP:
295 outs() << " INTERP ";
296 break;
297 case ELF::PT_LOAD:
298 outs() << " LOAD ";
299 break;
300 case ELF::PT_NOTE:
301 outs() << " NOTE ";
302 break;
303 case ELF::PT_OPENBSD_BOOTDATA:
304 outs() << "OPENBSD_BOOTDATA ";
305 break;
306 case ELF::PT_OPENBSD_MUTABLE:
307 outs() << "OPENBSD_MUTABLE ";
308 break;
309 case ELF::PT_OPENBSD_NOBTCFI:
310 outs() << "OPENBSD_NOBTCFI ";
311 break;
312 case ELF::PT_OPENBSD_RANDOMIZE:
313 outs() << "OPENBSD_RANDOMIZE ";
314 break;
315 case ELF::PT_OPENBSD_SYSCALLS:
316 outs() << "OPENBSD_SYSCALLS ";
317 break;
318 case ELF::PT_OPENBSD_WXNEEDED:
319 outs() << "OPENBSD_WXNEEDED ";
320 break;
321 case ELF::PT_PHDR:
322 outs() << " PHDR ";
323 break;
324 case ELF::PT_TLS:
325 outs() << " TLS ";
326 break;
327 default:
328 outs() << " UNKNOWN ";
329 }
330
331 const char *Fmt = ELFT::Is64Bits ? "0x%016" PRIx64 " " : "0x%08" PRIx64 " ";
332
333 outs() << "off " << format(Fmt, Vals: (uint64_t)Phdr.p_offset) << "vaddr "
334 << format(Fmt, Vals: (uint64_t)Phdr.p_vaddr) << "paddr "
335 << format(Fmt, Vals: (uint64_t)Phdr.p_paddr)
336 << format("align 2**%u\n", llvm::countr_zero<uint64_t>(Phdr.p_align))
337 << " filesz " << format(Fmt, Vals: (uint64_t)Phdr.p_filesz)
338 << "memsz " << format(Fmt, Vals: (uint64_t)Phdr.p_memsz) << "flags "
339 << ((Phdr.p_flags & ELF::PF_R) ? "r" : "-")
340 << ((Phdr.p_flags & ELF::PF_W) ? "w" : "-")
341 << ((Phdr.p_flags & ELF::PF_X) ? "x" : "-") << "\n";
342 }
343}
344
345template <typename ELFT> void ELFDumper<ELFT>::printDynamicRelocations() {
346 if (!any_of(Obj.sections(), [](const ELFSectionRef Sec) {
347 return Sec.getType() == ELF::SHT_DYNAMIC;
348 })) {
349 reportError(Obj.getFileName(), "not a dynamic object");
350 return;
351 }
352
353 std::vector<SectionRef> DynRelSec =
354 cast<ObjectFile>(Obj).dynamic_relocation_sections();
355 if (DynRelSec.empty())
356 return;
357
358 outs() << "\nDYNAMIC RELOCATION RECORDS\n";
359 const uint32_t OffsetPadding = (Obj.getBytesInAddress() > 4 ? 16 : 8);
360 const uint32_t TypePadding = 24;
361 outs() << left_justify(Str: "OFFSET", Width: OffsetPadding) << ' '
362 << left_justify(Str: "TYPE", Width: TypePadding) << " VALUE\n";
363
364 StringRef Fmt = Obj.getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64;
365 for (const SectionRef &Section : DynRelSec)
366 for (const RelocationRef &Reloc : Section.relocations()) {
367 uint64_t Address = Reloc.getOffset();
368 SmallString<32> RelocName;
369 SmallString<32> ValueStr;
370 Reloc.getTypeName(Result&: RelocName);
371 if (Error E = getELFRelocationValueString(&Obj, Reloc, ValueStr))
372 reportError(std::move(E), Obj.getFileName());
373 outs() << format(Fmt: Fmt.data(), Vals: Address) << ' '
374 << left_justify(Str: RelocName, Width: TypePadding) << ' ' << ValueStr << '\n';
375 }
376}
377
378template <class ELFT>
379void ELFDumper<ELFT>::printSymbolVersionDependency(
380 const typename ELFT::Shdr &Sec) {
381 outs() << "\nVersion References:\n";
382 Expected<std::vector<VerNeed>> V =
383 getELFFile().getVersionDependencies(Sec, this->WarningHandler);
384 if (!V) {
385 reportWarning(toString(E: V.takeError()), Obj.getFileName());
386 return;
387 }
388
389 raw_fd_ostream &OS = outs();
390 for (const VerNeed &VN : *V) {
391 OS << " required from " << VN.File << ":\n";
392 for (const VernAux &Aux : VN.AuxV)
393 OS << format(Fmt: " 0x%08x 0x%02x %02u %s\n", Vals: Aux.Hash, Vals: Aux.Flags,
394 Vals: Aux.Other, Vals: Aux.Name.c_str());
395 }
396}
397
398template <class ELFT> void ELFDumper<ELFT>::printSymbolVersion() {
399 const ELFFile<ELFT> &Elf = getELFFile();
400 StringRef FileName = Obj.getFileName();
401 ArrayRef<typename ELFT::Shdr> Sections =
402 unwrapOrError(Elf.sections(), FileName);
403 for (const typename ELFT::Shdr &Shdr : Sections) {
404 if (Shdr.sh_type != ELF::SHT_GNU_verneed &&
405 Shdr.sh_type != ELF::SHT_GNU_verdef)
406 continue;
407
408 if (Shdr.sh_type == ELF::SHT_GNU_verneed) {
409 printSymbolVersionDependency(Sec: Shdr);
410 } else {
411 OS << "\nVersion definitions:\n";
412 Expected<std::vector<VerDef>> V =
413 getELFFile().getVersionDefinitions(Shdr);
414 if (!V) {
415 this->reportUniqueWarning(V.takeError());
416 continue;
417 }
418 for (const VerDef &Def : *V) {
419 OS << Def.Ndx << ' ' << format_hex(N: Def.Flags, Width: 4) << ' '
420 << format_hex(N: Def.Hash, Width: 10) << ' ' << Def.Name << '\n';
421 if (!Def.AuxV.empty()) {
422 for (auto [I, Aux] : enumerate(First: Def.AuxV))
423 OS << (I ? ' ' : '\t') << Aux.Name;
424 OS << '\n';
425 }
426 }
427 }
428 }
429}
430
431template <class ELFT> void ELFDumper<ELFT>::printPrivateHeaders() {
432 printProgramHeaders();
433 printDynamicSection();
434 printSymbolVersion();
435}
436