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