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