I think I have a testcase where ld --gc-sections erroneously discards a section. I'd appreciate if someone will take a look at it. You can download it from http://busybox.net/~vda/ksymtab_bug.tar.bz2 This testcase is adapted from Linux kernel build. Kernel image is linked using custom linker script. It must contain some special sections needed for dynamic loading of external modules at runtime. One such section is __ksymtab. It seems that in this testcase ld does not follow instructions given to it in custom linker script (arch_x86_64_kernel_vmlinux.lds). For some reason __ksymtab___const_udelay and __ksymtab_synchronize_irq symbols are not included into zz_vmlinux_bad, while other __ksymtab_* symbols are included, as intended (for example, __ksymtab___udelay). --print-gc-sections does not report them as discarded, and they shouldn't be, because they are coming from __ksymtab input sections, and arch_x86_64_kernel_vmlinux.lds has KEEP directive for those: __ksymtab : { KEEP(*(__ksymtab)) } [in fact, zz_discarded.lst does not indicate any of __ksymtab sections as discarded] __ksymtab___const_udelay is in arch_x86_64_lib_lib.a, __ksymtab_synchronize_irq is in kernel_built-in.o, so it doesn't seem to be caused by .a versus .o difference. __ksymtab___const_udelay and __ksymtab_synchronize_irq are specific to this particular set of .o/.a files. With slightly different kernel .config, other __ksymtab_XXX sections will be erroneously discarded. There is no apparent pattern in which ones will be affected. # x86_64-pc-linux-gnu-ld -v GNU ld (GNU Binutils) 2.18 # Bad link: x86_64-pc-linux-gnu-ld -m elf_x86_64 --gc-sections --print-gc-sections \ --build-id \ -o zz_vmlinux_bad \ -Map zz_link_bad.map -T arch_x86_64_kernel_vmlinux.lds \ \ arch_x86_64_kernel_head.o arch_x86_64_kernel_head64.o \ arch_x86_64_kernel_init_task.o init_built-in.o \ --start-group \ usr_built-in.o arch_x86_64_kernel_built-in.o arch_x86_64_mm_built-in.o \ arch_x86_64_crypto_built-in.o arch_x86_64_vdso_built-in.o \ arch_x86_64_ia32_built-in.o kernel_built-in.o mm_built-in.o \ fs_built-in.o ipc_built-in.o security_built-in.o crypto_built-in.o \ block_built-in.o lib_lib.a arch_x86_64_lib_lib.a lib_built-in.o \ arch_x86_64_lib_built-in.o drivers_built-in.o sound_built-in.o \ arch_x86_64_pci_built-in.o net_built-in.o \ --end-group \ .tmp_kallsyms3.o \ 2>zz_discarded.lst # Here you will see __ksymtab___udelay but no __ksymtab___const_udelay objdump -x zz_vmlinux_bad | grep 'ksymtab[a-z_]*udelay' echo objdump -x zz_vmlinux_bad | grep '_synchronize_irq' echo # If one removes --gc-sections, link works: x86_64-pc-linux-gnu-ld -m elf_x86_64 \ --build-id \ -o zz_vmlinux_good \ -Map zz_link_good.map -T arch_x86_64_kernel_vmlinux.lds \ \ arch_x86_64_kernel_head.o arch_x86_64_kernel_head64.o \ arch_x86_64_kernel_init_task.o init_built-in.o \ --start-group \ usr_built-in.o arch_x86_64_kernel_built-in.o arch_x86_64_mm_built-in.o \ arch_x86_64_crypto_built-in.o arch_x86_64_vdso_built-in.o \ arch_x86_64_ia32_built-in.o kernel_built-in.o mm_built-in.o \ fs_built-in.o ipc_built-in.o security_built-in.o crypto_built-in.o \ block_built-in.o lib_lib.a arch_x86_64_lib_lib.a lib_built-in.o \ arch_x86_64_lib_built-in.o drivers_built-in.o sound_built-in.o \ arch_x86_64_pci_built-in.o net_built-in.o \ --end-group \ .tmp_kallsyms3.o # Here you will see both __ksymtab___udelay and __ksymtab___const_udelay objdump -x zz_vmlinux_good | grep 'ksymtab[a-z_]*udelay' echo objdump -x zz_vmlinux_good | grep '_synchronize_irq' echo Sample output: ffffffff806e0f00 l O __ksymtab 0000000000000010 __ksymtab___udelay (__ksymtab___const_udelay should be listed too, but it isnt) ffffffff806eb590 l O __kcrctab 0000000000000008 __kcrctab_synchronize_irq ffffffff806f4b20 l O __ksymtab_strings 0000000000000010 __kstrtab_synchronize_irq 00000000e523ad75 g *ABS* 0000000000000000 __crc_synchronize_irq ffffffff806e7050 l O __ksymtab 0000000000000010 __ksymtab___const_udelay ffffffff806e7060 l O __ksymtab 0000000000000010 __ksymtab___udelay ffffffff806f16f0 l O __kcrctab 0000000000000008 __kcrctab_synchronize_irq ffffffff806e3b90 l O __ksymtab 0000000000000010 __ksymtab_synchronize_irq ffffffff806fac80 l O __ksymtab_strings 0000000000000010 __kstrtab_synchronize_irq 00000000e523ad75 g *ABS* 0000000000000000 __crc_synchronize_irq
Looks like alignment padding for .bss is being written to the file
and overwriting the symbol table.
Alignment padding should not be written for .bss, but it is because the output .bss has SEC_HAS_CONTENTS set. See ldwite.c:build_link_order <case lang_padding_statement_enum>. SEC_HAS_CONTENTS is set because arch_x86_64_kernel_built-in.o .bss_page_aligned is SHT_PROGBITS. This is wrong. A bss style section should be SHT_NOBITS. However, linking a SHT_PROGBITS section into .bss should simply make .bss SHT_PROGBITS. It doesn't, because .bss has its ELF section type set via bfd_elf_special_section to SHT_NOBITS too early.
http://sourceware.org/ml/binutils-cvs/2007-09/msg00030.html http://sourceware.org/ml/binutils-cvs/2007-09/msg00031.html
Thanks for incredibly fast analysis and bugfix! I looked into how .bss_page_aligned got progbits in first place. GCC seems to infer nobits v. progbits based on *section name*! Oh crap... char boot_cpu_stack[IRQSTACKSIZE] __attribute__((section(".bss.page_aligned"))); gets nobits, char boot_cpu_stack[IRQSTACKSIZE] __attribute__((section(".bss_page_aligned"))); gets progbits. Somehow it doesn't feel like the Right Thing to do. Jakub Jelinek <jakub at redhat dot com> already was pointing this out here http://gcc.gnu.org/ml/gcc-patches/2006-08/msg00258.html Anyway, I will be able to make it work now. Thanks again!