EXC_BAD_ACCESS crash on arm64 when use NSInvocation - ios

I've begun prepare one old project to support arm64 architecture. But when I try to execute this code on 64 bit device I get EXC_BAD_ACCESS crash on [invocation retainArguments]; line
- (void)makeObjectsPerformSelector: (SEL)selector withArguments: (void*)arg1, ...
{
va_list argList;
NSArray* currObjects = [NSArray arrayWithArray: self];
for (id object in currObjects)
{
if ([object respondsToSelector: selector])
{
NSMethodSignature* signature = [[object class] instanceMethodSignatureForSelector: selector];
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: signature];
invocation.selector = selector;
invocation.target = object;
if (arg1 != nil)
{
va_start(argList, arg1);
char* arg = arg1;
for (int i = 2; i < signature.numberOfArguments; i++)
{
const char* type = [signature getArgumentTypeAtIndex: i];
NSUInteger size, align;
NSGetSizeAndAlignment(type, &size, &align);
NSUInteger mod = (NSUInteger) arg % align;
if (mod != 0)
arg += (align - mod);
[invocation setArgument: arg
atIndex: i];
arg = (i == 2) ? (char*) argList : (arg + size);
}
va_end(argList);
}
[invocation retainArguments];
[invocation invoke];
}
}
}
It seems like its some problem with arguments.

This is what have for the same purposes.
+ (void)callSelectorWithVarArgs:(SEL)selector onTarget:(id)target onThread:(id)thread wait:(BOOL)wait, ...
{
NSMethodSignature *aSignature = [[target class] instanceMethodSignatureForSelector:selector];
if (aSignature)
{
NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
void * arg;
int index = 2;
[anInvocation setSelector:selector];
[anInvocation setTarget:target];
va_list args;
va_start(args, wait);
do
{
arg = va_arg(args, void *);
if (arg)
{
[anInvocation setArgument:arg atIndex:index++];
}
}
while (arg);
va_end(args);
[anInvocation retainArguments];
if (thread == nil)
{
[anInvocation performSelectorOnMainThread:#selector(invoke) withObject:nil waitUntilDone:wait];
}
else
{
[anInvocation performSelector:#selector(invoke) onThread:thread withObject:nil waitUntilDone:wait];
}
}
}
Please take into account, that this code is potentially unsafe with when necessary to perform type conversion. When invoked method has longer argument that was passed to my callSelectorWithVarArgs:onTarget:onThread:wait: (for example, invoked method receives NSUInteger (which is 64bit on arm64) but i pass int (which is 32bit on both arm and arm64)), that causes read of 64 bit from start address of 32bit variable - and trash in data).
Anyway, your implementation is potentially dangerous - you treat all arguments passed to wrapped method as having the same types as arguments in invoked method.
This is your modified code that works:
- (void)makeObjectsPerformSelector:(SEL)selector withArguments: (void*)arg1, ...
{
NSArray* currObjects = [NSArray arrayWithArray: self];
for (id object in currObjects)
{
if ([object respondsToSelector: selector])
{
NSMethodSignature* signature = [[object class] instanceMethodSignatureForSelector: selector];
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: signature];
invocation.selector = selector;
invocation.target = object;
[invocation setArgument:&arg1 atIndex:2];
NSInteger index = 3;
void * arg;
va_list args;
va_start(args, arg1);
do
{
arg = va_arg(args, void *);
if (arg)
{
[invocation setArgument:&arg atIndex:index++];
}
}
while (arg);
va_end(args);
[invocation retainArguments];
[invocation invoke];
}
}
}

This code is making non-portable assumptions about the layout of different arguments in va_list, and which do not work on arm64.
You can see, for example, that there are other tricks (to solve a different problem) that relied on the layout of arguments in va_list, that worked in 32-bit, but which also don't work in 64-bit.
The only portable way to access arguments from a va_list is through va_arg, but that requires a fixed type at compile-time.

You are using int and you say it is running fine on 32bit but crash on 64bit. Switch to NSInteger or NSUInteger for your iterations. Guess that will fix your problem

