jack sexton jack sexton - 27 days ago 17
iOS Question

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

Answer

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]