Giovanni Beri
Giovanni Beri

Reputation: 21

Why is partial linking making all symbols non-global?

I'm trying to reduce the disk size of a big static library libIn.a that contains a lot (~3000) of small object files (.o), by combining all the object files into a single .o file; as far as I understand, this procedure is called "partial linking". The size reduction would be achieved by collapsing all the object sections (one per .o file) into a single one.

The problem I'm seeing is that the procedure I'm using does not preserve global-ness of the symbols included in the .o files included in libIn.a i.e., all symbols become local after the partial linking, and that causes "undefined symbol" errors downstream in the linking process.

As an example, that's how version.o object file (originally included in libIn.a, you can download version.o at https://github.com/giovanniberi93/problematic_object_file) looks like before performing partial linking:

└─ nm -Ca version.o
0000000000000000 T webrtc::LoadWebRTCVersionInRegister()
0000000000000024 s l_.str
0000000000000000 t ltmp0
0000000000000024 s ltmp1

So now the symbol webrtc::LoadWebRTCVersionInRegister() is global (T).

But when performing partial linking, the same symbol becomes local (t):

└─ ld -r version.o -o why_is_local.o

└─ nm -Ca why_is_local.o
0000000000000024 s LC1
0000000000000000 t webrtc::LoadWebRTCVersionInRegister()

Things get even weirder: when trying to replicate the same scenario with a sample .o file, the global symbols are not converted into local symbols (!); e.g., with input C++ file:

int function1() {
    return 1;
}

Its global symbol function1() is not converted into local symbol by performing partial linkage i.e., it stays global (T) before and after partial linking:

└─ clang -c file1.cc

└─ nm -Ca file1.o
0000000000000000 T function1()
0000000000000000 t ltmp0
0000000000000008 s ltmp1

└─ ld -r file1.o -o relocated.o

└─ nm -Ca relocated.o
0000000000000000 T function1()

There must be some difference in version.o and file1.o that is causing the global symbols to become local, but I've not been able to pinpoint it. Any input would be greatly appreciated.


My env (MacOS 12.6, arm64):

└─ clang -v
Apple clang version 13.0.0 (clang-1300.0.29.3)
Target: arm64-apple-darwin21.6.0
Thread model: posix
InstalledDir: /Users/giober/Desktop/XCodes/13.1/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

└─ ld -v
@(#)PROGRAM:ld  PROJECT:ld64-711
BUILD 18:11:19 Aug  3 2021
configured to support archs: armv6 armv7 armv7s arm64 arm64e arm64_32 i386 x86_64 x86_64h armv6m armv7k armv7m armv7em
LTO support using: LLVM version 13.0.0, (clang-1300.0.29.3) (static support for 27, runtime is 27)
TAPI support using: Apple TAPI version 13.0.0 (tapi-1300.0.6.5)

└─ nm --version
Apple LLVM version 13.0.0 (clang-1300.0.29.3)
  Optimized build.
  Default target: arm64-apple-darwin21.6.0
  Host CPU: vortex

Upvotes: 0

Views: 425

Answers (1)

Siguza
Siguza

Reputation: 23880

The symbol in question is marked as "private external":

% nm -m version.o 
0000000000000000 (__TEXT,__text) private external __ZN6webrtc27LoadWebRTCVersionInRegisterEv
0000000000000024 (__TEXT,__cstring) non-external l_.str
0000000000000000 (__TEXT,__text) non-external ltmp0
0000000000000024 (__TEXT,__cstring) non-external ltmp1

Not sure where that's coming from, but you can preserve this state with -keep_private_externs:

ld -keep_private_externs -r version.o -o output.o

Upvotes: 0

Related Questions