Bug 1570853

Summary: linker wrapper scripts corner cases should be documented
Product: Red Hat Developer Toolset Reporter: Mark Wielaard <mjw>
Component: doc-User_GuideAssignee: Vladimír Slávik <vslavik>
Status: CLOSED CURRENTRELEASE QA Contact:
Severity: unspecified Docs Contact:
Priority: unspecified    
Version: DTS 7.0 RHEL 7CC: jakub, jhradile, kanderso, lkuprova, mcermak, mjw, mnewsome, mpolacek, ohudlick, vslavik
Target Milestone: alpha   
Target Release: 7.1   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: Doc Type: Known Issue
Doc Text:
Specify libraries after object files when linking with GCC In Red Hat Developer Toolset, libraries are linked via linker scripts which might specify some symbols through static archives. This is required to ensure compatibility with multiple versions of Red Hat Enterprise Linux. However, the linker scripts use names of the respective shared object files. As a consequence, the linker uses different symbol handling rules than expected, and does not recognize symbols required by object files when the option adding the library is specified before options specifying the object files, such as: gcc -lsomelib objfile.o Such use of a library from Red Hat Developer Toolset results in linker error messages "undefined reference to symbol". To enable successful symbol resolution and linking, follow the standard linking practice and specify the option adding the library after the options specifying the object files: gcc objfile.o -lsomelib Note that this recommendation applies when using the version of GCC available as a part of Red Hat Enterprise Linux, too.
Story Points: ---
Clone Of: Environment:
Last Closed: 2018-08-01 10:49:11 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Mark Wielaard 2018-04-23 14:24:58 UTC
There are linker script wrappers for libelf.so and libdw.so to pull in the static archives when build under DTS. But they don't work when specified on the gcc command line with -lelf or -ldw. The work when given as input scripts directly.

For example taken test_elf.c:

#include <elf.h>
#include <libelf.h>

int
main (int argc, char **argv)
{
  return (elf_version (EV_CURRENT) == EV_CURRENT) ? 0 : 1;
}

$ scl enable devtoolset-7 bash
$ $ gcc -lelf -o test_elf test_elf.c 
/tmp/cckNkFo4.o: In function `main':
test_elf.c:(.text+0x15): undefined reference to `elf_version'
collect2: error: ld returned 1 exit status

But given the linker wrapper script directly as input does work:
$ gcc -o test_elf test_elf.c /opt/rh/devtoolset-7/root/usr/lib64/libelf.so
$ ./test_elf
$ echo $?
0

Comment 2 Mark Wielaard 2018-04-23 15:01:59 UTC
Better workaround, put the -lelf last:

$ gcc -o test_elf test_elf.c -lelf
$ ./test_elf
$ echo $?
0

Comment 3 Mark Wielaard 2018-04-24 10:22:05 UTC
Consensus seems to be that this isn't really a bug, just a slight difference caused by the implementation of the library wrapper scripts that needs documenting because things work slightly differently between the base RHEL toolchain and the DTS toolchain that uses linker script wrappers for libraries.

The issue seen comes from DTS using wrapper scripts for various libraries to define some or all symbols through static archives and the follow ld linker "rule" (see the ld(1) manual page):

           The linker will search an archive only once, at the location where
           it is specified on the command line.  If the archive defines a
           symbol which was undefined in some object which appeared before the
           archive on the command line, the linker will include the
           appropriate file(s) from the archive.  However, an undefined symbol
           in an object appearing later on the command line will not cause the
           linker to search the archive again.

The base RHEL toolchain doesn't normally use static archives but only shared library objects. When using the shared library object a symbol definition will be resolved at runtime by loading the shared library. But DTS defines some (or all) symbols in core libraries through a linker script wrapper that pulls in symbols through archive. This is done to make sure an executable produced by the DTS toolchain can be used as is on base RHEL without the user needing to install DTS shared libraries just to run the executable.

This does mean that the user has to be careful that objects defining symbols are put on the linker (or compiler) command line after those objects that use those symbols. Most build systems (like automake) already make sure libraries used by a program are listed last on the command line.

An example is the following program that uses the elf_version symbol from the libelf.so library.

#include <elf.h>
#include <libelf.h>

int
main (int argc, char **argv)
{
  return (elf_version (EV_CURRENT) == EV_CURRENT) ? 0 : 1;
}

With the base RHEL toolchain this could be build as follows:

$ gcc -lelf -o test_elf test_elf.c

This produces a executable that dynamically links against libelf.so and resolves the elf_version symbol at runtime.

But with the DTS toolchain the libelf.so file is actually a linker wrapper script that pulls in some symbol definitions through an static archive.

So having the -lelf argument too early on the command line will result in:

$ scl enable devtoolset-7 bash
$ gcc -lelf -o test_elf test_elf.c
/tmp/ccFlPTwb.o: In function `main':
test_elf.c:(.text+0x15): undefined reference to `elf_version'
collect2: error: ld returned 1 exit status

The correct way to build this program is to have the -lelf argument at the end.

$ gcc -o test_elf test_elf.c -lelf

This will work with both the base RHEL toolchain and the DTS toolchain.