x86-64: Fix misleading R_X86_64_TPOFF32 error message

Message ID 20250909203836.3988300-1-hjl.tools@gmail.com
State Committed
Headers
Series x86-64: Fix misleading R_X86_64_TPOFF32 error message |

Commit Message

H.J. Lu Sept. 9, 2025, 8:38 p.m. UTC
  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
  

Comments

H.J. Lu Sept. 10, 2025, 1:12 p.m. UTC | #1
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.
  
Fangrui Song Sept. 10, 2025, 4:57 p.m. UTC | #2
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.
  
H.J. Lu Sept. 10, 2025, 7:20 p.m. UTC | #3
On Wed, Sep 10, 2025 at 9:57 AM Fangrui Song <i@maskray.me> wrote:
>
> 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.

Good point.  Submitted a patch.
  

Patch

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"