debug-symbolsobjcopy

Separating out symbols and stripping unneeded symbols at the same time


I am interested in creating an external symbol file which I reference from my ELF file using objcopy --add-gnu-debuglink=....

Now I know how to extract only the debug symbols (objcopy --only-keep-debug) and how to only strip the debug symbols (objcopy --strip-debug).

However, I'd like to strip all unneeded symbols. I know this can be done via objcopy --strip-unneeded. The fact that debug and unneeded symbols are two different categories and two different command line switches suggests that there could be non-overlapping symbols.

Since unneeded symbols seem to encompass more than just the debug symbols, I'm afraid to lose some symbolic information when doing this (in GNU make):

stripped/%: %
    objcopy --only-keep-debug $@ $@.dbg
    objcopy --strip-unneeded $@
    objcopy --add-gnu-debuglink=$@.dbg $@

Can anyone explain what kind of symbols may be removed via --strip-unneeded that may not be removed with --strip-debug? Or put differently: which symbols not kept in the external symbol file by --only-keep-debug may get lost when running --strip-unneeded on the original binary? Is there a way to retain these symbols in my external symbol file or is this a non-issue?

Goal is to be able to debug issues using the external debug symbols and not ending up missing some symbolic information and find out too late. So I don't want to lose symbolic information, I just want to remove it entirely from the program binary.

PS: I am aware of How to generate gcc debug symbol outside the build target? But that question and its answers don't touch the detail I am asking about.


Solution

  • I've just stumbled to the very same question. Previously I just separated my built ELF-binaries with the objcopy's --only-keep-debug, --strip-debug, --add-gnu-debuglink flags. Now I need to save even more space and so I'm considering using --strip-unneeded instead of --strip-debug. But like you I'm afraid that this may affect my debugging experience.

    So, I've made several tests and come to the following conclusions:

    1. --strip-unneeded strips what is stripped by --strip-debug and even more. I.e. 'debug' info is considered as a part of 'unneeded' info.
    2. Debug binaries created with the --only-keep-debug flag, not only store info stripped by the --strip-debug, but also info stripped by the --strip-unneeded.
    3. I've not noticed any difference in debugging against --strip-debug vs --strip-unneeded, provided the debug binaries created with the --only-keep-debug.

    Details below:

    I've created a very simple C++ project, which contains an executable and a shared library. The library contains a globally exported function, which is called by the application. Also the library contains several local functions (i.e. static or in the anonymous namespace), being called by the globally exported function. The code in the local functions just created a crash by just throwing an unhandled exception.

    First, I've compiled both binaries with the -g -O0 flags. Second, I've extracted the debug information from them in a separate binaries and linked these debug files to the original binaries. I.e., for both files:

    objcopy --only-keep-debug $FILE $FILE.debug
    objcopy --add-gnu-debuglink=$FILE.debug $FILE
    

    After this point I had unstripped binaries also having separate correspondent linked debug binaries.

    Then I've copied these files into two additional directories. In the first one I've done --strip-debug against the original binaries and in another I've done --strip-unneeded.

    Considering file sizes, the original files where obviously the biggest, the files in strip-unneeded dir where the smallest, and the files in the strip-debug dir were in the middle. Also, additionally running --strip-debug against the files in the strip-unneeded dir has not changed the file sizes, meaning that --strip-debug strips just some subset of what is stripped by --strip-unneeded.

    I've then compared section listing of all the three variants by running readelf -S against all of them. Looking at them, it could be seen that --strip-debug strips the following sections: .debug_arranges, .debug_info, .debug_abbrev, .debug_line and .debug_str, and also somewhat reduced the .symtab and .strtab sections. --strip-unneeded also additionally completely removes .symtab and .strtab sections.

    I've then run readelf -S against the debug binaries, which I'd got with the --only-keep-debug flag. The sections there had all the sections removed by --strip-unneeded. So, it not only contained .debug_arranges, .debug_info, .debug_abbrev, .debug_line and .debug_str, but also .symtab and .strtab. And the sizes of the sections were almost identical to their original sizes.

    I've then tried to step-by-step debug all the three variants and haven't noticed any difference between them. Also I've produced crashes and core dumps with all of them and then tried to debug against the core dumps - also no difference.

    Versions used: gcc (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609 GNU objcopy (GNU Binutils for Ubuntu) 2.26.1 GNU strip (GNU Binutils for Ubuntu) 2.26.1