Linker: Weak symbol preferred over strong in static lib

Asked by Lars Rademacher

The arm gcc linker seems to prefer weak symbols over strong symbols, in case both are in the same static library.

This was checked for the Windows versions 4.7.4 20130913 and 4.8.4 20140526.

The following example shall show that the MinGW gcc (version 4.8.1) and the arm gcc behave differently in that concern:

We build a library with just two functions with the same name, but one of them is defined to have a weak symbol. The function is called from the main.c which is linked with the library. Normally, the strong version of both symbols should be chosen. This can be checked by analyzing the disassembly. If bar() calls weak(), the weak symbol was chosen and otherwise the strong symbol was chosen.

I found, that MinGW chooses the strong symbol but the arm-gcc chooses the weak one.

Is this a bug, or do I misunderstand something?

== Makefile ==
----- BEGIN -----
# TC = armgcc_4_8_q2\bin\arm-none-eabi-
TC =

all:
 $(TC)gcc -c bar.c
 $(TC)gcc -c foo.c
 $(TC)ar rvs libbar.a foo.o bar.o
 $(TC)gcc main.c -L. -lbar -o test.exe
----- END -----

== bar.c ==
----- BEGIN -----
void strong();

void bar()
{
 strong();
}
----- END -----

== foo.c ==
----- BEGIN -----
void weak();

__attribute__((weak)) void bar()
{
    weak();
}
----- END -----

== main.c ==
----- BEGIN -----
void bar();

void main()
{
    bar();
}

void weak(){}

void strong (){}
----- END -----

Question information

Language:
English Edit question
Status:
Solved
For:
GNU Arm Embedded Toolchain Edit question
Assignee:
No assignee Edit question
Solved by:
Lars Rademacher
Solved:
Last query:
Last reply:
Revision history for this message
Terry Guo (terry.guo) said :
#1

Interesting issue. I am looking into this. Theoretically both tool chain should have same behavior.

Revision history for this message
Terry Guo (terry.guo) said :
#2

Sorry that I can't reproduce this issue. I observed consistent behavior between windows tool chain and Linux tool chain. The way to choose bar() function depends on how the libbar.a is produced, specifically the order of object files. If libbar.a is built with:

$(TC)ar rvs libbar.a foo.o bar.o

The bar() from foo.o will be used. If libbar.a is built with

$(TC)ar rvs libbar.a bar.o foo.o

The bar() from bar.o will be used.

Please try to link them with command like "arm-none-eabi-gcc main.c -L. -lbar -o test.exe -specs=rdimon.specs -Wl,-Map=a.map". The a.map will tell which bar() will be used. Also please remember to delete the old libbar.a before rebuild it.

Revision history for this message
Lars Rademacher (lars-rademacher-g) said :
#3

Thank you for working on the problem.

I seem to have forgotten to delete the library one time, so on arm gcc i now see, that the strong symbol gets linked when bar.o is first in the link list.

I want the linker to always link the strong symbol and this is what MinGW does!
No matter about the link order, the (strong) bar function from bar.o always gets linked.
Is this some special feature about windows x86 gcc or am I doing something wrong?

Revision history for this message
Terry Guo (terry.guo) said :
#4

Which windows x86 gcc are you using? Can you provide a download link? I want to reproduce what you mentioned. Thanks.

Revision history for this message
Lars Rademacher (lars-rademacher-g) said :
#5

