1
1
mirror of https://github.com/rui314/mold.git synced 2024-10-04 08:37:28 +03:00

[ELF] Write tombstone values to dead debug info records

This is an attempt to fix https://github.com/rui314/mold/issues/357.
This commit is contained in:
Rui Ueyama 2022-04-09 09:10:47 +08:00
parent 4df5d6497a
commit e448e0eb2b
7 changed files with 113 additions and 9 deletions

View File

@ -339,10 +339,16 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
switch (rel.r_type) {
case R_ARM_ABS32:
*(u32 *)loc = S + A;
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
else
*(u32 *)loc = S + A;
break;
case R_ARM_TLS_LDO32:
*(u32 *)loc = S + A - ctx.tls_begin;
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
else
*(u32 *)loc = S + A - ctx.tls_begin;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "

View File

@ -369,7 +369,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
switch (rel.r_type) {
case R_AARCH64_ABS64:
*(u64 *)loc = S + A;
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
else
*(u64 *)loc = S + A;
continue;
case R_AARCH64_ABS32:
*(u32 *)loc = S + A;

View File

@ -384,7 +384,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
write16(S + A);
continue;
case R_386_32:
*(u32 *)loc = S + A;
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
else
*(u32 *)loc = S + A;
continue;
case R_386_PC8:
write8s(S + A);
@ -402,7 +405,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
*(u32 *)loc = S + A - GOT;
continue;
case R_386_TLS_LDO_32:
*(u32 *)loc = S + A - ctx.tls_begin;
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
else
*(u32 *)loc = S + A - ctx.tls_begin;
continue;
case R_386_SIZE32:
*(u32 *)loc = sym.esym().st_size + A;

View File

@ -434,7 +434,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
*(u32 *)loc = S + A;
break;
case R_RISCV_64:
*(u64 *)loc = S + A;
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
else
*(u64 *)loc = S + A;
break;
case R_RISCV_ADD8:
*loc += S + A;

View File

@ -608,13 +608,22 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
write32s(S + A);
break;
case R_X86_64_64:
*(u64 *)loc = S + A;
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
else
*(u64 *)loc = S + A;
break;
case R_X86_64_DTPOFF32:
write32s(S + A - ctx.tls_begin);
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
else
write32s(S + A - ctx.tls_begin);
break;
case R_X86_64_DTPOFF64:
*(u64 *)loc = S + A - ctx.tls_begin;
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
else
*(u64 *)loc = S + A - ctx.tls_begin;
break;
case R_X86_64_SIZE32:
write32(sym.esym().st_size + A);

View File

@ -322,6 +322,7 @@ private:
std::pair<SectionFragment<E> *, i64>
get_fragment(Context<E> &ctx, const ElfRel<E> &rel);
std::optional<u64> get_tombstone(Symbol<E> &sym);
bool is_relr_reloc(Context<E> &ctx, const ElfRel<E> &rel);
};
@ -2204,6 +2205,40 @@ InputSection<E>::get_fragment(Context<E> &ctx, const ElfRel<E> &rel) {
return {m->fragments[idx], offset - offsets[idx]};
}
// Input object files may contain duplicate code for inline functions
// and such. Linkers de-duplicate them at link-time. However, linkers
// generaly don't remove debug info for de-duplicated functions because
// doing that requires parsing the entire debug section.
//
// Instead, linkers write "tombstone" values to dead debug info records
// instead of bogus values so that debuggers can skip them.
//
// This function returns a tombstone value for the symbol if the symbol
// refers a dead debug info section.
template <typename E>
inline std::optional<u64> InputSection<E>::get_tombstone(Symbol<E> &sym) {
InputSection<E> *isec = sym.get_input_section();
// Setting a tombstone is a special feature for a dead debug section.
if (!isec || isec->is_alive)
return {};
std::string_view s = name();
if (!s.starts_with(".debug"))
return {};
// If the section was dead due to ICF, we don't want to emit debug
// info for that section but want to set real values to .debug_line so
// that users can set a breakpoint inside a merged section.
if (isec->killed_by_icf && s == ".debug_line")
return {};
// 0 is an invalid value in most debug info sections, so we use it
// as a tombstone value. .debug_loc and .debug_ranges reserve 0 as
// the terminator marker, so we use 1 if that's the case.
return (s == ".debug_loc" || s == ".debug_ranges") ? 1 : 0;
}
template <typename E>
inline bool InputSection<E>::is_relr_reloc(Context<E> &ctx, const ElfRel<E> &rel) {
return ctx.arg.pack_dyn_relocs_relr &&

42
test/elf/dead-debug-sections.sh Executable file
View File

@ -0,0 +1,42 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
cat <<EOF | $CXX -c -o $t/a.o -g -xc++ -
#include <iostream>
struct Foo {
Foo() { std::cout << "Hello world\n"; }
};
Foo x;
EOF
cat <<EOF | $CXX -c -o $t/b.o -g -xc++ -
#include <iostream>
struct Foo {
Foo() { std::cout << "Hello world\n"; }
};
Foo y;
EOF
cat <<EOF | $CXX -o $t/c.o -c -xc++ -g -
int main() {}
EOF
$CXX -B. -o $t/exe $t/a.o $t/b.o $t/c.o -g
$QEMU $t/exe | grep -q 'Hello world'
dwarfdump $t/exe | fgrep -q 'range entry 0x00000001 0x00000001'
echo OK