You are using the argument list more than once. Doing so is undefined behavior. You can work around this issue by using va_copy instead.
Move the va_start(argList, arg1) outside the outer for loop and create a copy of the arguments list using the following: va_list copyArgList; va_copy(copyArgList, argList);. Then use the copied argument list as normal.
More information about va_copy

I think you need to take a look at moving away from this approach and recode things to a safer mechanism based on va_arg which is the only safe mechanism for traversing variable arguments. Something along the lines of what was posted by #Nikita.
If you want to continue with the current approach you will need to delve into the iOS calling conventions for each architecture. You can find the ARM64 conventions here: https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html
Just from an initial look it is clearly not straight forward and variadic functions differ from the normal calling convention.

Related

Cast of 'long' to 'id' is disallowed with ARC - iOS Dev

Converting my project to ARC but need help with these two bits of code to do with uploading and downloading files:
- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite{
[delegate performSelector:progressSelector withObject:(id)(100*totalBytesWritten /totalBytesExpectedToWrite)];
and then the following code, specifically the last line:
- (void)connection:(NSURLConnection *)con // IN
didReceiveData:(NSData *)data // IN
{
NSLog(#"%s: self:0x%p\n", __func__, self);
NSInteger dataLength;
const uint8_t * dataBytes;
NSInteger bytesWritten;
NSInteger bytesWrittenSoFar;
dataLength = data.length;
dataBytes = (const uint8_t * )data.bytes;
bytesWrittenSoFar = 0;
if(fileStream!=NULL)
{
do {
NSLog(#"%d",(int)bytesWrittenSoFar);
bytesWritten = [fileStream write:&dataBytes[bytesWrittenSoFar] maxLength:dataLength - bytesWrittenSoFar];
assert(bytesWritten != 0);
if (bytesWritten == -1)
{
[self downloadSucceeded:NO];
break;
}
else
{
bytesWrittenSoFar += bytesWritten;
}
}
while (bytesWrittenSoFar != dataLength);
}
dataSize+=data.length;
if(dataSize==downloadSize)
{
downloadDidSucceed=TRUE;
}
[delegate performSelector:progressSelector withObject:(id)(long)(100*dataSize/downloadSize)];
}
Any help is appreciated
Your problem is here
[delegate performSelector:progressSelector withObject:(id)(long)(100*dataSize/downloadSize)];
try this
[delegate performSelector:progressSelector withObject:[NSnumber numberWithLong:(100*dataSize/downloadSize)]];
In Objective-C there is distinction between scalar values like int/long/float/double and object types.
the id type is an anonymous object type. You don't know what it is, but you know that it is an object.
A long is not, and cannot be, an object. It is a simple scalar value. that's why the compiler isn't letting you cast your long to an ID type.
Sh_Khan's answer of wrapping your long value in an NSNumber solves the problem by creating an object that contains your value. You could also use an NSValue to get the same effect, but NSValue can represent things like structs that you can't represent with NSNumbers.

Find ALL cocoa classes that are singleton?

There are several system classes in Cocoa that are singletons, such as UIApplication, NSNotificationCenter. Now, I want to find all classes that are singleton, any suggestion that how could I quickly find them all?
I'm working on a huge codebase, and I need to separate the system singleton object from the customized singleton.
Objective-C runtime hackery! Fun!
Now, before I continue, I will present the disclaimer that I'd never recommend putting anything like this in actual shipping code, and that if you do, it's totally not my fault. This can be fun/interesting to do for educational purposes, though.
This isn't going to be an exact science, since the language itself doesn't have any actual concept of a "singleton". Basically, we're just looking for Objective-C classes that have class methods with certain giveaway prefixes. If we find one of those, there's a good chance that we have a singleton.
With that in mind:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
static BOOL ClassIsSingleton(Class class) {
unsigned int methodCount = 0;
Method *methods = class_copyMethodList(object_getClass(class), &methodCount);
#try {
for (unsigned int i = 0; i < methodCount; i++) {
Method eachMethod = methods[i];
// only consider class methods with no arguments
if (method_getNumberOfArguments(eachMethod) != 2) {
continue;
}
char *returnType = method_copyReturnType(eachMethod);
#try {
// only consider class methods that return objects
if (strcmp(returnType, #encode(id)) != 0) {
continue;
}
}
#finally {
free(returnType);
}
NSString *name = NSStringFromSelector(method_getName(methods[i]));
// look for class methods with telltale prefixes
if ([name hasPrefix:#"shared"]) {
return YES;
} else if ([name hasPrefix:#"standard"]) {
return YES;
} else if ([name hasPrefix:#"default"]) {
return YES;
} else if ([name hasPrefix:#"main"]) {
return YES;
} // feel free to add any additional prefixes here that I may have neglected
}
}
#finally {
free(methods);
}
return NO;
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSMutableArray *singletons = [NSMutableArray new];
int classCount = objc_getClassList(NULL, 0);
Class *classes = (Class *)malloc(classCount * sizeof(Class));
#try {
classCount = objc_getClassList(classes, classCount);
for (int i = 0; i < classCount; i++) {
Class eachClass = classes[i];
if (ClassIsSingleton(eachClass)) {
[singletons addObject:NSStringFromClass(eachClass)];
}
}
}
#finally {
free(classes);
}
NSLog(#"Singletons: %#", singletons);
}
return 0;
}

How would I back a bunch of class properties with a dictionary?

I have a class that holds attributes in a dictionary where the keys are well defined. I would like to replace this attribute dictionary with a class, let's call it AttributeSet. Where there were defined keys:
extern NSString *const Foo;
I would like to have properties:
#interface AttributeSet : NSObject
#property(strong) NSString *Foo;
...a ton more
#end
I would actually like the AttributeSet object to use a dictionary behind the scenes because for backwards compatibility reasons. So when this happens:
attributeSet.Foo = #"bar";
I actually want this to happen:
- (void)setFoo:(NSString *)foo {
self.attributes[Foo] = foo; //Foo is the extern variable Foo
}
but I don't want to have to define getters and setters for all of the properties.
I know that I can use key-value observing but that will 1) require me to have a mapping of (property name) #"Foo" --> (variable name) Foo and 2) result in both the property being set and the dictionary value being set when in reality I just want the dictionary to be set.
I know that I can do something like this: https://github.com/iosptl/ios6ptl/blob/master/ch28/Person/Person/Person.m
but that would 1) still require me to have a mapping and 2) require me to have an #dynamic for every property.
Is there a more automatic way to do this?
Thanks
To use the dynamically-generated accessor approach, as illustrated in the Person code you linked, without requiring #dynamic, you can declare the properties in a category on your class rather than the class itself:
#interface AttributeSet : NSObject
// ... no properties here ...
#end
#interface AttributeSet (YourPropertiesCategoryName)
#property(strong) NSString *Foo;
...a ton more
#end
The compiler will auto-synthesize properties declared in the class itself or in a class extension (which looks like a category with no category name), but not for a category.
Note that you don't need to and shouldn't provide an implementation for the category. (If you do, the compiler will complain about the lack of implementation for the properties. It won't auto-synthesize them, but you'll still need to use #dynamic to silence the warnings.)
After a bit of time, I think I've come up with quite the extensible solution for you. All it requires of you is to simply create your objects using the following helper class, like this:
#import "DictionaryBackedObject.h"
extern NSString *const foo;
NSString *const foo = #"Foo";
#interface Foo : NSObject
#property NSString *foo;
#end
#implementation Foo
#end
int main() {
Foo *object = [DictionaryBackedObject dictionaryBackedObjectOfType:[Foo class]
backingDictionary:#{ foo: #"Bar" }
mutable:NO];
NSLog(#"%#", [object foo]);
}
Note: This implementation is far from perfect, and it does use the 'dreaded' dlsym API, meaning, that you cannot strip your symbols from the executable should you wish to use this class. Also, it may cause rejection should this be submitted to the app store. There are other ways to automatically determine the key to use along with the dictionary, however, should you wish to find a workaround.
This implementation does support struct properties, as well as weak, copy, and atomic ones as well. It will be significantly slower than setting the property on a normal object, as this goes through objective-c's forwarding API (required to support struct returns).
Hopefully this helps you out, I certainly had a lot of fun making it.
DictionaryBackedObject.h
#interface DictionaryBackedObject : NSObject
+(id) dictionaryBackedObjectOfType:(Class) kls backingDictionary:(NSDictionary *) dictionary mutable:(BOOL) isMutable;
#end
DictionaryBackedObject.m
#import "DictionaryBackedObject.h"
#include <stdalign.h>
#include <dlfcn.h>
#import ObjectiveC.runtime;
#import ObjectiveC.message;
__attribute__((noinline))
static SEL property_getGetterSelector(objc_property_t property) {
char *getter = property_copyAttributeValue(property, "G");
if (getter) {
SEL result = sel_registerName(getter);
free(getter);
return result;
}
return sel_registerName(property_getName(property));
}
__attribute__((noinline))
static SEL property_getSetterSelector(objc_property_t property) {
char *setter = property_copyAttributeValue(property, "S");
if (setter) {
SEL result = sel_registerName(setter);
free(setter);
return result;
}
char buffer[512];
char propertyName[512];
strncpy(propertyName, property_getName(property), 512);
propertyName[0] = toupper(propertyName[0]);
snprintf(buffer, 512, "set%s", propertyName);
return sel_registerName(buffer);
}
struct objc_property_attributes_t {
union {
struct {
int nonatomic : 1;
int copy : 1;
int weak : 1;
int strong : 1;
};
int memory_mode;
};
int is_readonly;
int is_dynamic;
};
static inline BOOL property_isAttributeNull(objc_property_t property, const char *attr) {
void *value = property_copyAttributeValue(property, attr);
BOOL results = value == NULL;
free(value);
return results;
}
static struct objc_property_attributes_t property_getPropertyAttributes(objc_property_t property) {
struct objc_property_attributes_t attrs;
attrs.nonatomic = !property_isAttributeNull(property, "N");
attrs.copy = !property_isAttributeNull(property, "C");
attrs.strong = attrs.copy || !property_isAttributeNull(property, "&");
attrs.weak = !property_isAttributeNull(property, "W");
attrs.is_readonly = !property_isAttributeNull(property, "R");
attrs.is_dynamic = !property_isAttributeNull(property, "D");
return attrs;
}
static objc_property_t class_getPropertyForSelector(Class kls, SEL cmd) {
#define VALID_PROPERTY(property) \
(property != NULL && (property_getGetterSelector(property) == cmd || property_getSetterSelector(property) == cmd))
const char *selName = sel_getName(cmd);
objc_property_t results = class_getProperty(kls, selName);
if (VALID_PROPERTY(results))
return results;
if (strstr(selName, "set") == selName) {
char lowercaseSel[512];
strncpy(lowercaseSel, strstr(selName, "set"), 512);
lowercaseSel[0] = tolower(lowercaseSel[0]);
results = class_getProperty(kls, lowercaseSel);
if (VALID_PROPERTY(results)) return results;
}
// Easy paths exhausted, go the 'hard' way of looping over all of the properties available
results = NULL;
unsigned propertyCount = 0;
objc_property_t *properties = class_copyPropertyList(kls, &propertyCount);
for (unsigned propertyIndex = 0; propertyIndex < propertyCount; propertyIndex++) {
if (VALID_PROPERTY(properties[propertyIndex])) {
results = properties[propertyIndex];
break;
}
}
free(properties);
return results;
#undef VALID_PROPERTY
}
#implementation DictionaryBackedObject
-(id) initWithDictionary:(NSDictionary *) dictionary mutable:(BOOL) isMutable {
return nil;
}
+(Class) dictionaryBackedSubclassOfClass:(Class) kls {
#synchronized (kls) {
NSString *className = [NSStringFromClass(kls) stringByAppendingFormat:#"_dictionaryBacked"];
Class subclass = Nil;
if ((subclass = NSClassFromString(className))) {
return subclass;
}
subclass = objc_allocateClassPair(kls, [className UTF8String], 0);
class_addIvar(subclass, "_backingDictionary", sizeof(NSDictionary *), _Alignof(NSDictionary *), #encode(NSDictionary *));
class_addIvar(subclass, "_backingDictionaryIsMutable", sizeof(NSNumber *), _Alignof(NSNumber *), #encode(NSNumber *));
unsigned propertyCount = 0;
objc_property_t *properties = class_copyPropertyList(kls, &propertyCount);
for (unsigned i = 0; i < propertyCount; i++) {
objc_property_t property = properties[i];
char *type = property_copyAttributeValue(property, "T");
SEL getterSel = property_getGetterSelector(property);
SEL setterSel = property_getSetterSelector(property);
char getterTypeBuffer[512];
snprintf(getterTypeBuffer, 512, "%s#:", type);
char setterTypeBuffer[512];
snprintf(setterTypeBuffer, 512, "v#:%s", type);
NSUInteger typeSize;
NSUInteger typeAlignment;
NSGetSizeAndAlignment(type, &typeSize, &typeAlignment);
BOOL isStret = (typeSize * CHAR_BIT) > (WORD_BIT * 2);
class_addMethod(subclass, getterSel, isStret ? _objc_msgForward_stret : _objc_msgForward , getterTypeBuffer);
class_addMethod(subclass, setterSel, _objc_msgForward, setterTypeBuffer);
free(type);
}
free(properties);
Ivar backingDictionaryIvar = class_getInstanceVariable(subclass, "_backingDictionary");
Ivar backingDictionaryMutableIvar = class_getInstanceVariable(subclass, "_backingDictionaryIsMutable");
class_addMethod(subclass, #selector(forwardingTargetForSelector:), imp_implementationWithBlock(^id (id self) {
return nil;
}), "##:");
class_addMethod(subclass, #selector(forwardInvocation:), imp_implementationWithBlock(^void (id self, NSInvocation *invocation) {
SEL _cmd = [invocation selector];
objc_property_t property = class_getPropertyForSelector([self class], _cmd);
if (property == NULL) {
[self doesNotRecognizeSelector:_cmd];
return;
}
BOOL isGetter = (_cmd == property_getGetterSelector(property));
struct objc_property_attributes_t attributes = property_getPropertyAttributes(property);
NSString *propertyType = (__bridge_transfer NSString *) CFStringCreateWithCStringNoCopy(
NULL, property_copyAttributeValue(property, "T"), kCFStringEncodingUTF8, NULL
);
NSUInteger propertySize;
NSGetSizeAndAlignment([propertyType UTF8String], &propertySize, NULL);
void *dlsymKey = dlsym(RTLD_MAIN_ONLY, property_getName(property));
id dictionaryKey = *(__unsafe_unretained id *) dlsymKey;
NSMutableDictionary *backingDictionary = object_getIvar(self, backingDictionaryIvar);
NSNumber *isMutable = object_getIvar(self, backingDictionaryMutableIvar);
// Performing synchronization on nil is a no-op, see objc_sync.mm:306.
#synchronized (attributes.nonatomic ? nil : self) {
if (isGetter) {
id value = backingDictionary[dictionaryKey];
if (attributes.strong) {
[invocation setReturnValue:&value];
} else if (attributes.weak) {
value = [value nonretainedObjectValue];
[invocation setReturnValue:&value];
} else {
void *buffer = alloca(propertySize);
[value getValue:buffer];
[invocation setReturnValue:buffer];
}
} else {
if ((attributes.is_readonly || ![isMutable boolValue])) {
[self doesNotRecognizeSelector:_cmd];
return;
}
id dictionaryValue = nil;
void *newValue = alloca(propertySize);
[invocation getArgument:newValue atIndex:2];
if (attributes.strong) {
dictionaryValue = (__bridge id) newValue;
if (attributes.copy) {
dictionaryValue = [dictionaryValue copy];
}
} else if (attributes.weak) {
dictionaryValue = [NSValue valueWithNonretainedObject:(__bridge id) newValue];
} else {
dictionaryValue = [NSValue valueWithBytes:newValue objCType:[propertyType UTF8String]];
}
if (dictionaryValue == nil) {
[backingDictionary removeObjectForKey:dictionaryKey];
} else {
[backingDictionary setObject:dictionaryValue forKey:dictionaryKey];
}
}
}
}), "v#:#");
class_addMethod(subclass, #selector(initWithDictionary:mutable:), imp_implementationWithBlock(^id (id self, NSDictionary *dictionary, BOOL mutable) {
object_setIvar(self, backingDictionaryIvar, dictionary);
object_setIvar(self, backingDictionaryMutableIvar, #(mutable));
return self;
}), "##:#c");
objc_registerClassPair(subclass);
return subclass;
}
}
+(id) dictionaryBackedObjectOfType:(Class)kls backingDictionary:(NSDictionary *)dictionary mutable:(BOOL)isMutable {
Class subclass = [self dictionaryBackedSubclassOfClass:kls];
return [[subclass alloc] initWithDictionary:dictionary mutable:isMutable];
}
#end
Rob Napier's example is a good option; the compiler's going to generate accessors for you unless you tell it not to, and the way you tell it that is with the #dynamic directive.
Another option would be automated code generation: write a script to emit ObjC code for your setters.
The third that I can think of is overwriting the accessors during runtime. In your class's +initialize, you can get the list of its properties from the runtime library and use class_replaceMethod() to insert your own accessors that use your dictionary instead of the ivars. This will require some string mangling to get the accessor names and keys from each other.
Here's a gist with a demo of that last option: https://gist.github.com/woolsweater/4fb874b15449ee7fd7e8