MinGW gcc version is this:
===
>c:\Apps\MinGW\bin\gcc --version -v
Using built-in specs.
COLLECT_GCC=c:\Apps\MinGW\bin\gcc
COLLECT_LTO_WRAPPER=c:/apps/mingw/bin/../libexec/gcc/mingw32/4.8.1/lto-wrapper.exe
gcc (GCC) 4.8.1
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Target: mingw32
Configured with: ../gcc-4.8.1/configure --prefix=/mingw --host=mingw32 --build=mingw32 --without-pic --enable-shared --enable-static --with-gnu-ld --enable-lto --enable-libssp --disable-multilib --enable-languages=c,c++,fortran,objc,obj-c++,ada --disable-sjlj-exceptions --with-dwarf2 --disable-win32-registry --enable-libstdcxx-debug --enable-version-specific-runtime-libs --with-gmp=/usr/src/pkg/gmp-5.1.2-1-mingw32-src/bld --with-mpc=/usr/src/pkg/mpc-1.0.1-1-mingw32-src/bld --with-mpfr= --with-system-zlib --with-gnu-as --enable-decimal-float=yes --enable-libgomp --enable-threads --with-libiconv-prefix=/mingw32 --with-libintl-prefix=/mingw --disable-bootstrap LDFLAGS=-s CFLAGS=-D_USE_32BIT_TIME_TThread model: win32
gcc version 4.8.1 (GCC)
COLLECT_GCC_OPTIONS='--version' '-v' '-mtune=generic' '-march=pentiumpro' c:/apps/mingw/bin/../libexec/gcc/mingw32/4.8.1/cc1.exe -quiet -v -iprefix c:\apps\mingw\bin\../lib/gcc/mingw32/4.8.1/ help-dummy -quiet -dumpbase help-dummy-mtune=generic -march=pentiumpro -auxbase help-dummy -version --version -o C:\Users\RADEMA~1\AppData\Local\Temp\ccaYcOfW.s
===

I don't see, if it is 4.8.1-1, -2, -3 or -4.

Download link for 4.8.1-4 can be found here: http://sourceforge.net/projects/mingw/files/MinGW/Base/gcc/Version4/gcc-4.8.1-4/

Revision history for this message
Joey Ye (jinyun-ye) said :
#6

This looks like an binutils bug, as strong symbol should be preferred over weak.

Revision history for this message
Joey Ye (jinyun-ye) said :
#7

After more search, it turns out to be the expected behavior.

"Gcc (actually the linker) treats libraries differently.
By default, it tries use the first found symbol and does not detect duplicated symbols."

The solution is to use in your example:
(TC)gcc main.c -L. -Wl,--whole-archive -lbar -Wl,--no-whole-archive -o test.exe

Please refer to http://winfred-lu.blogspot.jp/2009/11/understand-weak-symbols-by-examples.html for the whole detail.

- Joey

Revision history for this message
Lars Rademacher (lars-rademacher-g) said :
#8

This seems to work. Will test it in the productive software project.

Revision history for this message
Lars Rademacher (lars-rademacher-g) said :
#9

Thanks Joey Ye, that solved my question.

Revision history for this message
Lars Rademacher (lars-rademacher-g) said :
#10

I still have a problem.

The disassembly of the example code now looks like this:

[...]

0000824c <main>:
    824c: e92d4800 push {fp, lr}
    8250: e28db004 add fp, sp, #4
    8254: eb000012 bl 82a4 <bar>
    8258: e24bd004 sub sp, fp, #4
    825c: e8bd4800 pop {fp, lr}
    8260: e12fff1e bx lr

