llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.43k stars 11.74k forks source link

PPC32: executables linked with LLD gives segfault at _start on FreeBSD #42461

Closed 688cc089-1a96-4cb4-bd77-eaf433fc1da2 closed 5 years ago

688cc089-1a96-4cb4-bd77-eaf433fc1da2 commented 5 years ago
Bugzilla Link 43116
Resolution FIXED
Resolved on Aug 29, 2019 04:40
Version unspecified
OS FreeBSD
Attachments test programs code and binaries
CC @MaskRay,@luporl,@smithp35

Extended Description

On FreeBSD13/powerpc64, I'm trying to compile and run powerpc32 binaries, but they fail with segmentation fault at section "_start".

The test program is pretty simple:

int main() { return 0; }

The FreeBSD 32 bit compatibility libraries where linked using BFD and applications compiled with CLANG9+BFD run fine on this system. The problem appears when CLANG9+LLD9 is used.

Output on gdb:

Program received signal SIGSEGV, Segmentation fault. 0x39600000 in ?? () (gdb) bt

​0 0x39600000 in ?? ()

​1 0x100100b4 in _start (argc=1, argv=, env=, obj=, cleanup=0x5003d254 <gethints+584>,

ps_strings=0x0) at /root/freebsd/lib/csu/powerpc/crt1.c:87

Following instruction by instruction execution on GDB, the crash occurs when executing <_start+176>, with the following message:

Program received signal SIGSEGV, Segmentation fault. 0x39600000 in ?? () Cannot access memory at address 0x39600000

... ... 0x10010074 <_start+116> beq 0x10010084 <_start+132> 0x10010078 <_start+120> cmplwi r6,0 0x1001007c <_start+124> bne 0x10010068 <_start+104> 0x10010080 <_start+128> b 0x1001008c <_start+140> 0x10010084 <_start+132> stw r4,0(r3) 0x10010088 <_start+136> b 0x10010068 <_start+104> 0x1001008c <_start+140> cmplwi r8,0 0x10010090 <_start+144> beq 0x1001009c <_start+156> 0x10010094 <_start+148> lis r3,4099 0x10010098 <_start+152> stw r8,24(r3) 0x1001009c <_start+156> lis r3,4098 0x100100a0 <_start+160> addi r3,r3,20 0x100100a4 <_start+164> cmplwi r3,0 0x100100a8 <_start+168> beq 0x100100b8 <_start+184> 0x100100ac <_start+172> mr r3,r7 0x100100b0 <_start+176> bl 0x100103a8 <00000000.plt_call32.atexit> ... ...

688cc089-1a96-4cb4-bd77-eaf433fc1da2 commented 5 years ago

Thank you Fangrui, test program is working fine now with your patches and the lines bellow added right after ".text" in crt1.s:

.globl _GLOBAL_OFFSETTABLE .reloc 0, R_PPC_NONE, _GLOBAL_OFFSETTABLE

Backport from trunk to llvm9 was requested (and merged) by llvm/llvm-bugzilla-archive#43148

So I believe this can be closed

MaskRay commented 5 years ago

Fangrui, I added the two lines to crt1.s (also tried with crti.S), but ld.lld refuses to link the .o generated from clang with the following error:
"unrecognized relocation R_PPC_NONE"

It appears that some required features (by FreeBSD/PPC32) didn't get commited in time for LLVM 9.

Before https://reviews.llvm.org/rL368964 , lld refused input relocation types R_PPC_NONE and R_PPC64_NONE. release_90 was branched at r366426, so r368964 is not included.

There is probably still time to merge bugfixes into lld 9. Can you file a new bug to request for merging r368964 and r369184 into release_90, and mark the bug as a blocker of llvm/llvm-project#41819 ? r369184 is a PPC64 bug fix. r368964 is a preparation commit for r369184 and is required to make the .reloc 0, R_PPC_NONE, _GLOBAL_OFFSET_TABLE_ crt change. So it should also be merged.

Did you mean

crt1.s unpatched and LLVM10 (trunk) => crash crt1 patched and linked with lld trunk => work

