[ELF] Check the Elf_Rel addends for dynamic relocations

There used to be many cases where addends for Elf_Rel were not emitted in
the final object file (mostly when building for MIPS64 since the input .o
files use RELA but the output uses REL). These cases have been fixed since,
but this patch adds a check to ensure that the written values are correct.
It is based on a previous patch that I added to the CHERI fork of LLD since
we were using MIPS64 as a baseline. The work has now almost entirely
shifted to RISC-V and Arm Morello (which use Elf_Rela), but I thought
it would be useful to upstream our local changes anyway.

This patch adds a (hidden) command line flag --check-dynamic-relocations
that can be used to enable these checks. It is also on by default in
assertions builds for targets that handle all dynamic relocations kinds
that LLD can emit in Target::getImplicitAddend(). Currently this is
enabled for ARM, MIPS, and I386.

Reviewed By: MaskRay

Differential Revision: https://reviews.llvm.org/D101450
This commit is contained in:
Alex Richardson 2021-07-09 10:05:18 +01:00
parent 6d87ca08ae
commit 35c5e564e6
13 changed files with 146 additions and 3 deletions

View File

@ -707,20 +707,29 @@ void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const {
switch (type) {
default:
internalLinkerError(getErrorLocation(buf),
"cannot read addend for relocation " + toString(type));
return 0;
case R_ARM_ABS32:
case R_ARM_BASE_PREL:
case R_ARM_GLOB_DAT:
case R_ARM_GOTOFF32:
case R_ARM_GOT_BREL:
case R_ARM_GOT_PREL:
case R_ARM_IRELATIVE:
case R_ARM_REL32:
case R_ARM_RELATIVE:
case R_ARM_SBREL32:
case R_ARM_TARGET1:
case R_ARM_TARGET2:
case R_ARM_TLS_DTPMOD32:
case R_ARM_TLS_DTPOFF32:
case R_ARM_TLS_GD32:
case R_ARM_TLS_LDM32:
case R_ARM_TLS_LDO32:
case R_ARM_TLS_IE32:
case R_ARM_TLS_LDM32:
case R_ARM_TLS_LE32:
case R_ARM_TLS_LDO32:
case R_ARM_TLS_TPOFF32:
return SignExtend64<32>(read32le(buf));
case R_ARM_PREL31:
return SignExtend64<31>(read32le(buf));
@ -828,6 +837,10 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const {
uint64_t imm12 = read16le(buf + 2) & 0x0fff;
return u ? imm12 : -imm12;
}
case R_ARM_NONE:
case R_ARM_JUMP_SLOT:
// These relocations are defined as not having an implicit addend.
return 0;
}
}

View File

@ -385,8 +385,10 @@ int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *buf, RelType type) const {
const endianness e = ELFT::TargetEndianness;
switch (type) {
case R_MIPS_32:
case R_MIPS_REL32:
case R_MIPS_GPREL32:
case R_MIPS_TLS_DTPREL32:
case R_MIPS_TLS_DTPMOD32:
case R_MIPS_TLS_TPREL32:
return SignExtend64<32>(read32(buf));
case R_MIPS_26:
@ -394,25 +396,37 @@ int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *buf, RelType type) const {
// we should use another expression for calculation:
// ((A << 2) | (P & 0xf0000000)) >> 2
return SignExtend64<28>(read32(buf) << 2);
case R_MIPS_CALL_HI16:
case R_MIPS_GOT16:
case R_MIPS_GOT_HI16:
case R_MIPS_HI16:
case R_MIPS_PCHI16:
return SignExtend64<16>(read32(buf)) << 16;
case R_MIPS_CALL16:
case R_MIPS_CALL_LO16:
case R_MIPS_GOT_LO16:
case R_MIPS_GPREL16:
case R_MIPS_LO16:
case R_MIPS_PCLO16:
case R_MIPS_TLS_DTPREL_HI16:
case R_MIPS_TLS_DTPREL_LO16:
case R_MIPS_TLS_GD:
case R_MIPS_TLS_GOTTPREL:
case R_MIPS_TLS_LDM:
case R_MIPS_TLS_TPREL_HI16:
case R_MIPS_TLS_TPREL_LO16:
return SignExtend64<16>(read32(buf));
case R_MICROMIPS_GOT16:
case R_MICROMIPS_HI16:
return SignExtend64<16>(readShuffle<e>(buf)) << 16;
case R_MICROMIPS_CALL16:
case R_MICROMIPS_GPREL16:
case R_MICROMIPS_LO16:
case R_MICROMIPS_TLS_DTPREL_HI16:
case R_MICROMIPS_TLS_DTPREL_LO16:
case R_MICROMIPS_TLS_GD:
case R_MICROMIPS_TLS_GOTTPREL:
case R_MICROMIPS_TLS_LDM:
case R_MICROMIPS_TLS_TPREL_HI16:
case R_MICROMIPS_TLS_TPREL_LO16:
return SignExtend64<16>(readShuffle<e>(buf));
@ -446,7 +460,22 @@ int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *buf, RelType type) const {
return SignExtend64<25>(readShuffle<e>(buf) << 2);
case R_MICROMIPS_PC26_S1:
return SignExtend64<27>(readShuffle<e>(buf) << 1);
case R_MIPS_64:
case R_MIPS_TLS_DTPMOD64:
case R_MIPS_TLS_DTPREL64:
case R_MIPS_TLS_TPREL64:
case (R_MIPS_64 << 8) | R_MIPS_REL32:
return read64(buf);
case R_MIPS_COPY:
return config->is64 ? read64(buf) : read32(buf);
case R_MIPS_NONE:
case R_MIPS_JUMP_SLOT:
case R_MIPS_JALR:
// These relocations are defined as not having an implicit addend.
return 0;
default:
internalLinkerError(getErrorLocation(buf),
"cannot read addend for relocation " + toString(type));
return 0;
}
}

