This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
Behavior change, PLT entries for R_X86_64_PLT32 relocations with undefined symbol
- From: Martin McClure <martin dot mcclure at gemtalksystems dot com>
- To: binutils at sourceware dot org
- Date: Mon, 18 Mar 2019 19:07:55 -0700
- Subject: Behavior change, PLT entries for R_X86_64_PLT32 relocations with undefined symbol
tl;dr: The linker produced PLT entries for undefined symbols with
R_X86_64_PLT32 relocations up through Ubuntu 16.04; but produces calls
to empty PLT entries in Ubuntu 18.04. Has this usage ever been legal,
and if so how can it be made to work now?
(very simplified) reproduction case, x86_64:
----
library.c
int answer() {
return 42;
}
----
executable.c
#include <dlfcn.h>
int answer();
int main()
{
void *lib = dlopen("./library.so", RTLD_LAZY | RTLD_GLOBAL);
if (!lib) {
printf("dlopen failed");
}
printf("The answer is %d\n", answer());
return 0;
}
----
Compile and link:
gcc -fpic library.c -c -o library.o
gcc -shared -o library.so library.o
gcc -c -fPIC executable.c -o executable.o
gcc executable.o -lc -ldl -Wl,--unresolved-symbols=ignore-all -o executable
---
This produces a functional executable in Ubuntu versions up to and
including 16.04 (gcc 5.4.0, ld 2.26.1) but fails in Ubuntu 18.04 (gcc
7.3.0, ld 2.30). Another difference is that Ubuntu 18.04 configures with
--enable-default-pie. Full configuration info below for reference.
In the success case, ld produces a PLT entry for the symbol "answer" and
the call to answer() goes to that entry.
In the failure case, the call to answer() goes to an offset in the PLT,
but that offset in the PLT is empty (zeroes).
THE QUESTION: Is it supported usage to expect the linker to produce a
PLT entry from a R_X86_64_PLT32 relocation, even if the symbol is
undefined? And if so, is the failure I'm seeing a matter of incorrect
invocation, or a bug?
Why do I want this to work? The code base I work with has been using
this for quite a few years (long before I got involved). There are two
shared libraries that each implement the same few hundred functions, but
implement them differently (one locally, the other via RPC). By having
the executable dlopen() the version it wants to use, it can choose the
implementation at runtime rather than at link time.
Thanks for considering this question. Let me know if I've been unclear
or omitted any useful information.
Regards,
-Martin
Configuration for Ubuntu 16.04:
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu
5.4.0-6ubuntu1~16.04.11'
--with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs
--enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++
--prefix=/usr --program-suffix=-5 --enable-shared
--enable-linker-build-id --libexecdir=/usr/lib
--without-included-gettext --enable-threads=posix --libdir=/usr/lib
--enable-nls --with-sysroot=/ --enable-clocale=gnu
--enable-libstdcxx-debug --enable-libstdcxx-time=yes
--with-default-libstdcxx-abi=new --enable-gnu-unique-object
--disable-vtable-verify --enable-libmpx --enable-plugin
--with-system-zlib --disable-browser-plugin --enable-java-awt=gtk
--enable-gtk-cairo
--with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre
--enable-java-home
--with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64
--with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64
--with-arch-directory=amd64
--with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc
--enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64
--with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic
--enable-checking=release --build=x86_64-linux-gnu
--host=x86_64-linux-gnu --target=x86_64-linux-gnu
Configuration for Ubuntu 18.04:
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu
7.3.0-27ubuntu1~18.04'
--with-bugurl=file:///usr/share/doc/gcc-7/README.Bugs
--enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++
--prefix=/usr --with-gcc-major-version-only --program-suffix=-7
--program-prefix=x86_64-linux-gnu- --enable-shared
--enable-linker-build-id --libexecdir=/usr/lib
--without-included-gettext --enable-threads=posix --libdir=/usr/lib
--enable-nls --with-sysroot=/ --enable-clocale=gnu
--enable-libstdcxx-debug --enable-libstdcxx-time=yes
--with-default-libstdcxx-abi=new --enable-gnu-unique-object
--disable-vtable-verify --enable-libmpx --enable-plugin
--enable-default-pie --with-system-zlib --with-target-system-zlib
--enable-objc-gc=auto --enable-multiarch --disable-werror
--with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32
--enable-multilib --with-tune=generic
--enable-offload-targets=nvptx-none --without-cuda-driver
--enable-checking=release --build=x86_64-linux-gnu
--host=x86_64-linux-gnu --target=x86_64-linux-gnu