Crashed when pass va_list to FMDB executeUpdate function

I have one method which accepts va_list like this:
+(NSUInteger) addObjectToDB:(NSString*)dbFilePath withSQL:(NSString*)sql, ... {
va_list args;
va_start(args, sql);
FMDatabaseQueue* dbQ = [FMDatabaseQueue databaseQueueWithPath:dbFilePath];
__block NSUInteger result = -1;
[dbQ inDatabase:^(FMDatabase *db) {
[db open];
if ([db executeUpdate:sql, args]) {
result = (NSUInteger) [db lastInsertRowId];
}
[db close];
}];
va_end(args);
return result;
}
EDIT1:
I want to pass va_list(args) to the 'executeUpdate:' method of FMDatabase like this:
NSString* sql = [NSString stringWithFormat:#"Insert Into Table_Name Values (NULL, '%#', ?, ?)", #"string"];
[CXDBHelper addObjectToDB:self.dbFilePath withSQL:sql, NSData, NSData];
but the execution stopped in the method (FMDatabase.m) :
- (void)bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt {
if ((!obj) || ((NSNull *)obj == [NSNull null])) {
sqlite3_bind_null(pStmt, idx);
}
// FIXME - someday check the return codes on these binds.
else if ([obj isKindOfClass:[NSData class]]) {
at the last line without error message.
EDIT 2:
Thanks to #rmaddy's tips, I check the code executeUpdate:, it is defined as:
- (BOOL)executeUpdate:(NSString*)sql, ...
I realize that the problem is I cannot pass a va_list to it.
EDIT 3:
At last, I use another method which accept va_list: executeUpdate: withVAList: and everything is OK now.

incompatible block pointer types objectsPassingTest:

I want to get filtred NSSet:
NSSet* contents = [self.content objectsPassingTest:^(id obj, BOOL* stop){
NSNumber* chapterNo = ((LTContent*)obj).chapterNo;
return [chapterNo integerValue] < 0;
}];
But this code fires an error: incompatible block pointer types sending 'int (^)(id, BOOL *)' to parameter of type 'BOOL (^)(id, BOOL *)
If I change code:
NSSet* contents = [self.content objectsPassingTest:^(id obj, BOOL* stop){
NSNumber* chapterNo = ((LTContent*)obj).chapterNo;
BOOL a = [chapterNo integerValue] < 0;
return a;
}];
it works perfect. But I don't want to use odd line. What's wrong in first snippet?
You forgot block's return type:
NSSet* contents = [self.content objectsPassingTest:^BOOL(id obj, BOOL* stop) {
Specify the explicit return type BOOL for the block:
NSSet* contents = [set objectsPassingTest:^BOOL(id obj, BOOL* stop) {
// ...
return [chapterNo integerValue] < 0;
}];
Otherwise the compiler derives the return type from the
return statement, and that is int in your case.

Resources