View File

@ -250,16 +250,36 @@ int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const {
case R_386_PC16:
return SignExtend64<16>(read16le(buf));
case R_386_32:
case R_386_GLOB_DAT:
case R_386_GOT32:
case R_386_GOT32X:
case R_386_GOTOFF:
case R_386_GOTPC:
case R_386_IRELATIVE:
case R_386_PC32:
case R_386_PLT32:
case R_386_RELATIVE:
case R_386_TLS_DTPMOD32:
case R_386_TLS_DTPOFF32:
case R_386_TLS_LDO_32:
case R_386_TLS_LDM:
case R_386_TLS_IE:
case R_386_TLS_IE_32:
case R_386_TLS_LE:
case R_386_TLS_LE_32:
case R_386_TLS_GD:
case R_386_TLS_GD_32:
case R_386_TLS_GOTIE:
case R_386_TLS_TPOFF:
case R_386_TLS_TPOFF32:
return SignExtend64<32>(read32le(buf));
case R_386_NONE:
case R_386_JUMP_SLOT:
// These relocations are defined as not having an implicit addend.
return 0;
default:
internalLinkerError(getErrorLocation(buf),
"cannot read addend for relocation " + toString(type));
return 0;
}
}

View File

@ -20,6 +20,7 @@
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/GlobPattern.h"
#include "llvm/Support/PrettyStackTrace.h"
#include <atomic>
#include <vector>
@ -147,6 +148,7 @@ struct Configuration {
bool bsymbolicFunctions = false;
bool callGraphProfileSort;
bool checkSections;
bool checkDynamicRelocs;
bool compressDebugSections;
bool cref;
std::vector<std::pair<llvm::GlobPattern, uint64_t>> deadRelocInNonAlloc;
@ -356,6 +358,12 @@ static inline void errorOrWarn(const Twine &msg) {
else
warn(msg);
}
static inline void internalLinkerError(StringRef loc, const Twine &msg) {
errorOrWarn(loc + "internal linker error: " + msg + "\n" +
llvm::getBugReportMsg());
}
} // namespace elf
} // namespace lld

View File

