4

I'm experimenting with symbol visibility in shared libraries using gcc and clang (both seem to exhibit the same behavior as far as this problem is concerned). The motivation for this question is to ensure that a particular symbol is never exposed in the shared library. Imagine a function with a sensitive name that must remain secret and that function is used internally within the shared library. Things are doing what I expect on Linux, but on macOS I can't seem to ensure a symbol is indeed kept completely hidden.

I've distilled the problem down to the following minimal demonstrator. The real scenarios would use flags like -fvisibility etc. rather than explicitly putting attributes on every symbol, but I've done it this way just to minimise the example (i.e. don't focus on the style, just the behavior).

f.c

__attribute__ ((visibility("default"))) int func(int v)
{
    return v + 14;
}


__attribute__ ((visibility("hidden"))) double some_other_func()
{
    return 1.23456;
}

Compile this with the following command line (shown for macOS, use libf.so for Linux):

gcc -shared -fPIC -o libf.dylib f.c

I would expect the some_other_func() function to be hidden, but using nm I can confirm that it still appears in the text section as a global/external symbol (uppercase T rather than lowercase t, the latter being a local symbol that could be stripped out):

# macOS
> nm -DC lib.dylib
0000000000000f80 T func
0000000000000fa0 T some_other_func
                 U dyld_stub_binder

But on Linux, we get what we expect with some_other_func() not showing up:

# Linux
> nm -DC libf.so
0000000000201020 B __bss_start
                 w __cxa_finalize
0000000000201020 D _edata
0000000000201028 B _end
0000000000000598 T _fini
000000000000057a T func
                 w __gmon_start__
0000000000000460 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable

In both cases, if you write an application that tries to call some_other_func(), the linker doesn't let you (it still gives you an undefined symbol on both macOS and Linux), so the problem isn't with preventing the linker from using the symbol. My problem is trying to hide the symbol completely, but it's showing up in the text section and revealing the name.

I'm using compilers from Xcode 10.3:

> gcc --version
Configured with: --prefix=/Applications/Xcode-10.3.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode-10.3.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/usr/include/c++/4.2.1
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.6.0
Thread model: posix
InstalledDir: /Applications/Xcode-10.3.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
Craig Scott
  • 9,238
  • 5
  • 56
  • 85
  • Why not just `strip` the compiled dylib? – l'L'l Jul 27 '19 at 13:27
  • Because it is a global rather than local symbol, it seems I can't strip it (at least, not in a way that I can find that doesn't render the library unable to be linked to). – Craig Scott Jul 27 '19 at 13:37
  • It also shouldn't be necessary to strip it. The point of the visibility attributes is to prevent the symbol from being visible in the first place. – Craig Scott Jul 27 '19 at 13:48
  • 2
    I can't reproduce this issue, but also the `nm` that I'm using on macOS (clang-1001.0.46.4) doesn't include `-D` (dynamic) or `-C` (demangle) options, so I'm curious if you're using the same commands. `nm libf.dylib` includes `t` for _some_other_func as expected. I'm also using Xcode 10.3 on macOS 10.14 – Rob Napier Jul 27 '19 at 18:09
  • 2
    @RobNapier Thanks, that was the clue I needed. Turns out the `nm` and `strip` I'd been using were coming from brew's `binutils` package. If I use the ones from `/usr/bin` instead (which uses the ones from Xcode), then the hidden function shows up as `t` and it can be stripped out using `/usr/bin/strip -x libf.dylib`. If you'd like the credit, feel free to put it into the form of an answer, otherwise I'll do that myself in a couple of days. – Craig Scott Jul 27 '19 at 21:12
  • Also see [Hiding symbols in a shared library on Mac OS X](https://stackoverflow.com/q/13793325/608639). GCC and Clang have different behaviors in C++, but I don't recall seeing it in C. – jww Jul 28 '19 at 00:48
  • @CraigScott: Unless I’m missing something here, I thought you said `strip` was not an option... interesting. – l'L'l Jul 28 '19 at 04:30
  • While the symbol was appearing as a global symbol, it wasn't possible to strip it without also stripping other things that needed to remain (well, not conveniently). Now that I'm using the right `nm` and `strip`, that becomes possible and straightforward to do. My expectation that stripping shouldn't be needed at all was based on the Linux output, but if I omit the `-DC` options, it shows there too. So yes, stripping is ultimately needed, but it first has to be possible. ;) – Craig Scott Jul 28 '19 at 05:07

0 Answers0