iOS环境下在LLVM Bitcode中调用NSLog引发运行时错误
Let's break down exactly why your code is crashing and walk through the fixes step by step. The EXC_BAD_ACCESS at objc_msgSend tells us NSLog is getting an invalid object pointer—your current code passes a raw C string instead of the NSString object NSLog expects, plus you've got the wrong calling convention and function signature for NSLog. Here's how to fix it:
1. Correct NSLog's Function Signature & Calling Convention
NSLog is an Objective-C function, not a Swift one. Its actual signature is:
void NSLog(NSString *format, ...);
We need to define the right function type and use the C calling convention (not Swift) since that's what Objective-C uses.
2. Convert Your C String to an NSString Object
CreateGlobalStringPtr generates a null-terminated C string, but NSLog requires an NSString instance. We'll use Objective-C runtime functions to convert the C string properly:
- Fetch the
NSStringclass withobjc_getClass - Use
sel_registerNameto get the selector forstringWithUTF8String: - Call that selector on the
NSStringclass to create our string object
3. Fix the NSLog Call
Pass the valid NSString object as the first argument to NSLog instead of the raw C string.
Modified LLVM Pass Code
// Get or declare NSLog with the correct signature Function *Nslog = module->getFunction("NSLog"); if (!Nslog) { Type *VoidTy = Type::getVoidTy(module->getContext()); PointerType *NSStringPtrTy = PointerType::get(IntegerType::get(module->getContext(), 8), 0); // NSLog takes NSString* as first param, followed by variadic args FunctionType *FuncTy = FunctionType::get(VoidTy, {NSStringPtrTy}, true); Nslog = Function::Create(FuncTy, GlobalValue::ExternalLinkage, "NSLog", module); Nslog->setCallingConv(CallingConv::C); // Use C calling convention, not Swift } // Declare required Objective-C runtime functions if missing Function *objc_getClass = module->getFunction("objc_getClass"); if (!objc_getClass) { PointerType *CharPtrTy = PointerType::get(IntegerType::get(module->getContext(), 8), 0); FunctionType *ObjcGetClassTy = FunctionType::get(CharPtrTy, {CharPtrTy}, false); objc_getClass = Function::Create(ObjcGetClassTy, GlobalValue::ExternalLinkage, "objc_getClass", module); objc_getClass->setCallingConv(CallingConv::C); } Function *sel_registerName = module->getFunction("sel_registerName"); if (!sel_registerName) { PointerType *CharPtrTy = PointerType::get(IntegerType::get(module->getContext(), 8), 0); FunctionType *SelRegNameTy = FunctionType::get(CharPtrTy, {CharPtrTy}, false); sel_registerName = Function::Create(SelRegNameTy, GlobalValue::ExternalLinkage, "sel_registerName", module); sel_registerName->setCallingConv(CallingConv::C); } Function *objc_msgSend = module->getFunction("objc_msgSend"); if (!objc_msgSend) { PointerType *IdTy = PointerType::get(IntegerType::get(module->getContext(), 8), 0); // objc_msgSend is variadic: returns id, takes id (receiver), SEL (selector), then args FunctionType *ObjcMsgSendTy = FunctionType::get(IdTy, {IdTy, IdTy}, true); objc_msgSend = Function::Create(ObjcMsgSendTy, GlobalValue::ExternalLinkage, "objc_msgSend", module); objc_msgSend->setCallingConv(CallingConv::C); } IRBuilder<> builder(module->getContext()); builder.SetInsertPoint(bb); // Step 1: Create raw C string for the function name llvm::Value *cFuncName = builder.CreateGlobalStringPtr(func_name); // Step 2: Get the NSString class llvm::Value *NSStringClassName = builder.CreateGlobalStringPtr("NSString"); llvm::Value *NSStringClass = builder.CreateCall(objc_getClass, {NSStringClassName}); // Step 3: Get the selector for stringWithUTF8String: llvm::Value *selectorStr = builder.CreateGlobalStringPtr("stringWithUTF8String:"); llvm::Value *stringSelector = builder.CreateCall(sel_registerName, {selectorStr}); // Step 4: Convert C string to NSString llvm::Value *nsFuncName = builder.CreateCall(objc_msgSend, {NSStringClass, stringSelector, cFuncName}); // Step 5: Call NSLog with the valid NSString builder.CreateCall(Nslog, {nsFuncName});
Key Fixes Explained
- Calling Convention: Switched from
CallingConv::SwifttoCallingConv::C—all Objective-C functions use the standard C calling convention, so this was a critical mismatch. - Valid NSString Object: We no longer pass a raw C string to
NSLog. Instead, we properly construct anNSStringusing the Objective-C runtime, which givesNSLoga valid object pointer to work with. - Correct Function Signature: Fixed
NSLog's type to returnvoidand accept anNSString*as its first (variadic) parameter, matching its actual implementation.
Why the Original Code Crashed
Your original code passed a raw C string pointer to NSLog, which expects an NSString object. When NSLog tried to send Objective-C messages to this pointer (treating it as an object), it accessed invalid memory (hence the KERN_INVALID_ADDRESS at 0x1), triggering the segmentation fault.
内容的提问来源于stack exchange,提问作者jhcho

