This is an archive of the discontinued LLVM Phabricator instance.

[X86] -fno-plt: use GOT __tls_get_addr only if GOTPCRELX is enabled
ClosedPublic

Authored by MaskRay on Jul 7 2019, 8:13 PM.

Details

Summary

As of binutils 2.32, ld has a bogus TLS relaxation error when the GD/LD
code sequence using R_X86_64_GOTPCREL (instead of R_X86_64_GOTPCRELX) is
attempted to be relaxed to IE/LE (binutils PR24784). gold and lld are good.

In gcc/config/i386/i386.md, there is a configure-time check of as/ld
support and the GOT relaxation will not be used if as/ld doesn't support
it:

if (flag_plt || !HAVE_AS_IX86_TLS_GET_ADDR_GOT)
  return "call\t%P2";
return "call\t{*%p2@GOT(%1)|[DWORD PTR %p2@GOT[%1]]}";

In clang, -DENABLE_X86_RELAX_RELOCATIONS=OFF is the default. The ld.bfd
bogus error can be reproduced with:

thread_local int a;
int main() { return a; }

clang -fno-plt -fpic a.cc -fuse-ld=bfd

GOTPCRELX gained relative good support in 2016, which is considered
relatively new. It is even difficult to conditionally default to
-DENABLE_X86_RELAX_RELOCATIONS=ON due to cross compilation reasons. So
work around the ld.bfd bug by only using GOT when GOTPCRELX is enabled.

Diff Detail

Event Timeline

MaskRay created this revision.Jul 7 2019, 8:13 PM
Herald added a project: Restricted Project. · View Herald TranscriptJul 7 2019, 8:13 PM
nikic accepted this revision.Jul 10 2019, 10:08 AM

LGTM, this looks like a reasonable workaround to me.

This revision is now accepted and ready to land.Jul 10 2019, 10:08 AM

LGTM, this looks like a reasonable workaround to me.

Can you answer the question?

@nikic Can you share a reproduce of the Rust code?

nikic added a comment.EditedJul 11 2019, 12:31 AM

Can you share a reproduce of the Rust code?

Sorry, I missed that! The rust code is essentially the same as your C code, plus a lot of boilerplate to avoid linking libstd, otherwise the internal TLS use in libstd will already cause the linker error.

// test2.rs
#![crate_type="rlib"]
#![no_std]
#![feature(thread_local)]

#[thread_local]
static TEST: isize = 0;

pub fn get_test() -> isize {
    TEST
}

// test.rs
#![no_std]
#![no_main]
extern crate test2;

use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
    loop {}
}

#[no_mangle]
pub extern "C" fn _start() -> isize {
    test2::get_test()
}

Additionally -C link-arg=-nostartfiles needs to be specified to avoid linking libc. (Equivalents of -fno-plt and -fpic are enabled by default already.)

This requires a rustc build linked against LLVM 9 though, and the only public build with LLVM 9 (rustup-toolchain-install-master 82310f68ee567093e06ad767cc0d3cb6127457ba) already includes your patch here, otherwise rustc would be practically unusable.

MaskRay updated this revision to Diff 209155.Jul 11 2019, 3:05 AM

Add a TODO that we should delete the workaround when GOTPCRELX becomes commonplace

This revision was automatically updated to reflect the committed changes.