diff --git a/elf/arch-sh4.cc b/elf/arch-sh4.cc index 46d611282f..bf3070488f 100644 --- a/elf/arch-sh4.cc +++ b/elf/arch-sh4.cc @@ -90,6 +90,28 @@ i64 get_addend(u8 *loc, const ElfRel &rel) { } } +template <> +void write_addend(u8 *loc, i64 val, const ElfRel &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 &ctx, u8 *buf) { if (ctx.arg.pic) { diff --git a/elf/cmdline.cc b/elf/cmdline.cc index 9c49301adb..e5f2a4ceed 100644 --- a/elf/cmdline.cc +++ b/elf/cmdline.cc @@ -546,6 +546,11 @@ std::vector parse_nonpositional_args(Context &ctx) { if constexpr (is_sparc) 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) + ctx.arg.apply_dynamic_relocs = true; + auto read_arg = [&](std::string name) { for (const std::string &opt : add_dashes(name)) { if (args[0] == opt) { @@ -1343,7 +1348,7 @@ std::vector parse_nonpositional_args(Context &ctx) { Fatal(ctx) << "-auxiliary may not be used without -shared"; } - if constexpr (!E::is_rela) + if constexpr (!E::is_rela || is_sh4) if (!ctx.arg.apply_dynamic_relocs) Fatal(ctx) << "--no-apply-dynamic-relocs may not be used on " << E::target_name; diff --git a/elf/elf.h b/elf/elf.h index 08c689cb6f..c58fea0576 100644 --- a/elf/elf.h +++ b/elf/elf.h @@ -1827,6 +1827,22 @@ struct ElfRel { ib64 r_addend; }; +template <> +struct ElfRel { + 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 // diff --git a/elf/mold.h b/elf/mold.h index 382a329554..92de242920 100644 --- a/elf/mold.h +++ b/elf/mold.h @@ -262,9 +262,11 @@ class InputSection { std::string_view get_func_name(Context &ctx, i64 offset) const; bool is_relr_reloc(Context &ctx, const ElfRel &rel) const; bool is_killed_by_icf() const; - bool record_undef_error(Context &ctx, const ElfRel &rel); + std::pair *, i64> + get_fragment(Context &ctx, const ElfRel &rel); + ObjectFile &file; OutputSection *output_section = nullptr; i64 sh_size = -1; @@ -321,9 +323,6 @@ class InputSection { void copy_contents_riscv(Context &ctx, u8 *buf); - std::pair *, i64> - get_fragment(Context &ctx, const ElfRel &rel); - u64 get_thunk_addr(i64 idx); std::optional get_tombstone(Symbol &sym, SectionFragment *frag); @@ -2306,7 +2305,7 @@ i64 get_addend(InputSection &isec, const ElfRel &rel) { template void write_addend(u8 *loc, i64 val, const ElfRel &rel); -template requires E::is_rela +template requires E::is_rela && (!is_sh4) void write_addend(u8 *loc, i64 val, const ElfRel &rel) {} template diff --git a/elf/output-chunks.cc b/elf/output-chunks.cc index 7e2c824af9..4337653dab 100644 --- a/elf/output-chunks.cc +++ b/elf/output-chunks.cc @@ -2810,34 +2810,42 @@ void RelocSection::update_shdr(Context &ctx) { template void RelocSection::copy_buf(Context &ctx) { - auto write = [&](ElfRel &out, InputSection &isec, const ElfRel &rel) { + auto get_symidx_addend = [&](InputSection &isec, const ElfRel &rel) + -> std::pair { Symbol &sym = *isec.file.symbols[rel.r_sym]; - i64 symidx = 0; - i64 addend = 0; + + if (!(isec.shdr().sh_flags & SHF_ALLOC)) { + SectionFragment *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 *frag = sym.get_frag()) { - symidx = frag->output_section.shndx; - addend = frag->offset + sym.value + get_addend(isec, rel); - } else { - InputSection *target = sym.get_input_section(); - - if (OutputSection *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 *frag = sym.get_frag()) + return {frag->output_section.shndx, + frag->offset + sym.value + get_addend(isec, rel)}; + + InputSection *isec2 = sym.get_input_section(); + if (OutputSection *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 &out, InputSection &isec, const ElfRel &rel) { + i64 symidx; + i64 addend; + std::tie(symidx, addend) = get_symidx_addend(isec, rel); + if constexpr (is_alpha) if (rel.r_type == R_ALPHA_GPDISP || rel.r_type == R_ALPHA_LITUSE) addend = rel.r_addend; diff --git a/test/elf/relocatable-debug-info.sh b/test/elf/relocatable-debug-info.sh index 71a0dd4c27..c999355942 100755 --- a/test/elf/relocatable-debug-info.sh +++ b/test/elf/relocatable-debug-info.sh @@ -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