Bug 19353 - ld-new: internal error in relocate_tls, at ../../binutils/gold/aarch64.cc:7418
Summary: ld-new: internal error in relocate_tls, at ../../binutils/gold/aarch64.cc:7418
Status: RESOLVED FIXED
Alias: None
Product: binutils
Classification: Unclassified
Component: gold (show other bugs)
Version: 2.27
: P2 normal
Target Milestone: ---
Assignee: Cary Coutant
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-12-10 13:20 UTC by Andreas Schwab
Modified: 2016-01-12 07:59 UTC (History)
4 users (show)

See Also:
Host:
Target: aarch64-*-*
Build:
Last reconfirmed:


Attachments
testcase.tar.xz, Part 1 (9.54 MB, application/octet-stream)
2015-12-10 13:20 UTC, Andreas Schwab
Details
testcase.tar.xz, Part 2 (9.54 MB, application/octet-stream)
2015-12-10 13:21 UTC, Andreas Schwab
Details
testcase.tar.xz, Part 3 (5.83 MB, application/octet-stream)
2015-12-10 13:22 UTC, Andreas Schwab
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Andreas Schwab 2015-12-10 13:20:43 UTC
Created attachment 8840 [details]
testcase.tar.xz, Part 1

When building webkitgtk-2.10.4:

$ LD=gold/ld-new ./link
gold/ld-new: internal error in relocate_tls, at ../../binutils/gold/aarch64.cc:7418
Comment 1 Andreas Schwab 2015-12-10 13:21:50 UTC
Created attachment 8841 [details]
testcase.tar.xz, Part 2
Comment 2 Andreas Schwab 2015-12-10 13:22:25 UTC
Created attachment 8842 [details]
testcase.tar.xz, Part 3
Comment 3 Kristof Beyls 2016-01-05 11:01:02 UTC
I hit the same assert when building LLVM's unit test on AArch64 with gold.
It seems the issue is triggered when linking to a thread-local variable in dynamically linked library.

Reproducer 1:
$ cat reproduce_gold_crash.sh
cat > gold_crash_a1.c << INPUT
__thread void* tv;
INPUT

cat > gold_crash_b1.c << INPUT
extern __thread void* tv;
int main(int argc, char **argv) {
  tv = argv;
}
INPUT

gcc -O2 -fPIC -rdynamic -shared -Wl,-soname,gold_crash_a1.so.1 -o libgold_crash_a1.so gold_crash_a1.c
gcc -O2 -fPIC -rdynamic -o gold_crash1.exe gold_crash_b1.c -L.  -lgold_crash_a1
$ PATH=$HOME/bin:$PATH ./reproduce_gold_crash.sh
/home/llvm-test/bin/ld: internal error in relocate_tls, at ../../binutils-gdb/gold/aarch64.cc:7418
collect2: error: ld returned 1 exit status


Reproducer 2:
$ cat call_once.cpp
#include <mutex>
#include <stdio.h>

std::once_flag flag1;

int main() {
  std::call_once(flag1, [](){puts("Hello!\n");});
}
$ PATH=$HOME/bin:$PATH g++ -O2 -fPIC -std=c++11 call_once.cpp
/home/llvm-test/bin/ld: internal error in relocate_tls, at ../../binutils-gdb/gold/aarch64.cc:7418
collect2: error: ld returned 1 exit status
$ PATH=$HOME/bin:$PATH ld -v
GNU gold (GNU Binutils 2.26.51.20160104) 1.11


I could also reproduce this with the following Debian versions of gold:
* GNU gold (GNU Binutils for Debian 2.25.1) 1.11
* GNU gold (GNU Binutils for Debian 2.25) 1.11
Comment 4 Han Shen 2016-01-08 01:09:19 UTC
Hi, Kristof, I was able to reproduce case 2 (but not 1).

In gold, tls_segment is never created in layout.cc, so comes the assertion. In call_once.o, there is the tls symbol _ZSt15__once_callable, however there is no sections that has bit elfcpp::SHF_TLS turned on, and tls_segment is only created if there is at least one sectoin with SHF_TLS bit.

