While trying to strip out unneeded things from my linked executable I've found some strange thing. Assume we have a simple, straightforward C++ program:
class Foo {
public:
template <typename T>
char* getPtr() {
static char c;
return &c;
}
};
char* bar() {
Foo i;
return i.getPtr<int>();
}
int main() {
bar();
}
Binary being built with clang++ t.cc
has next dynamic symbol table:
$ gobjdump -T ./a.out
./a.out: file format mach-o-x86-64
DYNAMIC SYMBOL TABLE:
0000000100000f40 g 0f SECT 01 0000 [.text] __Z3barv
0000000100000f60 g 0f SECT 01 0080 [.text] __ZN3Foo6getPtrIiEEPcv
0000000100001020 g 0f SECT 08 0080 [.data] __ZZN3Foo6getPtrIiEEPcvE1c
0000000100000000 g 0f SECT 01 0010 [.text] __mh_execute_header
0000000100000f80 g 0f SECT 01 0000 [.text] _main
0000000000000000 g 01 UND 00 0200 dyld_stub_binder
Considering that it is an executable and not a dylib I would like to strip all entries except those for undefined symbols. Theoretically binary still will work since info about required dyld binding is still there and entry point is defined in some load command after mach-o header (so nothing to do with symbol table).
Trying strip
gives some weird result:
$ strip ./a.out
$ gobjdump -T ./a.out
./a.out: file format mach-o-x86-64
DYNAMIC SYMBOL TABLE:
0000000005614542 d 3c OPT 00 0000 radr://5614542
0000000100000f60 g 0f SECT 01 0080 [.text] __ZN3Foo6getPtrIiEEPcv
0000000100001020 g 0f SECT 08 0080 [.data] __ZZN3Foo6getPtrIiEEPcvE1c
0000000100000000 g 0f SECT 01 0010 [.text] __mh_execute_header
0000000000000000 g 01 UND 00 0200 dyld_stub_binder
Last two entries shouldn't be stripped anyway as they are required by dyld to handle this executable. At the same time we see that _main
and __Z3barv
are gone. But symbols from class Foo
are still there. The only difference between them and stripped ones is that former ones have N_WEAK_DEF
flag set (0080). Here is some scant info from <mach-o/nlist.h>
about that flag:
/*
* The N_WEAK_DEF bit of the n_desc field indicates to the static and dynamic
* linkers that the symbol definition is weak, allowing a non-weak symbol to
* also be used which causes the weak definition to be discared. Currently this
* is only supported for symbols in coalesed sections.
*/
#define N_WEAK_DEF 0x0080 /* coalesed symbol is a weak definition */
Unfortunately it doesn't explain why strip
ignores symbols with that flag.
So the question is - how to teach strip
to remove even N_WEAK_DEF
symbols in case user just doesn't want to export them.
P.S. I've looked into command options and haven't found anything useful (-N
removes undefined symbols too, so it is not an option). Declaring that class with visibility("hidden")
makes the puzzle, but unfortunately it is not easy to do with the real project.
MH_OBJECT
) file? Typically executables and shared libraries are linked by ld64 using a bytecode/compressed relocation format that dyld reads at start instead of nlist-based relocations which are more informative but are not used in final binaries unless explicitly requested. – Kristina BrooksLC_DYLD_INFO_ONLY
command with rebase and bind info in usual compressed form, but I haven't provide them since they should be irrelevant to my question. – Sergio