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 Result.append(in_start: FmtBuf.begin(), in_end: FmtBuf.end());
168 return Error::success();
169}
170
171Error objdump::getELFRelocationValueString(const ELFObjectFileBase *Obj,
172 const RelocationRef &Rel,
173 SmallVectorImpl<char> &Result) {
174 if (auto *ELF32LE = dyn_cast<ELF32LEObjectFile>(Val: Obj))
175 return getRelocationValueString(Obj: ELF32LE, RelRef: Rel, Result);
176 if (auto *ELF64LE = dyn_cast<ELF64LEObjectFile>(Val: Obj))
177 return getRelocationValueString(Obj: ELF64LE, RelRef: Rel, Result);
178 if (auto *ELF32BE = dyn_cast<ELF32BEObjectFile>(Val: Obj))
179 return getRelocationValueString(Obj: ELF32BE, RelRef: Rel, Result);
180 auto *ELF64BE = cast<ELF64BEObjectFile>(Val: Obj);
181 return getRelocationValueString(Obj: ELF64BE, RelRef: Rel, Result);
182}
183
184template <class ELFT>
185static uint64_t getSectionLMA(const ELFFile<ELFT> &Obj,
186 const object::ELFSectionRef &Sec) {
187 auto PhdrRangeOrErr = Obj.program_headers();
188 if (!PhdrRangeOrErr)
189 report_fatal_error(reason: Twine(toString(PhdrRangeOrErr.takeError())));
190
191 // Search for a PT_LOAD segment containing the requested section. Use this
192 // segment's p_addr to calculate the section's LMA.
193 for (const typename ELFT::Phdr &Phdr : *PhdrRangeOrErr)
194 if ((Phdr.p_type == ELF::PT_LOAD) &&
195 (isSectionInSegment<ELFT>(
196 Phdr, *cast<const ELFObjectFile<ELFT>>(Sec.getObject())
197 ->getSection(Sec.getRawDataRefImpl()))))
198 return Sec.getAddress() - Phdr.p_vaddr + Phdr.p_paddr;
199
200 // Return section's VMA if it isn't in a PT_LOAD segment.
201 return Sec.getAddress();
202}
203
204uint64_t objdump::getELFSectionLMA(const object::ELFSectionRef &Sec) {
205 if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Val: Sec.getObject()))
206 return getSectionLMA(Obj: ELFObj->getELFFile(), Sec);
207 else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Val: Sec.getObject()))
208 return getSectionLMA(Obj: ELFObj->getELFFile(), Sec);
209 else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Val: Sec.getObject()))
210 return getSectionLMA(Obj: ELFObj->getELFFile(), Sec);
211 const auto *ELFObj = cast<ELF64BEObjectFile>(Val: Sec.getObject());
212 return getSectionLMA(Obj: ELFObj->getELFFile(), Sec);
213}
214
215template <class ELFT> void ELFDumper<ELFT>::printDynamicSection() {
216 const ELFFile<ELFT> &Elf = getELFFile();
217 auto DynamicEntriesOrErr = Elf.dynamicEntries();
218 if (!DynamicEntriesOrErr) {
219 reportWarning(toString(DynamicEntriesOrErr.takeError()), Obj.getFileName());
220 return;
221 }
222 ArrayRef<typename ELFT::Dyn> DynamicEntries = *DynamicEntriesOrErr;
223
224 // Find the maximum tag name length to format the value column properly.
225 size_t MaxLen = 0;
226 for (const typename ELFT::Dyn &Dyn : DynamicEntries)
227 MaxLen = std::max(MaxLen, Elf.getDynamicTagAsString(Dyn.d_tag).size());
228 std::string TagFmt = " %-" + std::to_string(val: MaxLen) + "s ";
229
230 outs() << "\nDynamic Section:\n";
231
232 for (const typename ELFT::Dyn &Dyn : DynamicEntries) {
233 if (Dyn.d_tag == ELF::DT_NULL)
234 continue;
235
236 std::string Str = Elf.getDynamicTagAsString(Dyn.d_tag);
237
238 const char *Fmt =
239 ELFT::Is64Bits ? "0x%016" PRIx64 "\n" : "0x%08" PRIx64 "\n";
240 if (Dyn.d_tag == ELF::DT_NEEDED || Dyn.d_tag == ELF::DT_RPATH ||
241 Dyn.d_tag == ELF::DT_RUNPATH || Dyn.d_tag == ELF::DT_SONAME ||
242 Dyn.d_tag == ELF::DT_AUXILIARY || Dyn.d_tag == ELF::DT_FILTER) {
243 Expected<StringRef> StrTabOrErr = getDynamicStrTab(Elf);
244 if (StrTabOrErr) {
245 const char *Data = StrTabOrErr->data();
246 if (Dyn.getVal() >= StrTabOrErr->size()) {
247 reportWarning("invalid string table offset, string table size: 0x" +
248 Twine::utohexstr(Val: StrTabOrErr->size()),
249 Obj.getFileName());
250 outs() << format(Fmt: TagFmt.c_str(), Vals: Str.c_str())
251 << format(Fmt, Vals: (uint64_t)Dyn.getVal());
252 continue;
253 }
254 outs() << format(Fmt: TagFmt.c_str(), Vals: Str.c_str()) << Data + Dyn.getVal()
255 << "\n";
256 continue;
257 }
258 reportWarning(toString(E: StrTabOrErr.takeError()), Obj.getFileName());
259 consumeError(Err: StrTabOrErr.takeError());
260 }
261 outs() << format(Fmt: TagFmt.c_str(), Vals: Str.c_str())
262 << format(Fmt, Vals: (uint64_t)Dyn.getVal());
263 }
264}
265
266template <class ELFT> void ELFDumper<ELFT>::printProgramHeaders() {
267 outs() << "\nProgram Header:\n";
268 auto ProgramHeaderOrError = getELFFile().program_headers();
269 if (!ProgramHeaderOrError) {
270 reportWarning("unable to read program headers: " +
271 toString(ProgramHeaderOrError.takeError()),
272 Obj.getFileName());
273 return;
274 }
275
276 for (const typename ELFT::Phdr &Phdr : *ProgramHeaderOrError) {
277 switch (Phdr.p_type) {
278 case ELF::PT_DYNAMIC:
279 outs() << " DYNAMIC ";
280 break;
281 case ELF::PT_GNU_EH_FRAME:
282 outs() << "EH_FRAME ";
283 break;
284 case ELF::PT_GNU_RELRO:
285 outs() << " RELRO ";
286 break;
287 case ELF::PT_GNU_PROPERTY:
288 outs() << "PROPERTY ";
289 break;
290 case ELF::PT_GNU_STACK:
291 outs() << " STACK ";
292 break;
293 case ELF::PT_GNU_SFRAME:
294 outs() << " SFRAME ";
295 break;
296 case ELF::PT_INTERP:
297 outs() << " INTERP ";
298 break;
299 case ELF::PT_LOAD:
300 outs() << " LOAD ";
301 break;
302 case ELF::PT_NOTE:
303 outs() << " NOTE ";
304 break;
305 case ELF::PT_OPENBSD_BOOTDATA:
306 outs() << "OPENBSD_BOOTDATA ";
307 break;
308 case ELF::PT_OPENBSD_MUTABLE:
309 outs() << "OPENBSD_MUTABLE ";
310 break;
311 case ELF::PT_OPENBSD_NOBTCFI:
312 outs() << "OPENBSD_NOBTCFI ";
313 break;
314 case ELF::PT_OPENBSD_RANDOMIZE:
315 outs() << "OPENBSD_RANDOMIZE ";
316 break;
317 case ELF::PT_OPENBSD_SYSCALLS:
318 outs() << "OPENBSD_SYSCALLS ";
319 break;
320 case ELF::PT_OPENBSD_WXNEEDED:
321 outs() << "OPENBSD_WXNEEDED ";
322 break;
323 case ELF::PT_PHDR:
324 outs() << " PHDR ";
325 break;
326 case ELF::PT_TLS:
327 outs() << " TLS ";
328 break;
329 default:
330 outs() << " UNKNOWN ";
331 }
332
333 const char *Fmt = ELFT::Is64Bits ? "0x%016" PRIx64 " " : "0x%08" PRIx64 " ";
334
335 outs() << "off " << format(Fmt, Vals: (uint64_t)Phdr.p_offset) << "vaddr "
336 << format(Fmt, Vals: (uint64_t)Phdr.p_vaddr) << "paddr "
337 << format(Fmt, Vals: (uint64_t)Phdr.p_paddr)
338 << format("align 2**%u\n", llvm::countr_zero<uint64_t>(Phdr.p_align))
339 << " filesz " << format(Fmt, Vals: (uint64_t)Phdr.p_filesz)
340 << "memsz " << format(Fmt, Vals: (uint64_t)Phdr.p_memsz) << "flags "
341 << ((Phdr.p_flags & ELF::PF_R) ? "r" : "-")
342 << ((Phdr.p_flags & ELF::PF_W) ? "w" : "-")
343 << ((Phdr.p_flags & ELF::PF_X) ? "x" : "-") << "\n";
344 }
345}
346
347template <typename ELFT> void ELFDumper<ELFT>::printDynamicRelocations() {
348 if (!any_of(Obj.sections(), [](const ELFSectionRef Sec) {
349 return Sec.getType() == ELF::SHT_DYNAMIC;
350 })) {
351 reportError(Obj.getFileName(), "not a dynamic object");
352 return;
353 }
354
355 std::vector<SectionRef> DynRelSec =
356 cast<ObjectFile>(Obj).dynamic_relocation_sections();
357 if (DynRelSec.empty())
358 return;
359
360 outs() << "\nDYNAMIC RELOCATION RECORDS\n";
361 const uint32_t OffsetPadding = (Obj.getBytesInAddress() > 4 ? 16 : 8);
362 const uint32_t TypePadding = 24;
363 outs() << left_justify(Str: "OFFSET", Width: OffsetPadding) << ' '
364 << left_justify(Str: "TYPE", Width: TypePadding) << " VALUE\n";
365
366 StringRef Fmt = Obj.getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64;
367 for (const SectionRef &Section : DynRelSec)
368 for (const RelocationRef &Reloc : Section.relocations()) {
369 uint64_t Address = Reloc.getOffset();
370 SmallString<32> RelocName;
371 SmallString<32> ValueStr;
372 Reloc.getTypeName(Result&: RelocName);
373 if (Error E = getELFRelocationValueString(&Obj, Reloc, ValueStr))
374 reportError(std::move(E), Obj.getFileName());
375 outs() << format(Fmt: Fmt.data(), Vals: Address) << ' '
376 << left_justify(Str: RelocName, Width: TypePadding) << ' ' << ValueStr << '\n';
377 }
378}
379
380template <class ELFT>
381void ELFDumper<ELFT>::printSymbolVersionDependency(
382 const typename ELFT::Shdr &Sec) {
383 outs() << "\nVersion References:\n";
384 Expected<std::vector<VerNeed>> V =
385 getELFFile().getVersionDependencies(Sec, this->WarningHandler);
386 if (!V) {
387 reportWarning(toString(E: V.takeError()), Obj.getFileName());
388 return;
389 }
390
391 raw_fd_ostream &OS = outs();
392 for (const VerNeed &VN : *V) {
393 OS << " required from " << VN.File << ":\n";
394 for (const VernAux &Aux : VN.AuxV)
395 OS << format(Fmt: " 0x%08x 0x%02x %02u %s\n", Vals: Aux.Hash, Vals: Aux.Flags,
396 Vals: Aux.Other, Vals: Aux.Name.c_str());
397 }
398}
399
400template <class ELFT> void ELFDumper<ELFT>::printSymbolVersion() {
401 const ELFFile<ELFT> &Elf = getELFFile();
402 StringRef FileName = Obj.getFileName();
403 ArrayRef<typename ELFT::Shdr> Sections =
404 unwrapOrError(Elf.sections(), FileName);
405 for (const typename ELFT::Shdr &Shdr : Sections) {
406 if (Shdr.sh_type != ELF::SHT_GNU_verneed &&
407 Shdr.sh_type != ELF::SHT_GNU_verdef)
408 continue;
409
410 if (Shdr.sh_type == ELF::SHT_GNU_verneed) {
411 printSymbolVersionDependency(Sec: Shdr);
412 } else {
413 OS << "\nVersion definitions:\n";
414 Expected<std::vector<VerDef>> V =
415 getELFFile().getVersionDefinitions(Shdr);
416 if (!V) {
417 this->reportUniqueWarning(V.takeError());
418 continue;
419 }
420 for (const VerDef &Def : *V) {
421 OS << Def.Ndx << ' ' << format_hex(N: Def.Flags, Width: 4) << ' '
422 << format_hex(N: Def.Hash, Width: 10) << ' ' << Def.Name << '\n';
423 if (!Def.AuxV.empty()) {
424 for (auto [I, Aux] : enumerate(First: Def.AuxV))
425 OS << (I ? ' ' : '\t') << Aux.Name;
426 OS << '\n';
427 }
428 }
429 }
430 }
431}
432
433template <class ELFT> void ELFDumper<ELFT>::printPrivateHeaders() {
434 printProgramHeaders();
435 printDynamicSection();
436 printSymbolVersion();
437}
438