00008264 <weak>:
    8264: e52db004 push {fp} ; (str fp, [sp, #-4]!)
    8268: e28db000 add fp, sp, #0
    826c: e24bd000 sub sp, fp, #0
    8270: e49db004 pop {fp} ; (ldr fp, [sp], #4)
    8274: e12fff1e bx lr

00008278 <strong>:
    8278: e52db004 push {fp} ; (str fp, [sp, #-4]!)
    827c: e28db000 add fp, sp, #0
    8280: e24bd000 sub sp, fp, #0
    8284: e49db004 pop {fp} ; (ldr fp, [sp], #4)
    8288: e12fff1e bx lr
    828c: e92d4800 push {fp, lr}
    8290: e28db004 add fp, sp, #4
    8294: ebfffff2 bl 8264 <weak>
    8298: e24bd004 sub sp, fp, #4
    829c: e8bd4800 pop {fp, lr}
    82a0: e12fff1e bx lr

000082a4 <bar>:
    82a4: e92d4800 push {fp, lr}
    82a8: e28db004 add fp, sp, #4
    82ac: ebfffff1 bl 8278 <strong>
    82b0: e24bd004 sub sp, fp, #4
    82b4: e8bd4800 pop {fp, lr}
    82b8: e12fff1e bx lr

[...]

The code of the function strong() now looks kind of strange. After the expected code (8278-8288), there seems to be an alternative implementation for the main function, for calling the weak function (828c-82a0).

What is going on here? I deleted all object-files and libs before compiling.

This is the linker call: $(TC)gcc main.c -L. -Wl,--whole-archive -lbar -Wl,--no-whole-archive -o test.exe -specs=rdimon.specs

Revision history for this message
Joey Ye (jinyun-ye) said :
#11

It is actually the weak version of bar (It calls weak, not bar). Since its
symbol is rejected it end up with no symbol and looks like appending to
strong. To remove this useless code, please use option -Wl,--gc-sections

- Joey

On Wed, Aug 6, 2014 at 4:27 PM, Lars Rademacher <
<email address hidden>> wrote:

> Question #251943 on GCC ARM Embedded changed:
> https://answers.launchpad.net/gcc-arm-embedded/+question/251943
>
> Status: Solved => Open
>
> Lars Rademacher is still having a problem:
> I still have a problem.
>
> The disassembly of the example code now looks like this:
>
> [...]
>
> 0000824c <main>:
> 824c: e92d4800 push {fp, lr}
> 8250: e28db004 add fp, sp, #4
> 8254: eb000012 bl 82a4 <bar>
> 8258: e24bd004 sub sp, fp, #4
> 825c: e8bd4800 pop {fp, lr}
> 8260: e12fff1e bx lr
>
> 00008264 <weak>:
> 8264: e52db004 push {fp} ; (str fp, [sp,
> #-4]!)
> 8268: e28db000 add fp, sp, #0
> 826c: e24bd000 sub sp, fp, #0
> 8270: e49db004 pop {fp} ; (ldr fp, [sp],
> #4)
> 8274: e12fff1e bx lr
>
> 00008278 <strong>:
> 8278: e52db004 push {fp} ; (str fp, [sp,
> #-4]!)
> 827c: e28db000 add fp, sp, #0
> 8280: e24bd000 sub sp, fp, #0
> 8284: e49db004 pop {fp} ; (ldr fp, [sp],
> #4)
> 8288: e12fff1e bx lr
> 828c: e92d4800 push {fp, lr}
> 8290: e28db004 add fp, sp, #4
> 8294: ebfffff2 bl 8264 <weak>
> 8298: e24bd004 sub sp, fp, #4
> 829c: e8bd4800 pop {fp, lr}
> 82a0: e12fff1e bx lr
>
> 000082a4 <bar>:
> 82a4: e92d4800 push {fp, lr}
> 82a8: e28db004 add fp, sp, #4
> 82ac: ebfffff1 bl 8278 <strong>
> 82b0: e24bd004 sub sp, fp, #4
> 82b4: e8bd4800 pop {fp, lr}
> 82b8: e12fff1e bx lr
>
> [...]
>
> The code of the function strong() now looks kind of strange. After the
> expected code (8278-8288), there seems to be an alternative
> implementation for the main function, for calling the weak function
> (828c-82a0).
>
> What is going on here? I deleted all object-files and libs before
> compiling.
>
> This is the linker call: $(TC)gcc main.c -L. -Wl,--whole-archive -lbar
> -Wl,--no-whole-archive -o test.exe -specs=rdimon.specs
>
> --
> You received this question notification because you are an answer
> contact for GCC ARM Embedded.
>

Revision history for this message
Lars Rademacher (lars-rademacher-g) said :
#12

Thank you, this solves the problem.