ld64 LC_LOAD_DYLIB order is not deterministic when dylibs are added by LC_REEXPORT_DYLIB

Originator:mark
Number:rdar://15002251 Date Originated:2013-09-16
Status:Closed Resolved:2014-07-22
Product:Developer Tools Product Version:ld64-136 from Xcode 4.6.3 4H1503
Classification:Serious Bug Reproducible:Always
 
Summary:
ld64 LC_LOAD_DYLIB order is not deterministic when dylibs are added by LC_REEXPORT_DYLIB

When ld64 makes its output depend on dylibs found by LC_REEXPORT_DYLIB, the resulting output’s LC_LOAD_DYLIB load commands will not appear in a deterministic order. This impacts application initialization order and symbol resolution with dlsym(RTLD_DEFAULT) by making both of these operations nondeterministic as well.

In the example below, the program is linked against Foundation and CoreServices. Foundation contains LC_REEXPORT_DYLIB load commands for libobjc and CoreFoundation. CoreServices also contains an LC_REEXPORT_DYLIB for libobjc, among other things that are not relevant here.

In the linked output, the order of LC_LOAD_DYLIB load commands for libobjc and CoreFoundation is nondeterministic. libobjc usually appears first, but not always.

mark@cougar bash$ cat test.m
#import <Foundation/Foundation.h>

int main() {
  NSArray* a = @[@"1"];
  return [a count];
}
mark@cougar bash$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.8.5
BuildVersion:	12F37
mark@cougar bash$ xcodebuild -version
Xcode 4.6.3
Build version 4H1503
mark@cougar bash$ clang --version
Apple LLVM version 4.2 (clang-425.0.28) (based on LLVM 3.2svn)
Target: x86_64-apple-darwin12.5.0
Thread model: posix
mark@cougar bash$ ld -v
@(#)PROGRAM:ld  PROJECT:ld64-136
configured to support archs: armv6 armv7 armv7s i386 x86_64
LTO support using: LLVM version 3.2svn, from Apple Clang 4.2 (build 425.0.28)
mark@cougar bash$ clang test.m -o test -framework Foundation -framework CoreServices
mark@cougar bash$ otool -L test | tail -2
	/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
	/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 744.19.0)
mark@cougar bash$ clang test.m -o test -framework Foundation -framework CoreServices
mark@cougar bash$ otool -L test | tail -2
	/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 744.19.0)
	/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
mark@cougar bash$ clang test.m -o test -framework Foundation -framework CoreServices
mark@cougar bash$ otool -L test | tail -2
	/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
	/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 744.19.0)
mark@cougar bash$ 

Note that libobjc appears before CoreFoundation after the first and third builds, but the order is inverted after the second build.

Steps to Reproduce:
1. 

cat > test.m << __EOF__
#import <Foundation/Foundation.h>

int main() {
  NSArray* a = @[@"1"];
  return [a count];
}
__EOF__

2. Repeat running these two commands multiple times

clang test.m -o test -framework Foundation -framework CoreServices
otool -L test

Expected Results:
The output of the otool -L command should be consistent and not change even though test.m has been recompiled and relinked. The order of all LC_LOAD_DYLIB commands in the linker output should be deterministic.

Actual Results:
The output of otool -L shows that the LC_LOAD_DYLIB commands are not being added to test in a deterministic order. In this example, the order of CoreFoundation and libobjc is not deterministic.

Notes:
Workaround: specify all linker dependencies to ld64 directly, instead of relying on it determining them by reading LC_REEXPORT_DYLIB load commands. This is impractical.

This bug appears to have originated when threaded input file reading was added to ld64. Threads showed up in ld64 src/ld/InputFiles.cpp in ld64-133.3 (Xcode 4.4). They aren’t there in ld64-128.2 (Xcode 4.3).

Comments

07-Aug-2014 02:00 PM

I believe that this is fixed in Xcode 6 beta 5 (6A279r). Thank you!


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!