Quuxplusone / LLVMBugzillaTest

0 stars 0 forks source link

With LTO, dead stripped ObjC protocols can result in linker error #23006

Open Quuxplusone opened 9 years ago

Quuxplusone commented 9 years ago
Bugzilla Link PR23007
Status NEW
Importance P normal
Reported by Peter Collingbourne (peter@pcc.me.uk)
Reported on 2015-03-24 13:41:19 -0700
Last modified on 2021-06-21 11:41:55 -0700
Version trunk
Hardware PC All
CC dexonsmith@apple.com, hiraditya@msn.com, jsweval@arxan.com, kledzik@apple.com, llvm-bugs@lists.llvm.org, mwoodard@arxan.com, nicolasweber@gmx.de
Fixed by commit(s)
Attachments
Blocks
Blocked by
See also
On Darwin:

$ cat foo.mm
#import <Foundation/Foundation.h>

@protocol CrAppProtocol
- (bool)isHandlingSendEvent;
@end

bool foo(NSObject *obj) {
  return [obj conformsToProtocol:@protocol(CrAppProtocol)];
}
$ cat main.c
int main() {}
$ export DYLD_LIBRARY_PATH=$HOME/src/llvm-build-rel/lib
$ $HOME/src/llvm-build-rel/bin/clang -isysroot
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk
-c -flto foo.mm main.c
$ ar cru libfoo.a foo.o
$ $HOME/src/llvm-build-rel/bin/clang -isysroot
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk
-flto -Wl,-ObjC -Wl,-dead_strip main.o libfoo.a
ld: warning: '__Z3fooP8NSObject' is implemented in bitcode, but it was loaded
too late
Undefined symbols for architecture x86_64:
  "_objc_msgSend", referenced from:
      import-atom in libfoo.a(foo.o)
  "l_OBJC_PROTOCOL_REFERENCE_$_CrAppProtocol", referenced from:
      import-atom in libfoo.a(foo.o)
  "l_OBJC_LABEL_PROTOCOL_$_CrAppProtocol", referenced from:
      import-atom in libfoo.a(foo.o)
  "l_OBJC_PROTOCOL_$_CrAppProtocol", referenced from:
      l_OBJC_LABEL_PROTOCOL_$_CrAppProtocol in lto.o
      l_OBJC_PROTOCOL_REFERENCE_$_CrAppProtocol in lto.o
      import-atom in libfoo.a(foo.o)
      l_OBJC_LABEL_PROTOCOL_$_CrAppProtocol in libfoo.a(foo.o)
      l_OBJC_PROTOCOL_REFERENCE_$_CrAppProtocol in libfoo.a(foo.o)
ld: symbol(s) not found for architecture x86_64
clang-3.7: error: linker command failed with exit code 1 (use -v to see
invocation)

Reverting 209227 and 207979 in LLVM appears to fix this problem.
Quuxplusone commented 9 years ago

Note that reverting those patches only helps in an LLVM built without assertions, otherwise the assertion removed in r209227 fails.

Quuxplusone commented 9 years ago
There is something really odd going on here:

$ cat main.ll
target triple = "x86_64-apple-macosx10.10.0"
define i32 @main() {
  ret i32 0
}

$ cat foo.ll
target triple = "x86_64-apple-macosx10.10.0"
%0 = type opaque
%struct._objc_method = type { i8*, i8*, i8* }
%struct._protocol_t = type { i8*, i8*, %struct._objc_protocol_list*,
%struct.__method_list_t*, %struct.__method_list_t*, %struct.__method_list_t*,
%struct.__method_list_t*, %struct._prop_list_t*, i32, i32, i8** }
%struct._objc_protocol_list = type { i64, [0 x %struct._protocol_t*] }
%struct.__method_list_t = type { i32, i32, [0 x %struct._objc_method] }
%struct._prop_list_t = type { i32, i32, [0 x %struct._prop_t] }
%struct._prop_t = type { i8*, i8* }
@zed =  global %struct._protocol_t { i8* null, i8* null,
%struct._objc_protocol_list* null, %struct.__method_list_t* null,
%struct.__method_list_t* null, %struct.__method_list_t* null,
%struct.__method_list_t* null, %struct._prop_list_t* null, i32 80, i32 0, i8**
null }, section "__DATA,__datacoal_nt,coalesced", align 8
@bar = global %0* bitcast (%struct._protocol_t* @zed to %0*), section "__DATA,
__objc_protorefs, coalesced, no_dead_strip"
@llvm.compiler.used = appending global [1 x i8*] [  i8* bitcast (%0** @bar to
i8*)], section "llvm.metadata"
define zeroext i1 @_Z3fooP8NSObject() {
  ret i1 false
}

