LLVM Bugzilla is read-only and represents the historical archive of all LLVM issues filled before November 26, 2021. Use github to submit LLVM bugs

Bug 36634 - __rela_iplt_{start,end} get bogus section index if .rela.iplt section is discarded
Summary: __rela_iplt_{start,end} get bogus section index if .rela.iplt section is disc...
Status: RESOLVED FIXED
Alias: None
Product: lld
Classification: Unclassified
Component: ELF (show other bugs)
Version: unspecified
Hardware: All All
: P normal
Assignee: Unassigned LLVM Bugs
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2018-03-07 10:48 PST by Rui Ueyama
Modified: 2019-04-21 05:47 PDT (History)
5 users (show)

See Also:
Fixed By Commit(s):


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Rui Ueyama 2018-03-07 10:48:05 PST
For static linking, we synthesize __rela_iplt_start and __rela_iplt_end symbols which point to the beginning and ending of .rela.iplt. If .rela.iplt is discarded after that, then the symbols get bogus section index 0xffff. If that happens, no one really uses the symbols, so the symbols' section value doesn't matter, but it should at least be a sane value (0xffff is a reserved value).
Comment 1 emaste 2018-11-27 13:11:16 PST
We've been investigating an issue with lld 6.0 in upcoming FreeBSD 12.0 which turns out to be this.

https://bugs.freebsd.org/233397
Comment 2 emaste 2018-11-27 13:36:26 PST
From the FreeBSD PR, ld.bfd and gold pick different arbitrary sections for Ndx for these symbols:

Linking with gold 1.15 (binutils 2.30):

nuc% readelf -s a.out | grep iplt
  1033: 000000000048b068     0 NOTYPE  LOCAL  HIDDEN     8 __rela_iplt_start
  1034: 000000000048b068     0 NOTYPE  LOCAL  HIDDEN     8 __rela_iplt_end

  [ 8] .tdata            PROGBITS         000000000048b070  0008a070
       0000000000001800  0000000000000000 WAT       0     0     16

with ld.bfd 2.30

nuc% readelf -s a.out | grep iplt
  1032: 00000000004001c0     0 NOTYPE  LOCAL  DEFAULT    1 __rela_iplt_end
  1034: 00000000004001c0     0 NOTYPE  LOCAL  DEFAULT    1 __rela_iplt_start

  [ 1] .note.tag         NOTE             0000000000400190  00000190
       0000000000000030  0000000000000000   A       0     0     4
Comment 3 Fangrui Song 2018-11-27 18:26:08 PST
This is tricky.

I have tried several approaches:

* Leave __rela_iplt_start undefined if .rela.plt (IPLT) is empty. This should work as the two implementations I know use weak references:

// freebsd/src/lib/csu/common/ignore_init.c
  extern const Elf_Rela __rela_iplt_start[] __weak_symbol __hidden;
  extern const Elf_Rela __rela_iplt_end[] __weak_symbol __hidden;
  
  #include "reloc.c"
  
  static void
  process_irelocs(void)
  {
  	const Elf_Rela *r;
  
  	for (r = &__rela_iplt_start[0]; r < &__rela_iplt_end[0]; r++)
  		crt1_handle_rela(r);
  }

// glibc/csu/libc-start.c
  static void
  apply_irel (void)
  {
  # ifdef IREL
    /* We use weak references for these so that we'll still work with a linker
       that doesn't define them.  Such a linker doesn't support IFUNC at all
       and so uses won't work, but a statically-linked program that doesn't
       use any IFUNC symbols won't have a problem.  */
    extern const IREL_T IPLT_START[] __attribute__ ((weak));
    extern const IREL_T IPLT_END[] __attribute__ ((weak));
    for (const IREL_T *ipltent = IPLT_START; ipltent < IPLT_END; ++ipltent)
      IREL (ipltent);
  # endif
  }
  #endif

  I've seen a weird glibc R_X86_64_GOTPCRELX relocation error.
  Using 0 is not as good as using an arbitrary section as the distance between the section and the relocation site is smaller.

  In lld/ELF/Writer.cpp finalizeSections, there is some nuance when the following processes are reordered:
  
  * addRelIpltSymbols  
  * forEachRelSec(scanRelocations<ELFT>);
  * In.Iplt->addSymbols();
  * ...

* https://reviews.llvm.org/D54985 [ELF] Keep empty In.RelaIplt so that __rela_iplt_{start,end} have valid st_shndx

This requires a change in llvm-readobj as otherwise it would complain .rela.plt is not in any segment

--- c/tools/llvm-readobj/ELFDumper.cpp
+++ i/tools/llvm-readobj/ELFDumper.cpp
@@ -1489,7 +1489,8 @@ void ELFDumper<ELFT>::parseDynamicTable(
     const Elf_Phdr &Phdr = **I;
     uint64_t Delta = VAddr - Phdr.p_vaddr;
     if (Delta >= Phdr.p_filesz)
-      report_fatal_error("Virtual address is not in any segment");
+      return nullptr;

After that, `ninja check-lld-elf` reports:

  Expected Passes    : 1113
  Unsupported Tests  : 8
  Unexpected Failures: 271

This place also needs patching:

    PltRelSec = findNotEmptySectionByAddress(Obj, *DtJmpRel);
    if (!PltRelSec)
      report_fatal_error("There is no not empty RELPLT section at 0x" +
                         Twine::utohexstr(*DtJmpRel));
Comment 4 emaste 2018-11-28 11:23:32 PST
The first option (leaving them undefined in the absence of ifuncs) is my preference.
Comment 5 Rui Ueyama 2018-11-28 11:29:31 PST
Maybe we can scan all symbols to make sure that there is at least one ifunc symbol before adding __rela_iplt_{start,end} symbols.
Comment 6 Fangrui Song 2019-04-21 05:47:41 PDT
Fixed by https://reviews.llvm.org/D56623