? (BTW, .reloc , R_PPC_NONE, was supported in r360990, which is included in release_90. It wasn't supported in clang 8)

If patched crt1 works for you, I think there is nothing left on lld's side.

glibc crti.o has an R_PPC_GOT16 __gmon_start__ to force creation .got

musl crt files do not have GOT-generating relocations. It can have 0 DT_PPC_GOT, but because it does not support lazy binding PLT, a missing .got does not matter.

FreeBSD rtld-elf powerpc doesn't have GOT-generating relocations in its crt files but it supports lazy binding PLT. In the -fno-pie -no-pie crt files, I think there should be a .reloc 0, R_PPC_NONE, _GLOBAL_OFFSETTABLE to make it clear .got is needed.

688cc089-1a96-4cb4-bd77-eaf433fc1da2 commented 5 years ago

crt1.s unpatched and LLVM10 (trunk)

This is the reproduce tarball where all objects where compiled and linked using llvm10/trunk.

Clang trunk didn't make any difference, the crash point is the same as observed in the first experiment.

688cc089-1a96-4cb4-bd77-eaf433fc1da2 commented 5 years ago

crt1 patched and linked with lld trunk I linked the patched crt1.o using ld.lld from trunk (2ed2e62498b) with the other objects and t1 runs fine!

Here's the reproduce tarball attached, for comparison. All objects where compiled using CLANG9, then linked using LLD10.

It appears that some required features (by FreeBSD/PPC32) didn't get commited in time for LLVM 9.

As next step, I'll try to build the objects using LLVM10 and see there's any difference using crt1.s unpatched.

688cc089-1a96-4cb4-bd77-eaf433fc1da2 commented 5 years ago

You can place the following two lines of a.s to lib/csu/powerpc/crti.S or other file where you think is appropriate:

% cat a.s .globl _GLOBAL_OFFSETTABLE .reloc 0, R_PPC_NONE, _GLOBAL_OFFSETTABLE

Fangrui, I added the two lines to crt1.s (also tried with crti.S), but ld.lld refuses to link the .o generated from clang with the following error:

"unrecognized relocation R_PPC_NONE"

688cc089-1a96-4cb4-bd77-eaf433fc1da2 commented 5 years ago

Can you set a breakpoint at 0x100103b0, print r11? I guess ld.so failed to populate the slot.

It says:

(gdb) p/x $r11 $2 = 0x39600000

MaskRay commented 5 years ago

I think why ld.so didn't populate .plt slots may be related to the zero DT_PPC_GOT (missing .got). If I hacked ld.bfd to force Secure PLT, I notice that:

% ./ld-new @​response.txt -y _GLOBAL_OFFSETTABLE ./ld-new: root/t1/usr/lib32/crt1.o: definition of _GLOBAL_OFFSETTABLE ./ld-new: bss-plt forced due to root/t1/usr/lib32/crt1.o

You can place the following two lines of a.s to lib/csu/powerpc/crti.S or other file where you think is appropriate:

% cat a.s .globl _GLOBAL_OFFSETTABLE .reloc 0, R_PPC_NONE, _GLOBAL_OFFSETTABLE % llvm-mc -filetype=obj -triple=ppc a.s -o a.o % ld.lld @​response.txt -z separate-code a.o -y _GLOBAL_OFFSETTABLE -o a a.o: reference to _GLOBAL_OFFSETTABLE

: definition of _GLOBAL_OFFSET_TABLE_ # a has .got has non-zero DT_PPC_GOT
MaskRay commented 5 years ago

The "bss-plt forced due to root/t1/usr/lib32/crt1.o" (also crtbegin.o ...) is due to a different between clang and gcc -fno-pic (which applies to both -mbss-plt and -msecure-plt). It may not be very relevant here

clang -target ppc -msecure-plt -fno-pic -c => R_PPC_PLTREL24 with 0 addend gcc -msecure-plt -fno-pic -c => R_PPC_REL24 with 0 addend (Verified with lib32.txz downloaded from https://download.freebsd.org/ftp/releases/powerpc/12.0-RELEASE/ the crt1.o uses R_PPC_REL24 for external calls: atexit, main, and exit)

The warning is issued by bfd/elf32-ppc.c:ppc_elf_select_plt_layout on following conditions

With top of trunk lld

% ld.lld @​response.txt -z separate-code # -z separate-code to place _start at 0x10010000

% powerpc-linux-gnu-objdump -dr root/t1/usr/lib32/crt1.o b0: 48 00 00 01 bl b0 <_start+0xb0> b0: R_PPC_PLTREL24 atexit b4: 48 00 00 08 b bc <_start+0xbc> b8: 48 00 00 01 bl b8 <_start+0xb8> b8: R_PPC_PLTREL24 _init_tls

% powerpc-linux-gnu-objdump -dr t1.lld ... 100100b0: 48 00 02 f9 bl 100103a8 <00000000.plt_call32.atexit> # this jumps to a thunk 100100b4: 48 00 00 08 b 100100bc <_start+0xbc> 100100b8: 48 00 03 01 bl 100103b8 <00000000.plt_call32._init_tls> ... 100103a8 <00000000.plt_call32.atexit>: 100103a8: 3d 60 10 03 lis r11,4099 100103ac: 81 6b 00 b0 lwz r11,176(r11) # load address from 0x100300b0 100103b0: 7d 69 03 a6 mtctr r11 100103b4: 4e 80 04 20 bctr

65536*4099+176 = 0x100300b0. It references the atexit@FBSD_1.0 .plt slot that will be relocated by an R_PPC_JMP_SLOT

% readelf -r t1.lld

Relocation section '.rela.plt' at offset 0x328 contains 4 entries: Offset Info Type Sym. Value Symbol's Name + Addend 100300b0 00000215 R_PPC_JMP_SLOT 00000000 atexit@FBSD_1.0 + 0 100300b4 00000115 R_PPC_JMP_SLOT 00000000 _init_tls@FBSD_1.0 + 0 100300b8 00000315 R_PPC_JMP_SLOT 00000000 exit@FBSD_1.0 + 0 100300bc 00000415 R_PPC_JMP_SLOT 00000000 _Jv_RegisterClasses + 0

Can you set a breakpoint at 0x100103b0, print r11? I guess ld.so failed to populate the slot.

688cc089-1a96-4cb4-bd77-eaf433fc1da2 commented 5 years ago

LLD reproduce tarball Here's the reproduce tarball created with:

/root/test/clang -target powerpc-unknown-freebsd13.0 -O2 -g -msecure-plt -Wl,--secure-plt --sysroot=/root/t1 t1.c -o t1.bfd -fuse-ld=/root/test/ld.lld -Wl,--reproduce=a.tar

The sysroot at /root/t1 was built with in-system clang8 (patched) and linked with ld.bfd 2.17. I forced "-msecure-plt -Wl,--secure-plt" just in case.

I suspect the problem is actually in clang (both 8 and 9), as bfd is still complaining about falling back to bss-plt due to crt1.o. I don't know how to confirm that, but I hope you can confirm that in this attachment :)

688cc089-1a96-4cb4-bd77-eaf433fc1da2 commented 5 years ago

Fangrui,

It's supposed to be secure-plt already, since in-system compiler it already patched like this:

ppc::ReadGOTPtrMode ppc::getPPCReadGOTPtrMode(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) { if (Args.getLastArg(options::OPT_msecure_plt)) return ppc::ReadGOTPtrMode::SecurePlt; if ((Triple.isOSFreeBSD() && Triple.getOSMajorVersion() >= 13) || Triple.isOSNetBSD() || Triple.isOSOpenBSD()) return ppc::ReadGOTPtrMode::SecurePlt; else return ppc::ReadGOTPtrMode::Bss; }

(see https://github.com/freebsd/freebsd/blob/master/contrib/llvm/tools/clang/lib/Driver/ToolChains/Arch/PPC.cpp#L119)

Anyway, I found out ld.bfd complains it's using bss-plt due to crt1.o when linking some binaries. So something is wrong on my environment. I'm rebuilding the system again with -msecure-plt for comparison.

MaskRay commented 5 years ago

Did you compile and link with -msecure-plt?

If FreeBSD is expected to always use Secure PLT ABI on powerpc, we can add Triple.isOSFreeBSD() below

ppc::ReadGOTPtrMode ppc::getPPCReadGOTPtrMode(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) { if (Args.getLastArg(options::OPT_msecure_plt)) return ppc::ReadGOTPtrMode::SecurePlt; if (Triple.isOSNetBSD() || Triple.isOSOpenBSD() || Triple.isMusl()) /// <--- return ppc::ReadGOTPtrMode::SecurePlt; else return ppc::ReadGOTPtrMode::Bss; }

lld's PPC32 port only supports Secure PLT ABI. I can't find a reproduce tarball in the attachment but I notice that readelf -d says PPC_GOT is 0 -> there is no .got section