@ -1395,7 +1395,18 @@ static void setConfigs(opt::InputArgList &args) {
config->writeAddends = args.hasFlag(OPT_apply_dynamic_relocs,
OPT_no_apply_dynamic_relocs, false) ||
!config->isRela;
// Validation of dynamic relocation addends is on by default for assertions
// builds (for supported targets) and disabled otherwise. Ideally we would
// enable the debug checks for all targets, but currently not all targets
// have support for reading Elf_Rel addends, so we only enable for a subset.
#ifndef NDEBUG
bool checkDynamicRelocsDefault = m == EM_ARM || m == EM_386 || m == EM_MIPS;
#else
bool checkDynamicRelocsDefault = false;
#endif
config->checkDynamicRelocs =
args.hasFlag(OPT_check_dynamic_relocations,
OPT_no_check_dynamic_relocations, checkDynamicRelocsDefault);
config->tocOptimize =
args.hasFlag(OPT_toc_optimize, OPT_no_toc_optimize, m == EM_PPC64);
config->pcRelOptimize =

View File

@ -710,3 +710,9 @@ def: F<"Qy">;
defm mips_got_size:
Eq<"mips-got-size", "Max size of a single MIPS GOT. 0x10000 by default.">,
Flags<[HelpHidden]>;
// Hidden option used to opt-in to additional output checks.
defm check_dynamic_relocations: BB<"check-dynamic-relocations",
"Perform additional validation of the written dynamic relocations",
"Do not perform additional validation of the written dynamic relocations">,
Flags<[HelpHidden]>;

View File

@ -539,6 +539,41 @@ std::array<uint8_t, 4> OutputSection::getFiller() {
return {0, 0, 0, 0};
}
void OutputSection::checkDynRelAddends(const uint8_t *bufStart) {
assert(config->writeAddends && config->checkDynamicRelocs);
assert(type == SHT_REL || type == SHT_RELA);
std::vector<InputSection *> sections = getInputSections(this);
parallelForEachN(0, sections.size(), [&](size_t i) {
// When linking with -r or --emit-relocs we might also call this function
// for input .rel[a].<sec> sections which we simply pass through to the
// output. We skip over those and only look at the synthetic relocation
// sections created during linking.
const auto *sec = dyn_cast<RelocationBaseSection>(sections[i]);
if (!sec)
return;
for (const DynamicReloc &rel : sec->relocs) {
int64_t addend = rel.computeAddend();
const OutputSection *relOsec = rel.inputSec->getOutputSection();
assert(relOsec != nullptr && "missing output section for relocation");
const uint8_t *relocTarget =
bufStart + relOsec->offset + rel.inputSec->getOffset(rel.offsetInSec);
// For SHT_NOBITS the written addend is always zero.
int64_t writtenAddend =
relOsec->type == SHT_NOBITS
? 0
: target->getImplicitAddend(relocTarget, rel.type);
if (addend != writtenAddend)
internalLinkerError(
getErrorLocation(relocTarget),
"wrote incorrect addend value 0x" + utohexstr(writtenAddend) +
" instead of 0x" + utohexstr(addend) +
" for dynamic relocation " + toString(rel.type) +
" at offset 0x" + utohexstr(rel.getOffset()) +
(rel.sym ? " against symbol " + toString(*rel.sym) : ""));
}
});
}
template void OutputSection::writeHeaderTo<ELF32LE>(ELF32LE::Shdr *Shdr);
template void OutputSection::writeHeaderTo<ELF32BE>(ELF32BE::Shdr *Shdr);
template void OutputSection::writeHeaderTo<ELF64LE>(ELF64LE::Shdr *Shdr);

View File

@ -102,6 +102,8 @@ public:
void finalize();
template <class ELFT> void writeTo(uint8_t *buf);
// Check that the addends for dynamic relocations were written correctly.
void checkDynRelAddends(const uint8_t *bufStart);
template <class ELFT> void maybeCompress();
void sort(llvm::function_ref<int(InputSectionBase *s)> order);

View File

@ -538,6 +538,11 @@ public:
size_t getSize() const override { return relocs.size() * this->entsize; }
size_t getRelativeRelocCount() const { return numRelativeRelocs; }
void finalizeContents() override;
static bool classof(const SectionBase *d) {
return SyntheticSection::classof(d) &&
(d->type == llvm::ELF::SHT_RELA || d->type == llvm::ELF::SHT_REL ||
d->type == llvm::ELF::SHT_RELR);
}
int32_t dynamicTag, sizeDynamicTag;
std::vector<DynamicReloc> relocs;

View File

@ -130,6 +130,8 @@ ErrorPlace elf::getErrorPlace(const uint8_t *loc) {
TargetInfo::~TargetInfo() {}
int64_t TargetInfo::getImplicitAddend(const uint8_t *buf, RelType type) const {
internalLinkerError(getErrorLocation(buf),
"cannot read addend for relocation " + toString(type));
return 0;
}

View File

@ -2971,6 +2971,13 @@ template <class ELFT> void Writer<ELFT>::writeSections() {
for (OutputSection *sec : outputSections)
if (sec->type != SHT_REL && sec->type != SHT_RELA)
sec->writeTo<ELFT>(Out::bufferStart + sec->offset);
// Finally, check that all dynamic relocation addends were written correctly.
if (config->checkDynamicRelocs && config->writeAddends) {
for (OutputSection *sec : outputSections)
if (sec->type == SHT_REL || sec->type == SHT_RELA)
sec->checkDynRelAddends(Out::bufferStart);
}
}
// Computes a hash value of Data using a given hash function.

View File

@ -1,4 +1,6 @@
# REQUIRES: x86
# Needs https://reviews.llvm.org/D101451 to read the implicit addends.
# XFAIL: *
# RUN: yaml2obj %s -o %t.o
# RUN: not ld.lld %t.o -o /dev/null 2>&1 | FileCheck --check-prefix=ERR %s

View File

@ -4,6 +4,9 @@
## want to use this feature, which is not restricted by ELF gABI.
## GNU ld supports this as well.
# Needs https://reviews.llvm.org/D101451 to read the implicit addends.
# XFAIL: *
# RUN: yaml2obj %s -DTYPE=SHT_RELA -o %t1.o
# RUN: ld.lld -shared %t1.o -o %t1
# RUN: llvm-readelf --relocs %t1 | FileCheck %s