linkerldbinutilslinker-scriptsgold-linker

Referring to a specific symbol in a static library with the GNU gold linker


When laying out symbols in the address space using a linker script, ld allows to refer to a specific symbol coming from a static library with the following syntax:

archive.a:object_file.o(.section.symbol_name)

Using gold rather than ld, it seems that such a directive is ignored. The linking process succeeds. However, when using this instruction to put a specific symbol at a specific location with gold and checking the resulting symbol layout using nm or having a look at the Map file, the symbol is not in the expected location.

I made a small test case using a dummy hello world program statically compiled in its entrety with gcc 5.4.0. The C library is musl libc (last commit on the master branch from the official git repository). For binutils, I also use the last commit on the master branch from the official git repository.

I use the linker script to place a specific symbol (.text.exit) from a static library (musl C library: libc.a) at a specific location in the address space which is: the first position in the .text section.

My linker script is:

ENTRY(_start)
SECTIONS
{
    . = 0x10000;
    .text :
    {
        /* Forcing .text.exit in the first position in .text section */
        musl/lib/libc.a:exit.o(.text.exit);
        *(.text*);
    }
    . = 0x8000000;
    .data : { *(.data*) }
    .rodata : { *(.rodata*) }
    .bss : { *(.bss*) }
}

My Makefile:

# Set this to 1 to link with gold, 0 to link with ld
GOLD=1

SRC=test.c
OBJ=test.o
LIBS=musl/lib/crt1.o \
    musl/lib/libc.a \
    musl/lib/crtn.o
CC=gcc
CFLAGS=-nostdinc -I musl/include -I musl/obj/include
BIN=test
LDFLAGS=-static
SCRIPT=linker-script.x
MAP=map

ifeq ($(GOLD), 1)
LD=binutils-gdb/gold/ld-new
else
LD=binutils-gdb/ld/ld-new
endif

all:
    $(CC) $(CFLAGS) -c $(SRC) -o $(OBJ)
    $(LD) --output $(BIN) $(LDFLAGS) $(OBJ) $(LIBS) -T $(SCRIPT) \
        -Map $(MAP)

clean:
    rm -rf $(OBJ) $(BIN) $(MAP)

After compiling and linking I'm checking the map file (obtained using the -Map ld/gold flag) to have a look at the location of .text.exit. Using ld as the linker, it is indeed in the first position of the text section. Using gold, it is not (it is present farther in the address space, as if my directive was not taken into account).

Now, while neither of these work with gold:

musl/lib/libc.a:exit.o(.text.exit);
musl/lib/libc.a(.text.exit)

This works:

*(.text.exit);

Is that a missing feature in gold? or am I doing something wrong, maybe there is another way to refer to a specific symbol of a specific object file in an archive using gold?


Solution

  • When laying out symbols in the address space using a linker script, ld allows to refer to a specific symbol coming from a specific object file inside a static library with the following syntax:

    archive.a:object_file.o(.section.symbol_name)

    That isn't quite what that syntax means. When you see ".section.symbol_name" in the linker script (or in a readelf or objdump list of sections), that is the whole name of the section, and you'll only see sections with names like that if you use the -ffunction-sections option when compiling. Given that your script works with ld, and if you just use the full filename wild card with gold, it looks like your musl libraries were indeed compiled with -ffunction-sections, but that's not something you can always assume is true for system libraries. So the linker isn't really searching for a section named ".text" that defines a symbol named "exit" -- instead, it's simply looking for a section named ".text.exit". Subtle difference, but you should be aware of it.

    Now, while neither of these work with gold: musl/lib/libc.a:exit.o(.text.exit); musl/lib/libc.a(.text.exit);

    This works: *(.text.exit);

    Is that a missing feature in gold? or am I doing something wrong, maybe there is another way to refer to a specific symbol of a specific object file in an archive using gold?

    If you look at the resulting -Map output file, I suspect you'll see the name of the object file is written as "musl/lib/libc.a(exit.o)". That's the spelling you need to use in the script, and because of the parentheses, you need to quote it. This:

    "musl/lib/libc.a(exit.o)"(.text.exit)
    

    should work. If you want something that will work in both linkers, try something like this:

    "musl/lib/libc.a*exit.o*"(.text.exit)
    

    or just

    "*exit.o*"(.text.exit)