https://sourceware.org/bugzilla/show_bug.cgi?id=34988

            Bug ID: 34988
           Summary: ld internal error in elf_x86_64_relocate_section:
                    local IFUNC referenced only from .debug_info (gcc -g
                    -O2)
           Product: binutils
           Version: 2.47
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: ld
          Assignee: unassigned at sourceware dot org
          Reporter: harshit7kr at gmail dot com
  Target Milestone: ---
              Host: x86_64-pc-linux-gnu
            Target: x86_64-pc-linux-gnu
             Build: x86_64-pc-linux-gnu

Created attachment 17263
  --> https://sourceware.org/bugzilla/attachment.cgi?id=17263&action=edit
reproducer, compile with gcc -g -O2

ld aborts with "BFD internal error" when the only reference to a local (static)
STT_GNU_IFUNC symbol is in .debug_info. GCC produces such an object from
ordinary C at -O2 with -g.

Reproducer (attached as local-ifunc-callsite.c):

static int impl(void){return 1;} static int (*resolve(void))(void){return
impl;} static int f(void) attribute((ifunc("resolve"))); attribute((noinline))
static int call(int (*p)(void), int x){ return x; } int g(int x){ return
call(f, x); }

$ gcc -g -O2 -fPIC -shared local-ifunc-callsite.c -o libt.so /usr/bin/ld: BFD
(GNU Binutils) 2.47 internal error, aborting at .../bfd/elf64-x86-64.c:3271 in
elf_x86_64_relocate_section Please report this bug. collect2: error: ld
returned 1 exit status

The same happens for a non-PIC link (gcc -g -O2 -c; ld -e g t.o), and with
current master (2.47.50, elf64-x86-64.c:3261). Without -g it links. Tested with
GCC 16.2.1 on x86_64-pc-linux-gnu; -O3 and -Os behave the same, -O1 and -Og do
not trigger it (the code still refers to f).

Why: GCC drops call()'s unused argument, so .text no longer refers to f, but
the DWARF still does (DW_OP_addr f in a DW_TAG_call_site_parameter's
DW_AT_call_value and in a DW_TAG_formal_parameter's DW_AT_location). The only
relocations against f are two R_X86_64_64 in .rela.debug_info:

$ readelf -rW t.o | grep ' f' ... R_X86_64_64 f() f + 0 (x2, both in
.rela.debug_info) $ readelf -sW t.o | grep ' f$' 5: 0000000000000010 8 IFUNC
LOCAL DEFAULT 1 f

The relocation scan (_bfd_elf_link_iterate_on_relocs) skips non-SEC_ALLOC
sections, so the hash entry it creates for a local IFUNC is never created, and
elf_x86_64_relocate_section aborts when _bfd_elf_x86_get_local_sym_hash returns
NULL. A global IFUNC referenced the same way links fine: relocate_section
treats it as STT_FUNC in a non-alloc SHT_NOTE section and skips the relocation
in a debugging section.

Other backends: with hand-written assembly (a local IFUNC referenced only by a
.quad/.long in .debug_info or in a non-alloc SHT_NOTE section), master also
aborts in i386, AArch64, RISC-V, SPARC and LoongArch, and the s390 backends
fail with only "final link failed" (or use an unallocated PLT slot offset when
another local IFUNC in the object has a slot). The C reproducer above was only
tested on x86-64.

Patches to follow on [email protected].

-- 
You are receiving this mail because:
You are on the CC list for the bug.

Reply via email to