Bug 14037 - Bogus global symbol ABS redefinition when linking PIC app to eh_frame-stripped shared library
Summary: Bogus global symbol ABS redefinition when linking PIC app to eh_frame-strippe...
Status: NEW
Alias: None
Product: binutils
Classification: Unclassified
Component: binutils (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-05-01 02:13 UTC by Rich Felker
Modified: 2012-05-01 18:37 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Rich Felker 2012-05-01 02:13:34 UTC
When the following 3 conditions are satisfied:

1. Main program is built with -fPIC.
2. Main program uses global variables from the shared library.
3. Shared library has been stripped with the command:
strip --strip-unneeded -R .eh_frame -R .eh_frame_hdr

the GNU binutils linker generates incorrect symbol definitions for the affected global variable. Instead of generating a copy relocation (which it would do for non-PIC main program) or leaving the symbol undefined in the main program so that both the main program and shared library's GOT entries for the variable get resolved to the original copy of the variable in the shared library, it generates a nonsensical ABSOLUTE symbol definition in the main program whose value is the library-load-address-relative address of the variable in the shared library. For example, instead of (readelf output for the application):

     5: 00000000     0 OBJECT  GLOBAL DEFAULT  UND global

we get:

     5: 000015f8     4 OBJECT  GLOBAL DEFAULT  ABS global

This in turn causes either the main app or the library to crash when trying to access the global variable, since both GOT entries get resolved to this invalid absolute address.

At least x86 and x86_64 are affected.

I encountered this issue when systems integrators using musl (my libc) were trying to use the above strip command to debloat their libc file on disk. I believe it's a valid optimization since musl does not use unwind tables whatsoever, and in fact at first I suspected this was a bug on my side (in my dynamic linker code perhaps), but I've created a minimal test case that reproduces the issue with just binutils.

libfoo.c:

int global;
void setit(int x) { global = x; }

app.c:

void setit(int);
extern int global;
int main() { setit(42); return global; }

build:

gcc -fPIC -g libfoo.c -shared -o libfoo.so
strip -R .eh_frame -R .eh_frame_hdr libfoo.so
gcc -fPIC app.c -L. -lfoo

Compare results with and without the strip command to see the bug.
Comment 1 Alan Modra 2012-05-01 07:29:40 UTC
On the face of it this appears to be a strip/objcopy bug.  "global" is defined in .bss, section number 22 with my version of x86-64 gcc.  .eh_frame_hdr and .eh_frame are sections 13 and 14.  When they are removed, the dynamic symbol table is not updated.  "global" stays defined in section 22, which is now .shstrtab, an entirely different type of section.  This confuses the linker.

However, the documentation for objcopy -R and strip -R says "Note that using this option inappropriately may make the output file unusable".  Which is exactly what you've done.

If you want to strip sections, do so at link time using a linker script with /DISCARD/.
Comment 2 Rich Felker 2012-05-01 14:18:18 UTC
Thanks for getting to the bottom of this so quickly. That makes it a strip bug, then. If strip is going to renumber the sections, it needs to fix the section numbers in the symbol table too. Or it could just leave the section numbers alone and replace the stripped sections with dummy zero-length sections with unobtrusive names to avoid the problem.
Comment 3 Rich Felker 2012-05-01 18:37:16 UTC
Another way of looking at the issue is that, if strip is going to break things like this, the default linker scripts should order the sections such that mandatory sections like text/data/bss all come before (in the section table, not necessarily in the file) optional sections like dwarf2 unwind tables rather than interspersed.
Comment 4 Rich Felker 2012-05-01 18:37:16 UTC
Another way of looking at the issue is that, if strip is going to break things like this, the default linker scripts should order the sections such that mandatory sections like text/data/bss all come before (in the section table, not necessarily in the file) optional sections like dwarf2 unwind tables rather than interspersed.