Skip to content

Commit

Permalink
Do not emit incorrect relocations for mergeable sections with --reloc…
Browse files Browse the repository at this point in the history
…atable

Fixes #1265
  • Loading branch information
rui314 committed May 24, 2024
1 parent 02b439a commit 08b0a16
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 30 deletions.
22 changes: 22 additions & 0 deletions elf/arch-sh4.cc
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,28 @@ i64 get_addend(u8 *loc, const ElfRel<E> &rel) {
}
}

template <>
void write_addend(u8 *loc, i64 val, const ElfRel<E> &rel) {
switch (rel.r_type) {
case R_SH_DIR32:
case R_SH_REL32:
case R_SH_TLS_GD_32:
case R_SH_TLS_LD_32:
case R_SH_TLS_LDO_32:
case R_SH_TLS_IE_32:
case R_SH_TLS_LE_32:
case R_SH_TLS_DTPMOD32:
case R_SH_TLS_DTPOFF32:
case R_SH_TLS_TPOFF32:
case R_SH_GOT32:
case R_SH_PLT32:
case R_SH_GOTOFF:
case R_SH_GOTPC:
case R_SH_GOTPLT32:
*(ul32 *)loc = val;
}
}

template <>
void write_plt_header(Context<E> &ctx, u8 *buf) {
if (ctx.arg.pic) {
Expand Down
7 changes: 6 additions & 1 deletion elf/cmdline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,11 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
if constexpr (is_sparc<E>)
ctx.arg.apply_dynamic_relocs = false;

// For some reason, SH4 always stores relocation addends to
// relocated places even though its RELA.
if constexpr (is_sh4<E>)
ctx.arg.apply_dynamic_relocs = true;

auto read_arg = [&](std::string name) {
for (const std::string &opt : add_dashes(name)) {
if (args[0] == opt) {
Expand Down Expand Up @@ -1343,7 +1348,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
Fatal(ctx) << "-auxiliary may not be used without -shared";
}

if constexpr (!E::is_rela)
if constexpr (!E::is_rela || is_sh4<E>)
if (!ctx.arg.apply_dynamic_relocs)
Fatal(ctx) << "--no-apply-dynamic-relocs may not be used on "
<< E::target_name;
Expand Down
16 changes: 16 additions & 0 deletions elf/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -1827,6 +1827,22 @@ struct ElfRel<SPARC64> {
ib64 r_addend;
};

template <>
struct ElfRel<SH4> {
ElfRel() = default;

// Addend is ignored except for base relocations because even though
// SH4 is RELA, r_addend is ignored in most cases and works as if it
// were REL.
ElfRel(u64 offset, u32 type, u32 sym, i64 addend)
: r_offset(offset), r_type(type), r_sym(sym), r_addend(sym ? 0 : addend) {}

ul32 r_offset;
u8 r_type;
ul24 r_sym;
il32 r_addend;
};

//
// Machine descriptions
//
Expand Down
9 changes: 4 additions & 5 deletions elf/mold.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,11 @@ class InputSection {
std::string_view get_func_name(Context<E> &ctx, i64 offset) const;
bool is_relr_reloc(Context<E> &ctx, const ElfRel<E> &rel) const;
bool is_killed_by_icf() const;

bool record_undef_error(Context<E> &ctx, const ElfRel<E> &rel);

std::pair<SectionFragment<E> *, i64>
get_fragment(Context<E> &ctx, const ElfRel<E> &rel);

ObjectFile<E> &file;
OutputSection<E> *output_section = nullptr;
i64 sh_size = -1;
Expand Down Expand Up @@ -321,9 +323,6 @@ class InputSection {

void copy_contents_riscv(Context<E> &ctx, u8 *buf);

std::pair<SectionFragment<E> *, i64>
get_fragment(Context<E> &ctx, const ElfRel<E> &rel);

u64 get_thunk_addr(i64 idx);

std::optional<u64> get_tombstone(Symbol<E> &sym, SectionFragment<E> *frag);
Expand Down Expand Up @@ -2306,7 +2305,7 @@ i64 get_addend(InputSection<E> &isec, const ElfRel<E> &rel) {
template <typename E>
void write_addend(u8 *loc, i64 val, const ElfRel<E> &rel);

template <typename E> requires E::is_rela
template <typename E> requires E::is_rela && (!is_sh4<E>)
void write_addend(u8 *loc, i64 val, const ElfRel<E> &rel) {}

template <typename E>
Expand Down
54 changes: 31 additions & 23 deletions elf/output-chunks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2810,34 +2810,42 @@ void RelocSection<E>::update_shdr(Context<E> &ctx) {

template <typename E>
void RelocSection<E>::copy_buf(Context<E> &ctx) {
auto write = [&](ElfRel<E> &out, InputSection<E> &isec, const ElfRel<E> &rel) {
auto get_symidx_addend = [&](InputSection<E> &isec, const ElfRel<E> &rel)
-> std::pair<i64, i64> {
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
i64 symidx = 0;
i64 addend = 0;

if (!(isec.shdr().sh_flags & SHF_ALLOC)) {
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = isec.get_fragment(ctx, rel);
if (frag)
return {frag->output_section.shndx, frag->offset + frag_addend};
}

if (sym.esym().st_type == STT_SECTION) {
if (SectionFragment<E> *frag = sym.get_frag()) {
symidx = frag->output_section.shndx;
addend = frag->offset + sym.value + get_addend(isec, rel);
} else {
InputSection<E> *target = sym.get_input_section();

if (OutputSection<E> *osec = target->output_section) {
symidx = osec->shndx;
addend = get_addend(isec, rel) + target->offset;
} else if (isec.name() == ".eh_frame") {
symidx = ctx.eh_frame->shndx;
addend = get_addend(isec, rel);
} else {
// This is usually a dead debug section referring a
// COMDAT-eliminated section.
}
}
} else if (sym.write_to_symtab) {
symidx = sym.get_output_sym_idx(ctx);
addend = get_addend(isec, rel);
if (SectionFragment<E> *frag = sym.get_frag())
return {frag->output_section.shndx,
frag->offset + sym.value + get_addend(isec, rel)};

InputSection<E> *isec2 = sym.get_input_section();
if (OutputSection<E> *osec = isec2->output_section)
return {osec->shndx, get_addend(isec, rel) + isec2->offset};

// This is usually a dead debug section referring to a
// COMDAT-eliminated section.
return {0, 0};
}

if (sym.write_to_symtab)
return {sym.get_output_sym_idx(ctx), get_addend(isec, rel)};
return {0, 0};
};

auto write = [&](ElfRel<E> &out, InputSection<E> &isec, const ElfRel<E> &rel) {
i64 symidx;
i64 addend;
std::tie(symidx, addend) = get_symidx_addend(isec, rel);

if constexpr (is_alpha<E>)
if (rel.r_type == R_ALPHA_GPDISP || rel.r_type == R_ALPHA_LITUSE)
addend = rel.r_addend;
Expand Down
5 changes: 4 additions & 1 deletion test/elf/relocatable-debug-info.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@ EOF
./mold --relocatable -o $t/c.o $t/a.o $t/b.o

$CC -B. -o $t/exe $t/c.o
$QEMU $t/exe
$QEMU $t/exe | grep -q 'Hello world'

$OBJDUMP --dwarf=info $t/c.o > /dev/null 2> $t/log
! grep -q Warning $t/log || false

0 comments on commit 08b0a16

Please sign in to comment.