Behavior change, PLT entries for R_X86_64_PLT32 relocations with undefined symbol

Martin McClure martin.mcclure@gemtalksystems.com
Tue Mar 19 02:07:00 GMT 2019


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



More information about the Binutils mailing list