[PATCH] x86-64: Fix misleading R_X86_64_TPOFF32 error message
Fangrui Song
i@maskray.me
Wed Sep 10 16:57:56 GMT 2025
On Wed, Sep 10, 2025 at 6:45 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> On Tue, Sep 9, 2025 at 1:38 PM H.J. Lu <hjl.tools@gmail.com> wrote:
> >
> > R_X86_64_TPOFF32 relocation of local-exec TLS model can only be used in
> > executable, not in a shared library, even if the source code is compiled
> > with -fPIC. Change the linker error message from
> >
> > relocation R_X86_64_TPOFF32 against symbol `foo' can not be used when making a shared object; recompile with -fPIC
> >
> > to
> >
> > relocation R_X86_64_TPOFF32 against symbol `foo' can not be used when making a shared object; replace local-exec with initial-exec TLS model
> >
> > bfd/
> >
> > PR ld/33408
> > * elf64-x86-64.c (elf_x86_64_need_pic): Suggest "replace
> > local-exec with initial-exec TLS model" for R_X86_64_TPOFF32.
> > (elf_x86_64_scan_relocs): Drop ABI_64_P check for
> > R_X86_64_TPOFF32.
> >
> > ld/
> >
> > PR ld/33408
> > * testsuite/ld-x86-64/tls-le-pic-1-x32.d: New file.
> > * testsuite/ld-x86-64/tls-le-pic-1.d: Likewise.
> > * testsuite/ld-x86-64/tls-le-pic-1.s: Likewise.
> > * testsuite/ld-x86-64/tls-le-pic-2-x32.d: Likewise.
> > * testsuite/ld-x86-64/tls-le-pic-2.d: Likewise.
> > * testsuite/ld-x86-64/tls-le-pic-2.s: Likewise.
> > * testsuite/ld-x86-64/tls-le-pic-3-x32.d: Likewise.
> > * testsuite/ld-x86-64/tls-le-pic-3.d: Likewise.
> > * testsuite/ld-x86-64/tls-le-pic-3.s: Likewise.
> >
> > Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
> > ---
> > bfd/elf64-x86-64.c | 6 ++++--
> > ld/testsuite/ld-x86-64/tls-le-pic-1-x32.d | 4 ++++
> > ld/testsuite/ld-x86-64/tls-le-pic-1.d | 3 +++
> > ld/testsuite/ld-x86-64/tls-le-pic-1.s | 17 +++++++++++++++++
> > ld/testsuite/ld-x86-64/tls-le-pic-2-x32.d | 4 ++++
> > ld/testsuite/ld-x86-64/tls-le-pic-2.d | 3 +++
> > ld/testsuite/ld-x86-64/tls-le-pic-2.s | 10 ++++++++++
> > ld/testsuite/ld-x86-64/tls-le-pic-3-x32.d | 4 ++++
> > ld/testsuite/ld-x86-64/tls-le-pic-3.d | 3 +++
> > ld/testsuite/ld-x86-64/tls-le-pic-3.s | 16 ++++++++++++++++
> > ld/testsuite/ld-x86-64/x86-64.exp | 6 ++++++
> > 11 files changed, 74 insertions(+), 2 deletions(-)
> > create mode 100644 ld/testsuite/ld-x86-64/tls-le-pic-1-x32.d
> > create mode 100644 ld/testsuite/ld-x86-64/tls-le-pic-1.d
> > create mode 100644 ld/testsuite/ld-x86-64/tls-le-pic-1.s
> > create mode 100644 ld/testsuite/ld-x86-64/tls-le-pic-2-x32.d
> > create mode 100644 ld/testsuite/ld-x86-64/tls-le-pic-2.d
> > create mode 100644 ld/testsuite/ld-x86-64/tls-le-pic-2.s
> > create mode 100644 ld/testsuite/ld-x86-64/tls-le-pic-3-x32.d
> > create mode 100644 ld/testsuite/ld-x86-64/tls-le-pic-3.d
> > create mode 100644 ld/testsuite/ld-x86-64/tls-le-pic-3.s
> >
> > diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
> > index 00586455203..ad99cf6754e 100644
> > --- a/bfd/elf64-x86-64.c
> > +++ b/bfd/elf64-x86-64.c
> > @@ -1717,7 +1717,9 @@ elf_x86_64_need_pic (struct bfd_link_info *info,
> > {
> > object = _("a shared object");
> > if (!pic)
> > - pic = _("; recompile with -fPIC");
> > + pic = (howto->type == R_X86_64_TPOFF32
> > + ? _("; replace local-exec with initial-exec TLS model")
> > + : _("; recompile with -fPIC"));
> > }
> > else
> > {
> > @@ -2684,7 +2686,7 @@ elf_x86_64_scan_relocs (bfd *abfd, struct bfd_link_info *info,
> > goto create_got;
> >
> > case R_X86_64_TPOFF32:
> > - if (!bfd_link_executable (info) && ABI_64_P (abfd))
> > + if (!bfd_link_executable (info))
> > {
> > elf_x86_64_need_pic (info, abfd, sec, h, symtab_hdr, isym,
> > &x86_64_elf_howto_table[r_type]);
> > diff --git a/ld/testsuite/ld-x86-64/tls-le-pic-1-x32.d b/ld/testsuite/ld-x86-64/tls-le-pic-1-x32.d
> > new file mode 100644
> > index 00000000000..b0824c4ae10
> > --- /dev/null
> > +++ b/ld/testsuite/ld-x86-64/tls-le-pic-1-x32.d
> > @@ -0,0 +1,4 @@
> > +#source: tls-le-pic-1.s
> > +#as: --x32
> > +#ld: -shared -melf32_x86_64
> > +#error: .*: relocation R_X86_64_TPOFF32 against symbol `foo' can not be used when making a shared object; replace local-exec with initial-exec TLS model
> > diff --git a/ld/testsuite/ld-x86-64/tls-le-pic-1.d b/ld/testsuite/ld-x86-64/tls-le-pic-1.d
> > new file mode 100644
> > index 00000000000..6db5f029e75
> > --- /dev/null
> > +++ b/ld/testsuite/ld-x86-64/tls-le-pic-1.d
> > @@ -0,0 +1,3 @@
> > +#as: --64
> > +#ld: -shared -melf_x86_64
> > +#error: .*: relocation R_X86_64_TPOFF32 against symbol `foo' can not be used when making a shared object; replace local-exec with initial-exec TLS model
> > diff --git a/ld/testsuite/ld-x86-64/tls-le-pic-1.s b/ld/testsuite/ld-x86-64/tls-le-pic-1.s
> > new file mode 100644
> > index 00000000000..ffc8e63b3b4
> > --- /dev/null
> > +++ b/ld/testsuite/ld-x86-64/tls-le-pic-1.s
> > @@ -0,0 +1,17 @@
> > + .text
> > + .p2align 4
> > + .globl func
> > + .type func, @function
> > +func:
> > + movq %fs:0, %rax
> > + addq $foo@tpoff, %rax
> > + ret
> > + .size func, .-func
> > + .section .tbss,"awT",@nobits
> > + .align 4
> > + .globl foo
> > + .type foo, @object
> > + .size foo, 4
> > +foo:
> > + .zero 4
> > + .section .note.GNU-stack,"",@progbits
> > diff --git a/ld/testsuite/ld-x86-64/tls-le-pic-2-x32.d b/ld/testsuite/ld-x86-64/tls-le-pic-2-x32.d
> > new file mode 100644
> > index 00000000000..e16395a5568
> > --- /dev/null
> > +++ b/ld/testsuite/ld-x86-64/tls-le-pic-2-x32.d
> > @@ -0,0 +1,4 @@
> > +#source: tls-le-pic-2.s
> > +#as: --x32
> > +#ld: -shared -melf32_x86_64
> > +#error: .*: relocation R_X86_64_TPOFF32 against undefined symbol `foo' can not be used when making a shared object; replace local-exec with initial-exec TLS model
> > diff --git a/ld/testsuite/ld-x86-64/tls-le-pic-2.d b/ld/testsuite/ld-x86-64/tls-le-pic-2.d
> > new file mode 100644
> > index 00000000000..0e718e01ebc
> > --- /dev/null
> > +++ b/ld/testsuite/ld-x86-64/tls-le-pic-2.d
> > @@ -0,0 +1,3 @@
> > +#as: --64
> > +#ld: -shared -melf_x86_64
> > +#error: .*: relocation R_X86_64_TPOFF32 against undefined symbol `foo' can not be used when making a shared object; replace local-exec with initial-exec TLS model
> > diff --git a/ld/testsuite/ld-x86-64/tls-le-pic-2.s b/ld/testsuite/ld-x86-64/tls-le-pic-2.s
> > new file mode 100644
> > index 00000000000..ad930db1546
> > --- /dev/null
> > +++ b/ld/testsuite/ld-x86-64/tls-le-pic-2.s
> > @@ -0,0 +1,10 @@
> > + .text
> > + .p2align 4
> > + .globl func
> > + .type func, @function
> > +func:
> > + movq %fs:0, %rax
> > + addq $foo@tpoff, %rax
> > + ret
> > + .size func, .-func
> > + .section .note.GNU-stack,"",@progbits
> > diff --git a/ld/testsuite/ld-x86-64/tls-le-pic-3-x32.d b/ld/testsuite/ld-x86-64/tls-le-pic-3-x32.d
> > new file mode 100644
> > index 00000000000..f6731daa991
> > --- /dev/null
> > +++ b/ld/testsuite/ld-x86-64/tls-le-pic-3-x32.d
> > @@ -0,0 +1,4 @@
> > +#source: tls-le-pic-3.s
> > +#as: --x32
> > +#ld: -shared -melf32_x86_64
> > +#error: .*: relocation R_X86_64_TPOFF32 against `foo' can not be used when making a shared object; replace local-exec with initial-exec TLS model
> > diff --git a/ld/testsuite/ld-x86-64/tls-le-pic-3.d b/ld/testsuite/ld-x86-64/tls-le-pic-3.d
> > new file mode 100644
> > index 00000000000..987e370ad17
> > --- /dev/null
> > +++ b/ld/testsuite/ld-x86-64/tls-le-pic-3.d
> > @@ -0,0 +1,3 @@
> > +#as: --64
> > +#ld: -shared -melf_x86_64
> > +#error: .*: relocation R_X86_64_TPOFF32 against `foo' can not be used when making a shared object; replace local-exec with initial-exec TLS model
> > diff --git a/ld/testsuite/ld-x86-64/tls-le-pic-3.s b/ld/testsuite/ld-x86-64/tls-le-pic-3.s
> > new file mode 100644
> > index 00000000000..6e213fd5b54
> > --- /dev/null
> > +++ b/ld/testsuite/ld-x86-64/tls-le-pic-3.s
> > @@ -0,0 +1,16 @@
> > + .text
> > + .p2align 4
> > + .globl func
> > + .type func, @function
> > +func:
> > + movq %fs:0, %rax
> > + addq $foo@tpoff, %rax
> > + ret
> > + .size func, .-func
> > + .section .tbss,"awT",@nobits
> > + .align 4
> > + .type foo, @object
> > + .size foo, 4
> > +foo:
> > + .zero 4
> > + .section .note.GNU-stack,"",@progbits
> > diff --git a/ld/testsuite/ld-x86-64/x86-64.exp b/ld/testsuite/ld-x86-64/x86-64.exp
> > index 23e0a98d1cd..f04ae923ebc 100644
> > --- a/ld/testsuite/ld-x86-64/x86-64.exp
> > +++ b/ld/testsuite/ld-x86-64/x86-64.exp
> > @@ -578,6 +578,12 @@ run_dump_test "pr33292"
> > run_dump_test "pr33292-x32"
> > run_dump_test "pr28387"
> > run_dump_test "pr28387-x32"
> > +run_dump_test "tls-le-pic-1"
> > +run_dump_test "tls-le-pic-1-x32"
> > +run_dump_test "tls-le-pic-2"
> > +run_dump_test "tls-le-pic-2-x32"
> > +run_dump_test "tls-le-pic-3"
> > +run_dump_test "tls-le-pic-3-x32"
> >
> > if { ![skip_sframe_tests] } {
> > run_dump_test "sframe-simple-1"
> > --
> > 2.51.0
> >
>
> I am checking it in.
>
> --
> H.J.
Removing "recompile with -fPIC" looks right, as this is about
executable links vs -shared.
However, the suggestion "; replace local-exec with initial-exec TLS
model" might not be the best choice.
Initial-exec for shared objects might not be supported by dlopen'ed
shared objects.
> From https://maskray.me/blog/2021-02-14-all-about-thread-local-storage
>
> If you add the __attribute((tls_model("initial-exec"))) attribute, a thread-local variable can use this model in -fpic mode. If the object file is linked into an executable, everything is fine. If the object file is linked into a shared object, the shared object generally needs to be an immediately loaded shared object. The linker sets the DF_STATIC_TLS flag to annotate a shared object with initial-exec TLS relocations.
>
> glibc ld.so reserves some space in static TLS blocks and allows dlopen on such a shared object if its TLS size is small. There could be an obscure reason for using such an attribute: general dynamic and local dynamic TLS models are not async-signal-safe in glibc. However, other libc implementations may not reserve additional TLS space for dlopen'ed initial-exec shared objects, e.g. musl will error.
Simply suggesting that local-exec is incompatible with -shared suffices.
More information about the Binutils
mailing list