$ export DYLD_LIBRARY_PATH=/Users/espindola/llvm/build/lib/
$ ./build/bin/llvm-as foo.ll -o foo.o
$ ./build/bin/llvm-as main.ll -o main.o
$ rm -rf libfoo.a
$ ar cru libfoo.a foo.o
$ ./build/bin/clang main.o libfoo.a -o t  -Wl,-ObjC
$ ./build/bin/clang main.o foo.o -o t -Wl,-ObjC  -Wl,-dead_strip
$ ./build/bin/clang main.o libfoo.a -o t -Wl,-ObjC  -Wl,-dead_strip
ld: warning: '__Z3fooP8NSObject' is implemented in bitcode, but it was loaded
too late

Why is the file being in an archive makes a difference? Why it is even fetched
from the archive to begin with?

A guess is that internalize should ignore objc sections
("__DATA,__datacoal_nt,coalesced" in this case), but I really don't know what
ld64 is trying to do in here.
Quuxplusone commented 9 years ago

The "is implemented in bitcode, but it was loaded too late" warning is there to shed light on some libLTO/ld64 issues. Early in the link, the linker asks libLTO about all the symbols it will need, and the linker loads as needed to resolve those symbols. But sometimes the resulting codegen'ed mach-o needs some symbols that libLTO never told the linker about. This usually happens with "lib" routines that the backend decided to use (e.g. memcpy) and there was no explicit reference that libLTO could have reported to the linker.

The -Objc linker flag tells the linker to force load all archive members that define ObjC classes or categories. In this reduced case, there are no classes or categories defined (just a protocol), so the linker does not force load libfoo.a(foo.o). Perhaps is should detect protocols too...

Quuxplusone commented 9 years ago
This is a perhaps related case that, strangely enough, does not involve ObjC
code. Reverting 209227 and 207979 does not seem to help here. The issue also
seems to be present in "Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM
3.5svn)".

