[gPXE-devel] Weak pxe_menu_boot()
Stefan Hajnoczi
stefanha at gmail.com
Fri Jul 9 06:29:29 EDT 2010
On Sat, Jul 3, 2010 at 6:47 PM, Stefan Hajnoczi <stefanha at gmail.com> wrote:
> Here is the objdump -dr bin/autoboot.o output we want (using the glibc
> weak alias technique):
>
> 20e: e8 fc ff ff ff call 20f <netboot+0xe2>
> 20f: R_386_PC32 printf
> 213: 89 f0 mov %esi,%eax
> 215: e8 fc ff ff ff call 216 <netboot+0xe9>
> 216: R_386_PC32 pxe_menu_boot
>
> Notice that pxe_menu_boot is a relocation because at this point the
> toolchain doesn't know whether or not to use the weak symbol yet.
>
> Here is the broken objdump -dr bin/autoboot.o output we currently have
> in gpxe.git:
>
> 00000000 <pxe_menu_boot>:
> 0: b8 fd 1f fc c3 mov $0xc3fc1ffd,%eax
> 5: c3 ret
> [...]
> 20d: e8 fc ff ff ff call 20e <netboot+0xe1>
> 20e: R_386_PC32 printf
> 212: bf fd 1f fc c3 mov $0xc3fc1ffd,%edi
>
> The compiler has inlined pxe_menu_boot, there is no relocation!
>
> Here is what readelf -a bin/autoboot.o says about the broken autoboot.o:
> Num: Value Size Type Bind Vis Ndx Name
> 23: 00000000 6 FUNC WEAK HIDDEN 1 pxe_menu_boot
>
> I don't see anything particularly interesting in this readelf output
> but I put it here for completeness.
>
> So it seems that gcc inlined the function when it shouldn't have.
> Here is what happens when CFLAGS += -Os is changed to CFLAGS += -O in
> Makefile.housekeeping:
>
> 2ad: e8 fc ff ff ff call 2ae <netboot+0xf0>
> 2ae: R_386_PC32 printf
> 2b2: 89 f0 mov %esi,%eax
> 2b4: e8 fc ff ff ff call 2b5 <netboot+0xf7>
> 2b5: R_386_PC32 pxe_menu_boot
>
> Suddenly there is a relocation for pxe_menu_boot again!
>
> Compiler bug or do we not understand __attribute__ ((weak)) properly?
> The plot thickens...
Richard Sandiford identified the issue: the compiler inlines
pxe_menu_boot() because the symbol has visibility "hidden". From
include/compiler.h:
/* Force visibility of all symbols to "hidden", i.e. inform gcc that
* all symbol references resolve strictly within our final binary.
* This avoids unnecessary PLT/GOT entries on x86_64.
*
* This is a stronger claim than specifying "-fvisibility=hidden",
* since it also affects symbols marked with "extern".
*/
#ifndef ASSEMBLY
#if __GNUC__ >= 4
#pragma GCC visibility push(hidden)
#endif
#endif /* ASSEMBLY */
So although the function is extern, the pxe_menu_boot symbol has
visibility "hidden" and the compiler decides inlining is okay, despite
it being a weak symbol. Add __attribute__((visibility("default"))) to
pxe_menu_boot() to test this.
It seems to me that gcc interprets "hidden" as "all symbol references
resolve strictly within the compilation unit" rather than "all symbols
references resolve strictly within our final binary". Whether or not
gcc changes its behavior, we need to solve this for gPXE so that it
works on existing compilers.
Stefan
More information about the gPXE-devel
mailing list