This is reproducible on aarch64. I got notice this issue due to the Go tools. However, here is the reduced test case purely in C. When a PIC shared library that has the "main" function is linked against a non-PIC .o file to form a non-PIE executable, Gold and the GNU linker both use crti.o. Only Gold does not find the address of main properly. The address is 0 in the generated executable, which leads the segmentation fault at runtime. Here is my reduced example: $ cat main.c #include <stdio.h> extern int foo(int, int); int main() { printf("%d\n", foo(1, 3)); return 0; } $ cat foo.c int __attribute__ ((noinline)) foo(int x, int y) { return x & y; } $ gcc -o main.o -fPIC -c main.c $ gcc -shared -o libmain.so main.o $ gcc -o foo.o -c foo.c $ gcc -fuse-ld=gold -o a.out -lmain -L$PWD -Wl,-v,-rpath=$PWD foo.o collect2 version 4.8.5 20150623 (Red Hat 4.8.5-28.0.4) /usr/bin/ld.gold --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -dynamic-linker /lib/ld-linux-aarch64.so.1 -X -o a.out /usr/lib/gcc/aarch64-redhat-linux/4.8.5/../../../../lib64/crt1.o /usr/lib/gcc/aarch64-redhat-linux/4.8.5/../../../../lib64/crti.o /usr/lib/gcc/aarch64-redhat-linux/4.8.5/crtbegin.o -L/home/aion1223/shared -L/usr/lib/gcc/aarch64-redhat-linux/4.8.5 -L/usr/lib/gcc/aarch64-redhat-linux/4.8.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/aarch64-redhat-linux/4.8.5/../../.. -lmain -v -rpath=/home/aion1223/shared foo.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/aarch64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/aarch64-redhat-linux/4.8.5/../../../../lib64/crtn.o GNU gold (version 2.27-28.base.0.2.el7_5.1) 1.12 $ ./a.out Segmentation fault (core dumped) If I replace -fuse-ld=gold with -fuse-ld=bfd, it works. The crti.o is still preferred over Scrti.o. This started to happen once glibc is updated. The difference is start.S in glibc has changed to use so called MOVL macro. It is defined in glibc's sysdep.h. /* Load an immediate into R. Note R is a register number and not a register name. */ #ifdef __LP64__ # define MOVL(R, NAME) \ movz PTR_REG (R), #:abs_g3:NAME; \ movk PTR_REG (R), #:abs_g2_nc:NAME; \ movk PTR_REG (R), #:abs_g1_nc:NAME; \ movk PTR_REG (R), #:abs_g0_nc:NAME; #else # define MOVL(R, NAME) \ movz PTR_REG (R), #:abs_g1:NAME; \ movk PTR_REG (R), #:abs_g0_nc:NAME; #endif The start.S has a code like MOVL(0, main), and after linking, 0 is assigned to x0. That code is only for non-PIC/PIE. Also, if main is in the .o file rather than in the .so file, both linkers had no problem: $ gcc -fPIC -o foo.o -c foo.c $ gcc -shared -o libfoo.so foo.o $ gcc -o main.o -c main.c $ gcc -fuse-ld=gold -o a.out -lfoo -L$PWD -Wl,-v,-rpath=$PWD main.o collect2 version 4.8.5 20150623 (Red Hat 4.8.5-28.0.4) /usr/bin/ld.gold --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -dynamic-linker /lib/ld-linux-aarch64.so.1 -X -o a.out /usr/lib/gcc/aarch64-redhat-linux/4.8.5/../../../../lib64/crt1.o /usr/lib/gcc/aarch64-redhat-linux/4.8.5/../../../../lib64/crti.o /usr/lib/gcc/aarch64-redhat-linux/4.8.5/crtbegin.o -L/home/aion1223/shared2 -L/usr/lib/gcc/aarch64-redhat-linux/4.8.5 -L/usr/lib/gcc/aarch64-redhat-linux/4.8.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/aarch64-redhat-linux/4.8.5/../../.. -lfoo -v -rpath=/home/aion1223/shared2 main.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/aarch64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/aarch64-redhat-linux/4.8.5/../../../../lib64/crtn.o GNU gold (version 2.27-28.base.0.2.el7_5.1) 1.12 $ ./a.out 1 $ gcc -fuse-ld=bfd -o a.out -lfoo -L$PWD -Wl,-v,-rpath=$PWD main.o collect2 version 4.8.5 20150623 (Red Hat 4.8.5-28.0.4) /usr/bin/ld.bfd --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -dynamic-linker /lib/ld-linux-aarch64.so.1 -X -o a.out /usr/lib/gcc/aarch64-redhat-linux/4.8.5/../../../../lib64/crt1.o /usr/lib/gcc/aarch64-redhat-linux/4.8.5/../../../../lib64/crti.o /usr/lib/gcc/aarch64-redhat-linux/4.8.5/crtbegin.o -L/home/aion1223/shared2 -L/usr/lib/gcc/aarch64-redhat-linux/4.8.5 -L/usr/lib/gcc/aarch64-redhat-linux/4.8.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/aarch64-redhat-linux/4.8.5/../../.. -lfoo -v -rpath=/home/aion1223/shared2 main.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/aarch64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/aarch64-redhat-linux/4.8.5/../../../../lib64/crtn.o GNU ld version 2.27-28.base.0.2.el7_5.1 $ ./a.out 1 I am not sure if this is a Gold bug. However, I believe that it is worthy looking into. As this issue is not reproducible with bfd, I filed this bug against Gold. The details is here: https://github.com/golang/go/issues/28334 I haven't heard a single C/C++ application that puts main in a shared library yet. However, it seems like Go might do it more often. Perhaps, this bug might not be crucial in the C/C++ world but has some meaning in the Go world.
To reproduce the issue, glibc should be updated. The one I have used is the oraclelinux:7.5 docker container. After yum update, the bug is probably reproducible.
This bug is reproducible with Gold at the head of the master branch: commit a0cd42fcd1d9a7544e93f6b1ef7f9a1fe98c8897 $ git remote show origin| egrep Fetch Fetch URL: https://sourceware.org/git/binutils-gdb.git
The master branch has been updated by Cary Coutant <ccoutant@sourceware.org>: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=7ae39e2d406dbec568c5ffd462119037b994fdf9 commit 7ae39e2d406dbec568c5ffd462119037b994fdf9 Author: Egeyar Bagcioglu <egeyar.bagcioglu@oracle.com> Date: Tue Feb 19 16:12:44 2019 -0800 Check whether symbols with MOVW_.ABS relocations require PLT entries (aarch64). 2019-02-19 Egeyar Bagcioglu <egeyar.bagcioglu@oracle.com> gold/ PR gold/23870 * aarch64.cc (Target_aarch64::Scan::global): Check if a symbol with R_AARCH64_MOVW_.ABS_* relocations requires a PLT entry. * testsuite/Makefile.am: Add aarch64_pr23870 test case. * testsuite/Makefile.in: Regenerate. * testsuite/aarch64_pr23870_bar.c: New file. * testsuite/aarch64_pr23870_foo.c: New file. * testsuite/aarch64_pr23870_main.S: New file.
Fixed on trunk.
The master branch has been updated by Cary Coutant <ccoutant@sourceware.org>: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=b218a839358197c1b62200481dc14eb3c8a9a38f commit b218a839358197c1b62200481dc14eb3c8a9a38f Author: Cary Coutant <ccoutant@gmail.com> Date: Fri Mar 19 17:57:40 2021 -0700 Fix failing test for PR 23870. gold/ PR gold/pr23870 * testsuite/aarch64_pr23870_bar.c: Return a magic value. * testsuite/aarch64_pr23870_foo.c: Check the magic value and return success or failure.