$ cat main.c
int main() {}
$ cat fad.ii
class A {
public:
  A();
} a;
int b;
$ clang++ -c -flto fad.ii
$ clang -c -flto main.c
$ rm -f libfad.a
$ ar cru libfad.a fad.o
$ clang -flto -Wl,-ObjC -Wl,-dead_strip main.o libfad.a
ld: warning: '_b' is implemented in bitcode, but it was loaded too late
Undefined symbols for architecture x86_64:
  "_a", referenced from:
      __GLOBAL__I_a in lto.o
      import-atom in libfad.a(fad.o)
  "A::A()", referenced from:
      __GLOBAL__I_a in lto.o
      import-atom in libfad.a(fad.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Quuxplusone commented 9 years ago

I am reasonably sure that this is a bug in the linker, ld64. I have a (quick and dirty) patch for ld64 that fixes my minimal test cases, and appears to let me link Chromium.

This might not be the correct forum to discuss changes to ld64, though. Nick, is there a more appropriate forum?

Quuxplusone commented 9 years ago

Unfortunately my patch was not sufficient. I discovered that the ld64 code for identifying which object files belong to Objective C classes does not support LTO objects, and this seems rather more complicated to fix.

For now I've worked around the issue by passing -all_load when using LTO, and I've filed http://openradar.appspot.com/radar?id=5888585047736320 with Apple.

Quuxplusone commented 3 years ago
Does not repro anymore. I came to this bug because i'm getting similar error
but the examples here do not repro the error message anymore.

ld: warning: '_create_id' is implemented in bitcode, but it was loaded too late
ld: warning: '_trace_begin' is implemented in bitcode, but it was loaded too
late
ld: warning: '_trace_end' is implemented in bitcode, but it was loaded too late
ld: warning: '___cyg_profile_func_enter' is implemented in bitcode, but it was
loaded too late
ld: warning: '___cyg_profile_func_exit' is implemented in bitcode, but it was
loaded too late
ld: warning: '___cyg_profile_func_enter_bare' is implemented in bitcode, but it
was loaded too late
Undefined symbols for architecture x86_64:

...
...

ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

------------------------------------------------------------
Stack trace:
------------------------------------------------------------

ld: warning: '_create_id' is implemented in bitcode, but it was loaded too late
ld: warning: '_trace_begin' is implemented in bitcode, but it was loaded too
late
ld: warning: '_trace_end' is implemented in bitcode, but it was loaded too late
ld: warning: '___cyg_profile_func_enter' is implemented in bitcode, but it was
loaded too late
ld: warning: '___cyg_profile_func_exit' is implemented in bitcode, but it was
loaded too late
ld: warning: '___cyg_profile_func_enter_bare' is implemented in bitcode, but it
was loaded too late
0  0x10fe381cc  __assert_rtn + 123
1  0x10fe40f80
ld::tool::HeaderAndLoadCommandsAtom<x86_64>::sectionFlags(ld::Internal::FinalSection*)
const (.cold.4) + 0
2  0x10fdced9a
ld::tool::HeaderAndLoadCommandsAtom<x86_64>::sectionFlags(ld::Internal::FinalSection*)
const + 744
3  0x10fdce31b
ld::tool::HeaderAndLoadCommandsAtom<x86_64>::copySegmentLoadCommands(unsigned
char*, unsigned char*) const + 1069
4  0x10fdccf7f
ld::tool::HeaderAndLoadCommandsAtom<x86_64>::copyRawContent(unsigned char*)
const + 295
5  0x10fd838f3  ld::tool::OutputFile::writeAtoms(ld::Internal&, unsigned char*)
+ 321
6  0x10fd7a88d  ld::tool::OutputFile::writeOutputFile(ld::Internal&) + 821
7  0x10fd71e69  ld::tool::OutputFile::write(ld::Internal&) + 189
8  0x10fcf06e3  main + 774
9  0x7fff20432f5d  start + 1
A linker snapshot was created at:
    /tmp/Library/Foo#iphonesimulator-x86_64-214806.ld-snapshot
ld: Assertion failed: (0 && "typeTempLTO should not make it to final linked
image"), function sectionFlags, file
/Library/Caches/com.apple.xbs/Sources/ld64/ld64-
650.9/src/ld/HeaderAndLoadCommands.hpp, line 866.
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Quuxplusone commented 3 years ago
The linker invocation does have both
-dead_strip and -ObjC though.

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld
-demangle -application_extension -lto_library
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib
-dynamic -arch x86_64 -dead_strip -ios_simulator_version_min 10.0.0 -syslibroot
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk
-e _NSExtensionMain -ObjC ...
Quuxplusone commented 3 years ago
So here is the repro.

$ cat test.c
int foo() { return 0; }

$ cat main.c
int foo();

int main() {
  foo();
  return 0;
}

$ cat trace.cpp
#include <atomic>
#include <stdio.h>

extern "C" {

typedef uint64_t counter_type;
std::atomic<counter_type> id;

__attribute__((no_instrument_function)) counter_type create_id()
{
    return id++;
}

__attribute__((no_instrument_function)) void __cyg_profile_func_enter_bare(void
*func, void *caller)
{
    counter_type c = create_id();
    fprintf(stdout, "e %p %p %llu\n", func, caller, c);
}
}

$ cat crash.sh
  clang -c main.c
  clang -flto=thin -finstrument-function-entry-bare test.c -c
  ar cru libtest.a test.o
  clang++ -flto=thin trace.cpp -c
  ar cru libtest.a trace.o
  clang++ -flto=thin main.o libtest.a

ld: warning: '_create_id' is implemented in bitcode, but it was loaded too late
ld: warning: '___cyg_profile_func_enter_bare' is implemented in bitcode, but it
was loaded too late
ld: warning: '_id' is implemented in bitcode, but it was loaded too late
0  0x10b8cd1cc  __assert_rtn + 123
1  0x10b8d5f80
ld::tool::HeaderAndLoadCommandsAtom<x86_64>::sectionFlags(ld::Internal::FinalSection*)
const (.cold.4) + 0
2  0x10b863d9a
ld::tool::HeaderAndLoadCommandsAtom<x86_64>::sectionFlags(ld::Internal::FinalSection*)
const + 744
3  0x10b86331b
ld::tool::HeaderAndLoadCommandsAtom<x86_64>::copySegmentLoadCommands(unsigned
char*, unsigned char*) const + 1069
4  0x10b861f7f
ld::tool::HeaderAndLoadCommandsAtom<x86_64>::copyRawContent(unsigned char*)
const + 295
5  0x10b8188f3  ld::tool::OutputFile::writeAtoms(ld::Internal&, unsigned char*)
+ 321
6  0x10b80f88d  ld::tool::OutputFile::writeOutputFile(ld::Internal&) + 821
7  0x10b806e69  ld::tool::OutputFile::write(ld::Internal&) + 189
8  0x10b7856e3  main + 774
9  0x7fff20432f5d  start + 1
A linker snapshot was created at:
    /tmp/a.out-2021-05-20-224729.ld-snapshot
ld: Assertion failed: (0 && "typeTempLTO should not make it to final linked
image"), function sectionFlags, file
/Library/Caches/com.apple.xbs/Sources/ld64/ld64-
650.9/src/ld/HeaderAndLoadCommands.hpp, line 866.
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Quuxplusone commented 3 years ago
If the linker has a warning:
  '_foo' is implemented in bitcode, but it was loaded too late
Then either you need to:
1) not implement _foo in bitcode (use mach-o)
2) don't put the .o file with _foo in a static library (they are loaded late)
2) add '-u _foo' to the link line, which will force _foo to be loaded early