So, Cary, shall we create the tls_segment whenever the Scanner encounters a tls symbol?
Comment 5 Cary Coutant 2016-01-12 07:19:01 UTC
(In reply to Han Shen from comment #4)
> Hi, Kristof, I was able to reproduce case 2 (but not 1).
> 
> In gold, tls_segment is never created in layout.cc, so comes the assertion.
> In call_once.o, there is the tls symbol _ZSt15__once_callable, however there
> is no sections that has bit elfcpp::SHF_TLS turned on, and tls_segment is
> only created if there is at least one sectoin with SHF_TLS bit.
> 
> So, Cary, shall we create the tls_segment whenever the Scanner encounters a
> tls symbol?

It looks like this is in relocate_tls(), in this block:

    case elfcpp::R_AARCH64_TLSDESC_ADR_PAGE21:
    case elfcpp::R_AARCH64_TLSDESC_LD64_LO12:
    case elfcpp::R_AARCH64_TLSDESC_ADD_LO12:
    case elfcpp::R_AARCH64_TLSDESC_CALL:
      ...
	    if (tlsopt == tls::TLSOPT_TO_IE)
	      {
		if (tls_segment == NULL)
		  {
		    gold_assert(parameters->errors()->error_count() > 0
				|| issue_undefined_symbol_error(gsym));
		    return aarch64_reloc_funcs::STATUS_BAD_RELOC;
		  }
		return tls_desc_gd_to_ie(relinfo, target, rela, r_type,
					 view, psymval, got_entry_address,
					 address);
	      }

at the gold_assert() there. Is that where the assert is happening for you?

Looking at tls_desc_gd_to_ie(), and comparing this with the similar case in the x86 backend, it looks to me like the insistence on having a non-NULL tls_segment here is too zealous -- the optimization from GD to IE never needs tls_segment, and it's perfectly reasonable to have a relocation to an external TLS symbol without having a TLS segment in the main executable.

I think if you simply remove the "if (tls_segment == NULL) { ... }" block here, it will fix the problem.
Comment 6 Cary Coutant 2016-01-12 07:59:06 UTC
In fact, if I compile the example for x86-64 with -mtls-dialect=gnu2, I see the same failure. In x86_64.cc, we don't check for tls_segment when optimizing R_X86_64_TLSGD to IE, but we do have a bogus check when optimizing R_X86_64_GOTPC32_TLSDESC or R_X86_64_TLSDESC_CALL. We have a similar problem in i386.cc. It looks like the aarch64.cc target simply inherited the bug from x86_64.cc.
Comment 7 Sourceware Commits 2016-01-12 07:59:09 UTC
The master branch has been updated by Cary Coutant <ccoutant@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=d21f123b0ead1806416cf0dafae12bec4cca8920

commit d21f123b0ead1806416cf0dafae12bec4cca8920
Author: Cary Coutant <ccoutant@gmail.com>
Date:   Mon Jan 11 23:57:44 2016 -0800

    Fix internal error when applying TLSDESC relocations with no TLS segment.
    
    gold/
    	PR gold/19353
    	* aarch64.cc (Target_aarch64::relocate_tls): Don't insist that
    	we have a TLS segment for GD-to-IE optimization.
    	* i386.cc (Target_i386::tls_gd_to_ie): Remove tls_segment parameter.
    	Adjust all calls.
    	(Target_i386::tls_desc_gd_to_ie): Likewise.
    	(Target_i386::relocate_tls): Don't insist that we have a TLS segment
    	for TLSDESC GD-to-IE optimizations.
    	* x86_64.cc (Target_x86_64::tls_gd_to_ie): Remove tls_segment parameter.
    	Adjust all calls.
    	(Target_x86_64::tls_desc_gd_to_ie): Likewise.
    	(Target_x86_64::relocate_tls): Don't insist that we have a TLS segment
    	for TLSDESC GD-to-IE optimizations.
Comment 8 Cary Coutant 2016-01-12 07:59:56 UTC
Fixed on trunk.