Files
RedBear-OS/local/recipes/dev/libclc/source/llvm/tools/llvm-objdump/ELFDump.cpp
T
vasilito cb424d7448 build: static patch-sanity linter (shift-left the malformed-patch class)
verify-patch-sanity.py validates every active recipe .patch has internally-
consistent hunk line counts — catching the 'malformed patch at line N' failure
at commit/CI/preflight time instead of hours into a cook. This cycle hit that
class three times (qtwaylandscanner, sddm, xwayland), each only discovered when
cookbook tried to apply the patch.

Running it across the repo found 29 latent malformed patches (validated against
GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22').
They were harmless only because they sit in vendored recipes (baked, not re-
applied) — but would fail on any version-bump re-derivation. --fix recounts the
hunk headers (body untouched) and repaired all 29.

Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test
(test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats
(empty placeholders, bare-@@ git hunks).
2026-08-01 05:13:02 +03:00

436 lines
15 KiB
C++

//===-- ELFDump.cpp - ELF-specific dumper -----------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file implements the ELF-specific dumper for llvm-objdump.
///
//===----------------------------------------------------------------------===//
#include "ELFDump.h"
#include "llvm-objdump.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace llvm::object;
using namespace llvm::objdump;
namespace {
template <typename ELFT> class ELFDumper : public Dumper {
public:
ELFDumper(const ELFObjectFile<ELFT> &O) : Dumper(O), Obj(O) {}
void printPrivateHeaders() override;
void printDynamicRelocations() override;
private:
const ELFObjectFile<ELFT> &Obj;
const ELFFile<ELFT> &getELFFile() const { return Obj.getELFFile(); }
void printDynamicSection();
void printProgramHeaders();
void printSymbolVersion();
void printSymbolVersionDependency(const typename ELFT::Shdr &Sec);
};
} // namespace
template <class ELFT>
static std::unique_ptr<Dumper> createDumper(const ELFObjectFile<ELFT> &Obj) {
return std::make_unique<ELFDumper<ELFT>>(Obj);
}
std::unique_ptr<Dumper>
objdump::createELFDumper(const object::ELFObjectFileBase &Obj) {
if (const auto *O = dyn_cast<ELF32LEObjectFile>(&Obj))
return createDumper(*O);
if (const auto *O = dyn_cast<ELF32BEObjectFile>(&Obj))
return createDumper(*O);
if (const auto *O = dyn_cast<ELF64LEObjectFile>(&Obj))
return createDumper(*O);
return createDumper(cast<ELF64BEObjectFile>(Obj));
}
template <class ELFT>
static Expected<StringRef> getDynamicStrTab(const ELFFile<ELFT> &Elf) {
auto DynamicEntriesOrError = Elf.dynamicEntries();
if (!DynamicEntriesOrError)
return DynamicEntriesOrError.takeError();
typename ELFT::Xword StringTableSize{0};
const uint8_t *MappedAddr = nullptr;
for (const typename ELFT::Dyn &Dyn : *DynamicEntriesOrError) {
if (Dyn.d_tag == ELF::DT_STRTAB) {
auto MappedAddrOrError = Elf.toMappedAddr(Dyn.getPtr());
if (!MappedAddrOrError)
return MappedAddrOrError.takeError();
MappedAddr = *MappedAddrOrError;
}
if (Dyn.d_tag == ELF::DT_STRSZ)
StringTableSize = Dyn.getVal();
}
if (MappedAddr && StringTableSize)
return StringRef(reinterpret_cast<const char *>(MappedAddr),
StringTableSize);
// If the dynamic segment is not present, or is missing the important tags, we
// fall back on the sections.
auto SectionsOrError = Elf.sections();
if (!SectionsOrError)
return SectionsOrError.takeError();
for (const typename ELFT::Shdr &Sec : *SectionsOrError) {
if (Sec.sh_type == ELF::SHT_DYNAMIC)
return Elf.getLinkAsStrtab(Sec);
}
return createError("dynamic string table not found");
}
template <class ELFT>
static Error getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
const RelocationRef &RelRef,
SmallVectorImpl<char> &Result) {
const ELFFile<ELFT> &EF = Obj->getELFFile();
DataRefImpl Rel = RelRef.getRawDataRefImpl();
auto SecOrErr = EF.getSection(Rel.d.a);
if (!SecOrErr)
return SecOrErr.takeError();
int64_t Addend = 0;
// If there is no Symbol associated with the relocation, we set the undef
// boolean value to 'true'. This will prevent us from calling functions that
// requires the relocation to be associated with a symbol.
//
// In SHT_REL case we would need to read the addend from section data.
// GNU objdump does not do that and we just follow for simplicity atm.
bool Undef = false;
if ((*SecOrErr)->sh_type == ELF::SHT_CREL) {
auto ERela = Obj->getCrel(Rel);
Addend = ERela.r_addend;
Undef = ERela.getSymbol(false) == 0;
} else if ((*SecOrErr)->sh_type == ELF::SHT_RELA) {
const typename ELFT::Rela *ERela = Obj->getRela(Rel);
Addend = ERela->r_addend;
Undef = ERela->getSymbol(false) == 0;
} else if ((*SecOrErr)->sh_type == ELF::SHT_REL) {
const typename ELFT::Rel *ERel = Obj->getRel(Rel);
Undef = ERel->getSymbol(false) == 0;
} else {
return make_error<BinaryError>();
}
// Default scheme is to print Target, as well as "+ <addend>" for nonzero
// addend. Should be acceptable for all normal purposes.
std::string FmtBuf;
raw_string_ostream Fmt(FmtBuf);
if (!Undef) {
symbol_iterator SI = RelRef.getSymbol();
Expected<const typename ELFT::Sym *> SymOrErr =
Obj->getSymbol(SI->getRawDataRefImpl());
// TODO: test this error.
if (!SymOrErr)
return SymOrErr.takeError();
if ((*SymOrErr)->getType() == ELF::STT_SECTION) {
Expected<section_iterator> SymSI = SI->getSection();
if (!SymSI)
return SymSI.takeError();
const typename ELFT::Shdr *SymSec =
Obj->getSection((*SymSI)->getRawDataRefImpl());
auto SecName = EF.getSectionName(*SymSec);
if (!SecName)
return SecName.takeError();
Fmt << *SecName;
} else {
Expected<StringRef> SymName = SI->getName();
if (!SymName)
return SymName.takeError();
Fmt << (Demangle ? demangle(*SymName) : *SymName);
}
} else {
Fmt << "*ABS*";
}
if (Addend != 0) {
Fmt << (Addend < 0
? "-"
: "+") << format("0x%" PRIx64,
(Addend < 0 ? -(uint64_t)Addend : (uint64_t)Addend));
}
Fmt.flush();
Result.append(FmtBuf.begin(), FmtBuf.end());
return Error::success();
}
Error objdump::getELFRelocationValueString(const ELFObjectFileBase *Obj,
const RelocationRef &Rel,
SmallVectorImpl<char> &Result) {
if (auto *ELF32LE = dyn_cast<ELF32LEObjectFile>(Obj))
return getRelocationValueString(ELF32LE, Rel, Result);
if (auto *ELF64LE = dyn_cast<ELF64LEObjectFile>(Obj))
return getRelocationValueString(ELF64LE, Rel, Result);
if (auto *ELF32BE = dyn_cast<ELF32BEObjectFile>(Obj))
return getRelocationValueString(ELF32BE, Rel, Result);
auto *ELF64BE = cast<ELF64BEObjectFile>(Obj);
return getRelocationValueString(ELF64BE, Rel, Result);
}
template <class ELFT>
static uint64_t getSectionLMA(const ELFFile<ELFT> &Obj,
const object::ELFSectionRef &Sec) {
auto PhdrRangeOrErr = Obj.program_headers();
if (!PhdrRangeOrErr)
report_fatal_error(Twine(toString(PhdrRangeOrErr.takeError())));
// Search for a PT_LOAD segment containing the requested section. Use this
// segment's p_addr to calculate the section's LMA.
for (const typename ELFT::Phdr &Phdr : *PhdrRangeOrErr)
if ((Phdr.p_type == ELF::PT_LOAD) &&
(isSectionInSegment<ELFT>(
Phdr, *cast<const ELFObjectFile<ELFT>>(Sec.getObject())
->getSection(Sec.getRawDataRefImpl()))))
return Sec.getAddress() - Phdr.p_vaddr + Phdr.p_paddr;
// Return section's VMA if it isn't in a PT_LOAD segment.
return Sec.getAddress();
}
uint64_t objdump::getELFSectionLMA(const object::ELFSectionRef &Sec) {
if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Sec.getObject()))
return getSectionLMA(ELFObj->getELFFile(), Sec);
else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Sec.getObject()))
return getSectionLMA(ELFObj->getELFFile(), Sec);
else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Sec.getObject()))
return getSectionLMA(ELFObj->getELFFile(), Sec);
const auto *ELFObj = cast<ELF64BEObjectFile>(Sec.getObject());
return getSectionLMA(ELFObj->getELFFile(), Sec);
}
template <class ELFT> void ELFDumper<ELFT>::printDynamicSection() {
const ELFFile<ELFT> &Elf = getELFFile();
auto DynamicEntriesOrErr = Elf.dynamicEntries();
if (!DynamicEntriesOrErr) {
reportWarning(toString(DynamicEntriesOrErr.takeError()), Obj.getFileName());
return;
}
ArrayRef<typename ELFT::Dyn> DynamicEntries = *DynamicEntriesOrErr;
// Find the maximum tag name length to format the value column properly.
size_t MaxLen = 0;
for (const typename ELFT::Dyn &Dyn : DynamicEntries)
MaxLen = std::max(MaxLen, Elf.getDynamicTagAsString(Dyn.d_tag).size());
std::string TagFmt = " %-" + std::to_string(MaxLen) + "s ";
outs() << "\nDynamic Section:\n";
for (const typename ELFT::Dyn &Dyn : DynamicEntries) {
if (Dyn.d_tag == ELF::DT_NULL)
continue;
std::string Str = Elf.getDynamicTagAsString(Dyn.d_tag);
const char *Fmt =
ELFT::Is64Bits ? "0x%016" PRIx64 "\n" : "0x%08" PRIx64 "\n";
if (Dyn.d_tag == ELF::DT_NEEDED || Dyn.d_tag == ELF::DT_RPATH ||
Dyn.d_tag == ELF::DT_RUNPATH || Dyn.d_tag == ELF::DT_SONAME ||
Dyn.d_tag == ELF::DT_AUXILIARY || Dyn.d_tag == ELF::DT_FILTER) {
Expected<StringRef> StrTabOrErr = getDynamicStrTab(Elf);
if (StrTabOrErr) {
const char *Data = StrTabOrErr->data();
if (Dyn.getVal() >= StrTabOrErr->size()) {
reportWarning("invalid string table offset, string table size: 0x" +
Twine::utohexstr(StrTabOrErr->size()),
Obj.getFileName());
outs() << format(TagFmt.c_str(), Str.c_str())
<< format(Fmt, (uint64_t)Dyn.getVal());
continue;
}
outs() << format(TagFmt.c_str(), Str.c_str()) << Data + Dyn.getVal()
<< "\n";
continue;
}
reportWarning(toString(StrTabOrErr.takeError()), Obj.getFileName());
consumeError(StrTabOrErr.takeError());
}
outs() << format(TagFmt.c_str(), Str.c_str())
<< format(Fmt, (uint64_t)Dyn.getVal());
}
}
template <class ELFT> void ELFDumper<ELFT>::printProgramHeaders() {
outs() << "\nProgram Header:\n";
auto ProgramHeaderOrError = getELFFile().program_headers();
if (!ProgramHeaderOrError) {
reportWarning("unable to read program headers: " +
toString(ProgramHeaderOrError.takeError()),
Obj.getFileName());
return;
}
for (const typename ELFT::Phdr &Phdr : *ProgramHeaderOrError) {
switch (Phdr.p_type) {
case ELF::PT_DYNAMIC:
outs() << " DYNAMIC ";
break;
case ELF::PT_GNU_EH_FRAME:
outs() << "EH_FRAME ";
break;
case ELF::PT_GNU_RELRO:
outs() << " RELRO ";
break;
case ELF::PT_GNU_PROPERTY:
outs() << "PROPERTY ";
break;
case ELF::PT_GNU_STACK:
outs() << " STACK ";
break;
case ELF::PT_INTERP:
outs() << " INTERP ";
break;
case ELF::PT_LOAD:
outs() << " LOAD ";
break;
case ELF::PT_NOTE:
outs() << " NOTE ";
break;
case ELF::PT_OPENBSD_BOOTDATA:
outs() << "OPENBSD_BOOTDATA ";
break;
case ELF::PT_OPENBSD_MUTABLE:
outs() << "OPENBSD_MUTABLE ";
break;
case ELF::PT_OPENBSD_NOBTCFI:
outs() << "OPENBSD_NOBTCFI ";
break;
case ELF::PT_OPENBSD_RANDOMIZE:
outs() << "OPENBSD_RANDOMIZE ";
break;
case ELF::PT_OPENBSD_SYSCALLS:
outs() << "OPENBSD_SYSCALLS ";
break;
case ELF::PT_OPENBSD_WXNEEDED:
outs() << "OPENBSD_WXNEEDED ";
break;
case ELF::PT_PHDR:
outs() << " PHDR ";
break;
case ELF::PT_TLS:
outs() << " TLS ";
break;
default:
outs() << " UNKNOWN ";
}
const char *Fmt = ELFT::Is64Bits ? "0x%016" PRIx64 " " : "0x%08" PRIx64 " ";
outs() << "off " << format(Fmt, (uint64_t)Phdr.p_offset) << "vaddr "
<< format(Fmt, (uint64_t)Phdr.p_vaddr) << "paddr "
<< format(Fmt, (uint64_t)Phdr.p_paddr)
<< format("align 2**%u\n", llvm::countr_zero<uint64_t>(Phdr.p_align))
<< " filesz " << format(Fmt, (uint64_t)Phdr.p_filesz)
<< "memsz " << format(Fmt, (uint64_t)Phdr.p_memsz) << "flags "
<< ((Phdr.p_flags & ELF::PF_R) ? "r" : "-")
<< ((Phdr.p_flags & ELF::PF_W) ? "w" : "-")
<< ((Phdr.p_flags & ELF::PF_X) ? "x" : "-") << "\n";
}
}
template <typename ELFT> void ELFDumper<ELFT>::printDynamicRelocations() {
if (!any_of(Obj.sections(), [](const ELFSectionRef Sec) {
return Sec.getType() == ELF::SHT_DYNAMIC;
})) {
reportError(Obj.getFileName(), "not a dynamic object");
return;
}
std::vector<SectionRef> DynRelSec =
cast<ObjectFile>(Obj).dynamic_relocation_sections();
if (DynRelSec.empty())
return;
outs() << "\nDYNAMIC RELOCATION RECORDS\n";
const uint32_t OffsetPadding = (Obj.getBytesInAddress() > 4 ? 16 : 8);
const uint32_t TypePadding = 24;
outs() << left_justify("OFFSET", OffsetPadding) << ' '
<< left_justify("TYPE", TypePadding) << " VALUE\n";
StringRef Fmt = Obj.getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64;
for (const SectionRef &Section : DynRelSec)
for (const RelocationRef &Reloc : Section.relocations()) {
uint64_t Address = Reloc.getOffset();
SmallString<32> RelocName;
SmallString<32> ValueStr;
Reloc.getTypeName(RelocName);
if (Error E = getELFRelocationValueString(&Obj, Reloc, ValueStr))
reportError(std::move(E), Obj.getFileName());
outs() << format(Fmt.data(), Address) << ' '
<< left_justify(RelocName, TypePadding) << ' ' << ValueStr << '\n';
}
}
template <class ELFT>
void ELFDumper<ELFT>::printSymbolVersionDependency(
const typename ELFT::Shdr &Sec) {
outs() << "\nVersion References:\n";
Expected<std::vector<VerNeed>> V =
getELFFile().getVersionDependencies(Sec, this->WarningHandler);
if (!V) {
reportWarning(toString(V.takeError()), Obj.getFileName());
return;
}
raw_fd_ostream &OS = outs();
for (const VerNeed &VN : *V) {
OS << " required from " << VN.File << ":\n";
for (const VernAux &Aux : VN.AuxV)
OS << format(" 0x%08x 0x%02x %02u %s\n", Aux.Hash, Aux.Flags,
Aux.Other, Aux.Name.c_str());
}
}
template <class ELFT> void ELFDumper<ELFT>::printSymbolVersion() {
const ELFFile<ELFT> &Elf = getELFFile();
StringRef FileName = Obj.getFileName();
ArrayRef<typename ELFT::Shdr> Sections =
unwrapOrError(Elf.sections(), FileName);
for (const typename ELFT::Shdr &Shdr : Sections) {
if (Shdr.sh_type != ELF::SHT_GNU_verneed &&
Shdr.sh_type != ELF::SHT_GNU_verdef)
continue;
if (Shdr.sh_type == ELF::SHT_GNU_verneed) {
printSymbolVersionDependency(Shdr);
} else {
OS << "\nVersion definitions:\n";
Expected<std::vector<VerDef>> V =
getELFFile().getVersionDefinitions(Shdr);
if (!V) {
this->reportUniqueWarning(V.takeError());
continue;
}
for (const VerDef &Def : *V) {
OS << Def.Ndx << ' ' << format_hex(Def.Flags, 4) << ' '
<< format_hex(Def.Hash, 10) << ' ' << Def.Name << '\n';
if (!Def.AuxV.empty()) {
for (auto [I, Aux] : enumerate(Def.AuxV))
OS << (I ? ' ' : '\t') << Aux.Name;
OS << '\n';
}
}
}
}
}
template <class ELFT> void ELFDumper<ELFT>::printPrivateHeaders() {
printProgramHeaders();
printDynamicSection();
printSymbolVersion();
}