Bug 17699

Summary: incorrect R_386_TLS_DTPMOD32 relocation
Product: binutils Reporter: Timo Teräs <timo.teras>
Component: goldAssignee: Cary Coutant <ccoutant>
Status: RESOLVED INVALID    
Severity: normal CC: bugdal, ian
Priority: P2    
Version: unspecified   
Target Milestone: ---   
Host: Target:
Build: Last reconfirmed:
Attachments: object that results in bad reloc in elf

Description Timo Teräs 2014-12-11 17:19:11 UTC
gold from binutils 2.24 with few CVE patch applied, and compiling gcc 4.9.2's libstdc++ produce incorrect R_386_TLS_DPT_MOD32 relocation on x86 (on x86_64 and arm the equivalent relocation is produced correctly):

on x86:

The relevant parts of "readelf -a /usr/lib/libstdc++.so.6":

Relocation section '.rel.dyn' at offset 0x4811c contains 2534 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
...
000d30f8  00000123 R_386_TLS_DTPMOD3 000cfe80   .tbss
...

Symbol table '.dynsym' contains 4207 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 000cfe80     0 SECTION LOCAL  DEFAULT   17 

...

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
...
  [17] .tbss             NOBITS          000cfe80 0cee80 000010 00 WAT  0   0  4


And for reference the equivalent relocation on x86_64 is:
0000000e1240  000000000010 R_X86_64_DTPMOD64                    0

which is correct.

This makes the produced file unusable (at least with musl c-library). After consulting with Rich Felker, the conclusion was that the relocation on i386 should be similar to what it is on x86_64, against 0 not indirect via the section symbol.

Ignoring the 'SECTION' symbol and assuming at as self-reference on musl dynamic linker is a workaround. But generating such a relocation sounds like a gold bug.
Comment 1 Rich Felker 2014-12-11 17:31:00 UTC
Some follow-up regarding my (Rich Felker) reasoning for why the reloc gold is producing is wrong:

1. The existing convention for producing a DTPMOD reloc for "this DSO itself" is to omit the symbol reference. This is what gold does on other archs and what BFD ld has always done.

2. Semantically, a symbol pointing to a section does not even make sense as the target for a TLS relocation. The targeted symbol for such a reloc must be TLS type (STT_TLS). Simply the fact that the targeted section happens to be a TLS-related section does not make it a TLS symbol. If anything, by a principle of least surprise, such a symbol should resolve to the TLS image (but for BSS that doesn't even exist) rather than the resulting thread-local storage.

3. In my opinion, STT_SECTION symbols are not valid in the dynamic symbol table at all. Sections are not meaningful at load/execution time, and it's arguably even valid to strip the section headers if you really want to.

I'm not clear on why glibc's dynamic linker accepts this relocation; probably it's simply a consequence of an implementation detail of the order in which operations are performed. But musl's dynamic linker is not accepting it, and I don't see any logically consistent way to accept it short of hacks to just ignore the referenced symbol and treat it as a null symbol reference, so I'd much rather see this fixed in gold than work on hacks to work around it.
Comment 2 Cary Coutant 2014-12-11 18:25:17 UTC
Please attach the .o file containing the original relocation.
Comment 3 Timo Teräs 2014-12-11 19:27:38 UTC
Created attachment 8004 [details]
object that results in bad reloc in elf

Pinpointed to originate from ./gcc-4.9.2/libstdc++-v3/libsupc++/eh_globals.cc. Object file attached.
Comment 4 Timo Teräs 2014-12-11 19:51:51 UTC
The minimal way to reproduce this seems to be:

-- test.cc
struct foo { int a; };

namespace
{
  struct foo *get_global() throw()
  {
    static __thread struct foo global;
    return &global;
  }
} // anonymous namespace
--

And compile .so with:
g++ -fPIC test.cc -shared -o test.so

Without -fPIC it looks better.
Comment 5 Timo Teräs 2014-12-11 20:08:26 UTC
Even more minimal test case in C:
 int bar() { static __thread int foo; return foo; }

Notable that this does not cause the same issue:
 __thread int __attribute__((__visibility__("hidden"))) foo; int *bar() { return &foo; }
Comment 6 Cary Coutant 2016-11-23 02:59:31 UTC
I can't find any support for the claims made in Comment #1.

In all ABIs using the IA-64 TLS model, the DTPMOD relocation is defined as resolving to the index of the load module where the *referenced symbol* is defined. Thus, the r_sym field of the relocation is relevant, and is used by the dynamic linker to determine what load module index to insert into the GOT entry. In the case of a reference to a local symbol, the local symbol can be (and usually is) collapsed to a section symbol, and the dynamic loader resolves the relocation to the index of the current load module (which is where the local symbol is defined).

It appears that Gnu ld has adopted a convention where a 0 symbol index may also be used to refer to the current load module, but that convention is not described in the ABI, as far as I can determine, nor does the dynamic linker depend on it.

In my opinion, the musl dynamic linker was written to conform to one specific implementation (viz, the Gnu linker), and not to the specification.

Section symbols in the dynamic symbol table are not only perfectly valid, but are used frequently -- whether for TLS or not. They do not depend on the section table any more than any other symbol, since the value of a symbol in an executable is always a proper virtual address.
Comment 7 Rich Felker 2016-11-23 04:22:45 UTC
Can you cite any documentation that a non-STT_TLS symbol reference is permissible in a DTPMOD relocation?
Comment 8 Cary Coutant 2016-11-23 19:23:38 UTC
> Can you cite any documentation that a non-STT_TLS symbol reference is
> permissible in a DTPMOD relocation?

The section symbol for an SHF_TLS section is effectively a TLS symbol, and must be treated as such.

See, for example, PR 16773, where a compiler-generated relocation references the .tdata section symbol rather than a local symbol in the section.