Hi there, I'm trying to run trivial Objective-C code that uses the Foundation framework under MacOS X in lli. It seems that the code will compile and run using llc, however fails to work in lli. SimpleFoundation.m: ---- #import <Foundation/Foundation.h> int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; // insert code here... NSLog(@"Hello, World!"); [pool release]; return 0; } ---- $ llvm-gcc -c -emit-llvm SimpleFoundation.m -o SimpleFoundation.bc $ llc SimpleFoundation.bc -o SimpleFoundation.s $ gcc /System/Library/Frameworks/Foundation.framework/Foundation SimpleFoundation.s -o SimpleFoundation.exe $ ./SimpleFoundation.exe 2007-07-19 17:42:32.667 SimpleFoundation.exe[2535] Hello, World! $ lli -load=/System/Library/Frameworks/Foundation.framework/Foundation SimpleFoundation.bc Segmentation fault $ lli -force-interpreter -load=/System/Library/Frameworks/Foundation.framework/Foundation SimpleFoundation.bc Could not resolve external global address: .objc_class_name_NSAutoreleasePool Abort trap $ nm /System/Library/Frameworks/Foundation.framework/Foundation | grep .objc_class_name_NSAutoreleasePool 00000000 A .objc_class_name_NSAutoreleasePool It seems that when linked by GCC the symbols are found without any trouble however when using lli, the symbols arn't resolved properly. I'm not really sure what's causing this, I think it might be something to do with the fact that this symbol doesn't start with an '_' for some funky reason. Help is appreciated, I'm not sure if this is a bug in llvm or a problem with the way I am using LLVM.
Chris Lattner
2007-Jul-19 21:56 UTC
[LLVMdev] Trouble Resolving Objective-C Symbols in lli
On Thu, 19 Jul 2007, Andy Kitchen wrote:> Hi there, I'm trying to run trivial Objective-C code that uses the > Foundation framework under MacOS X in lli. It seems that the code will > compile and run using llc, however fails to work in lli.Nice! this is a great project, unfortunately, there are some issues here :) I'm CC'ing Marcel, as he has some experience with dynamic generation of code with the objc runtime library.> SimpleFoundation.m: > ---- > > #import <Foundation/Foundation.h> > > int main (int argc, const char * argv[]) { > NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; > > // insert code here... > NSLog(@"Hello, World!"); > [pool release]; > return 0; > } > > ---- > > $ llvm-gcc -c -emit-llvm SimpleFoundation.m -o SimpleFoundation.bc > $ llc SimpleFoundation.bc -o SimpleFoundation.s > $ gcc /System/Library/Frameworks/Foundation.framework/Foundation > SimpleFoundation.s -o SimpleFoundation.exe > $ ./SimpleFoundation.exe > > 2007-07-19 17:42:32.667 SimpleFoundation.exe[2535] Hello, World!yay :)> $ lli -load=/System/Library/Frameworks/Foundation.framework/Foundation > SimpleFoundation.bc > > Segmentation fault > > $ lli -force-interpreter > -load=/System/Library/Frameworks/Foundation.framework/Foundation > SimpleFoundation.bc > > Could not resolve external global address: .objc_class_name_NSAutoreleasePool > Abort trap > > $ nm /System/Library/Frameworks/Foundation.framework/Foundation | > grep .objc_class_name_NSAutoreleasePool > > 00000000 A .objc_class_name_NSAutoreleasePoolOk, as you figured out, you do need to tell lli explicitly what frameworks to load. Once you have that, you are hitting another problem. Specifically, the JIT::getPointerToNamedFunction method in lib/ExecutionEngine/JIT/Intercept.cpp just does a dlsym on missing symbols. If dlsym returns null, you get the error message. The problem here is that .objc_class_name_* are special symbols that are used by the objc linker support and they have magic meaning. This itself isn't a problem, the problem is that they are absolute symbols (which is why nm prints 'A' for the symbol) and their absolute value is 0. When dlsym correctly returns the address of this symbol, it returns a null pointer (which is the address of the symbol) and lli aborts because it thinks dlsym returned failure. After consulting with our dynamic linker guru, I don't think there is a wonderful clean way to do this. I suggest adding a memcmp to the error path in getPointerToNamedFunction that checks to see if the symbol starts with ".objc_class_name_". If so, getPointerToNamedFunction should return null without aborting. There may be one or two other prefixes you will have to add, for selectors or categories. Once this is done, your example above should work. However, when you move on to more interesting examples, you'll probably hit other issues. For example, the objc runtime needs to be informed of any new classes that are added to an address space. I think the runtime has API calls that are used to do this, but the LLVM JIT currently doesn't know how to do any of them. If you're interested in investigating this and adding support to LLVM, it would be greatly appreciated. -Chris -- http://nondot.org/sabre/ http://llvm.org/
Ralph Corderoy
2007-Jul-20 09:38 UTC
[LLVMdev] Trouble Resolving Objective-C Symbols in lli
Hi Chris,> Once you have that, you are hitting another problem. Specifically, > the JIT::getPointerToNamedFunction method in > lib/ExecutionEngine/JIT/Intercept.cpp just does a dlsym on missing > symbols. If dlsym returns null, you get the error message. > > The problem here is that .objc_class_name_* are special symbols that > are used by the objc linker support and they have magic meaning. This > itself isn't a problem, the problem is that they are absolute symbols > (which is why nm prints 'A' for the symbol) and their absolute value > is 0. When dlsym correctly returns the address of this symbol, it > returns a null pointer (which is the address of the symbol) and lli > aborts because it thinks dlsym returned failure. > > After consulting with our dynamic linker guru, I don't think there is > a wonderful clean way to do this.I could be missing something, but shouldn't the use of dlsym() be char *err; void *p; if ((err = dlerror())) { error("earlier undetected dlerror: %s\n", err); } p = dlsym(handle, sym); if ((err = dlerror())) { error("dlsym failed: %s\n", err); } return p; The authors of dlsym() realised the return value was overloaded AFAICS. Cheers, Ralph.