Bug 23870 - gold does not resolve the address of main when main is in a shared library
Summary: gold does not resolve the address of main when main is in a shared library
Status: RESOLVED FIXED
Alias: None
Product: binutils
Classification: Unclassified
Component: gold (show other bugs)
Version: 2.31
: P2 normal
Target Milestone: ---
Assignee: Cary Coutant
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2018-11-07 13:47 UTC by Stephen Kim
Modified: 2021-03-20 00:59 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Stephen Kim 2018-11-07 13:47:29 UTC
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.
Comment 1 Stephen Kim 2018-11-09 13:35:59 UTC
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.
Comment 2 Stephen Kim 2018-11-15 09:41:24 UTC
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
Comment 3 Sourceware Commits 2019-02-20 00:14:05 UTC
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.
Comment 4 Cary Coutant 2019-02-20 00:34:04 UTC
Fixed on trunk.
Comment 5 Sourceware Commits 2021-03-20 00:59:01 UTC
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.