Search code examples
iosswiftswift3

How to get value of UnsafeMutableRawPointer?


I'm trying to get the address that is pointed to by UnsafeMutableRawPointer, but I'm unable to do so. I'm also new to Swift so I might be missing something or doing it completely wrong. Preferably I would like to cast the raw value into a CChar.

enter image description here


Solution

  • Note to passers-by: much of my answer won't make sense, as it doesn't answer the initial question above, but rather the question(s) that arose in the chat with OP.

    Took me a few hours, but now that I've learned some assembly, I can answer some questions.

    1. CChar is a C Char ... literally. It represents the char type of C. It's a typealias to Int8. It's a single byte. You can't use this like a pointer type, as those are 8 bytes (on 64 bit machines).

    2. You don't need all this UnsafeMutableRawPointer boilerplate, and you certainly don''t need to access its raw value. You can pass arrays directly where pointers are expected.

      When a function is declared as taking an UnsafePointer argument, it can accept any of the following: ...

      • A [Type] value, which is passed as a pointer to the start of the array.

      from Interacting with C APIs - pointers.

    3. You were experiencing issues were your mutation of 0x8(%rdi) did not seem to be reflected on the Swift side. The issue here is that you're writing at an offset of 8 bytes, but then print(a.load(as: void_star.self)) is reading the first byte. You're reading a byte you never modified.

    I did some further exploration. Here's the loot from my adventure:

    exampleSwift.swift:

    @_silgen_name("incrementByValue")
        func incrementByValue(_: Int64)
    
    @_silgen_name("incrementByReference")
        func incrementByReference(_: inout Int64)
    
    @_silgen_name("return1234")
        func return1234() -> Int64
    
    @_silgen_name("incrementElements")
        func incrementElements(of _: UnsafeRawPointer, count _: Int)
    
    var a: Int64 = 100
    print("\"a\" before \"incrementByValue(a)\": \(a)")
    incrementByValue(a)
    print("\"a\" after \"incrementByValue(a)\": \(a)\n")
    
    var b: Int64 = 200
    print("\"b\" before \"incrementByValue(b)\": \(b)")
    incrementByReference(&b)
    print("\"b\" after \"incrementByValue(b)\": \(b)\n")
    
    print("return1234() returned: \(return1234())\n")
    
    var array: [Int64] = Array(0...5)
    
    print("\"array\" before incrementElements(of: array, count: array.count): \n\t\(array)")
    incrementElements(of: array, count: array.count)
    print("\"array\" after incrementElements(of: array, count: array.count): \n\t\(array)\n")
    

    exampleASM.s:

    .text
    
    .globl _incrementByValue
    .globl _incrementByReference
    .globl _return1234
    .globl _incrementElements
    
    // A test routine that demonstrates operating on a value
    _incrementByValue:
        // %rdi contains a copy of the argument passed in.
        // Changes here won't be reflected back in Swift
        incq %rdi
        ret
    
    // A test routine that demonstrates operating on a reference
    _incrementByReference:
        // %rdi contains a reference tp the argument passed in.
        // Changes to the reference itself won't be reflected back in Swift,
        // but changes to the referenced memory will.
        incq (%rdi)
        ret
    
    // A test routine that demonstrates the use of %rax for returning a value
    _return1234:
        movq $1234, %rax    // return value is in rax
        ret
    
    //A test routine that demonstrates operating on an array
    _incrementElements:
        // %rdi: Pointer to first of n Int64 elements
        // %rsi: the array count, n
    
        movq    %rsi, %rcx  // Set loop counter (%rcx) to n
        aLoop:
            incq    (%rdi)      // increment value pointer to by %rdi
            add     $8, %rdi    // advance pointer by 8 bytes
            loop    aLoop       // loop back to aLoop if rcx > 0
    
        ret
    

    Compile, link and run with:

    llvm-g++ -c exampleASM.s &&
    swiftc -c exampleSwift.swift &&
    ld exampleASM.o exampleSwift.o -o exampleBinary -force_load /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_macosx.a -framework CoreFoundation -macosx_version_min 10.12.0 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -lobjc -lSystem -arch x86_64 -L /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx -rpath /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx -no_objc_category_merging &&
    ./exampleBinary
    

    Output:

    "a" before "incrementByValue(a)": 100
    "a" after "incrementByValue(a)": 100
    
    "b" before "incrementByValue(b)": 200
    "b" after "incrementByValue(b)": 201
    
    return1234() returned: 1234
    
    "array" before incrementElements(of: array, count: array.count): 
        [0, 1, 2, 3, 4, 5]
    "array" after incrementElements(of: array, count: array.count): 
        [1, 2, 3, 4, 5, 6]