0

I'm making a Swift translation of a helper utility to turn on accessibility in the iOS simulator (code copied below), but I'm missing something about the c interop details because it generates a General Protection Fault.

Original code:

@interface ASAccessibilityEnabler : NSObject

@end


@implementation ASAccessibilityEnabler

+ (void)load;
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        void *handle = [self loadDylib:@"/usr/lib/libAccessibility.dylib"];
        if (!handle) {
            [NSException raise:NSGenericException format:@"Could not enable accessibility"];
        }

        int (*_AXSAutomationEnabled)(void) = dlsym(handle, "_AXSAutomationEnabled");
        void (*_AXSSetAutomationEnabled)(int) = dlsym(handle, "_AXSSetAutomationEnabled");

        int initialValue = _AXSAutomationEnabled();
        _AXSSetAutomationEnabled(YES);
        atexit_b(^{
            _AXSSetAutomationEnabled(initialValue);
        });
    });
}

+ (void *)loadDylib:(NSString *_Nonnull)path;
{
    NSDictionary *environment = [[NSProcessInfo processInfo] environment];
    NSString *simulatorRoot = [environment objectForKey:@"IPHONE_SIMULATOR_ROOT"];
    if (simulatorRoot) {
        path = [simulatorRoot stringByAppendingPathComponent:path];
    }
    return dlopen([path fileSystemRepresentation], RTLD_LOCAL);
}

@end

My Swift translation (with the GPF marked as a comment):

var accessibilityEnabled = false
func enableAccessibility() {
    guard !accessibilityEnabled else { return }
    guard
        let handle = loadDylibInSimulator(name: "/usr/lib/libAccessibility.dylib"),
        let _AXSAutomationEnabled = dlsym(handle, "_AXSAutomationEnabled")?
            .bindMemory(to: (@convention(c) () -> CInt).self, capacity: 1),
        let _AXSSetAutomationEnabled = dlsym(handle, "_AXSSetAutomationEnabled")?
            .bindMemory(to: (@convention(c) (CInt) -> Void).self, capacity: 1)
        else { fatalError("sad trombone") }
    accessibilityEnabled = true
    let initialValue = _AXSAutomationEnabled.pointee() // <--- Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    _AXSSetAutomationEnabled.pointee(1)
    atexit_b {
        _AXSSetAutomationEnabled.pointee(initialValue)
    }
}

func loadDylibInSimulator(name: String) -> UnsafeMutableRawPointer? {
    let environment = ProcessInfo.processInfo.environment
    guard let simulatorRoot = environment["IPHONE_SIMULATOR_ROOT"] else { return nil }
    let url = URL(fileURLWithPath: simulatorRoot).appendingPathComponent(name)
    let cString = FileManager.default.fileSystemRepresentation(withPath: url.path)
    return dlopen(cString, RTLD_LOCAL)
}

I assume it's a mistake in my translation from Objective C to Swift: what is the correct translation of those function pointers into Swift?

Tikitu
  • 679
  • 6
  • 22
  • The error in your translation is that you *dereference* the pointer from dlsym. – The referenced Q&A should solve your problem, otherwise let me know. – Martin R Jun 01 '20 at 07:13
  • Thanks @MartinR that is indeed exactly what I needed! – Tikitu Jun 01 '20 at 08:02

0 Answers0