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 | |
22 | using namespace llvm; |
23 | using namespace llvm::object; |
24 | using namespace llvm::objdump; |
25 | |
26 | namespace { |
27 | template <typename ELFT> class ELFDumper : public Dumper { |
28 | public: |
29 | ELFDumper(const ELFObjectFile<ELFT> &O) : Dumper(O), Obj(O) {} |
30 | void printPrivateHeaders() override; |
31 | void printDynamicRelocations() override; |
32 | |
33 | private: |
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 | |
44 | template <class ELFT> |
45 | static std::unique_ptr<Dumper> createDumper(const ELFObjectFile<ELFT> &Obj) { |
46 | return std::make_unique<ELFDumper<ELFT>>(Obj); |
47 | } |
48 | |
49 | std::unique_ptr<Dumper> |
50 | objdump::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 | |
60 | template <class ELFT> |
61 | static 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 | |
96 | template <class ELFT> |
97 | static 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 | |
172 | Error 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 | |
185 | template <class ELFT> |
186 | static 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 | |
205 | uint64_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 | |
216 | template <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 | |
267 | template <class ELFT> void ELFDumper<ELFT>::() { |
268 | outs() << "\nProgram Header:\n" ; |
269 | auto = 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 | |
345 | template <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 | |
378 | template <class ELFT> |
379 | void 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 | |
398 | template <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 | |
431 | template <class ELFT> void ELFDumper<ELFT>::() { |
432 | printProgramHeaders(); |
433 | printDynamicSection(); |
434 | printSymbolVersion(); |
435 | } |
436 | |