3

I'm trying to get an UnsafeMutablePointer from an UnsafeMutableRawPointer obtained using Unmanaged.passUnretained().toOpaque():

class C { var foo = 42, bar = "bar" }
let c = C()

let rawPointer = Unmanaged.passUnretained(c).toOpaque()

let pointer = rawPointer.bindMemory(to: C.self, capacity: 1)
let pointee = pointer.pointee
print(pointee.foo) // EXC_BAD_ACCESS

Here's some LLDB output, which looks strange to me as everything seems alright in pointer until I ask for its pointee:

(lldb) frame variable -L c
scalar: (memtest2.C) c = 0x0000000101d00030 {
0x0000000101d00040:   foo = 42
0x0000000101d00048:   bar = "bar"
}
(lldb) frame variable -L rawPointer
0x00000001005e2e08: (UnsafeMutableRawPointer) rawPointer = {
scalar:   _rawValue = 0x0000000101d00030 {
0x0000000101d00040:     foo = 42
0x0000000101d00048:     bar = "bar"
  }
}
(lldb) frame variable -L pointer
0x00000001005e2e10: (UnsafeMutablePointer<memtest2.C>) pointer = 0x0000000101d00030
(lldb) frame variable -L pointer._rawValue
scalar: (memtest2.C) pointer._rawValue = 0x0000000101d00030 {
0x0000000101d00040:   foo = 42
0x0000000101d00048:   bar = "bar"
}
(lldb) frame variable -L pointee
0x00000001005e2e18: (memtest2.C) pointee = 0x00000001005b65d8 {
0x00000001005b65e8:   foo = 140736790071664
0x00000001005b65f0:   bar = ""
}

I've also tried assumingMemoryBound(to:), load(as:), or simply:

let pointer = UnsafePointer<C>(bitPattern: Int(bitPattern: rawPointer))!
print(pointer.pointee.foo) // EXC_BAD_ACCESS

But I always get this EXC_BAD_ACCESS error. What is going on here?

nyg
  • 2,380
  • 3
  • 25
  • 40
  • Try `let obj = Unmanaged.fromOpaque(rawPointer).takeUnretainedValue();print(obj.foo)` . – OOPer Sep 19 '17 at 21:59
  • @OOPer My end-goal is not to access the `C` instance but really to obtain a valid typed pointer from the raw one. – nyg Sep 20 '17 at 18:14
  • Then I cannot understand your _end-goal_. Anyway, your code only works when `rawPointer` is a pointer to a reference to an instance of `C`, `Unmanaged.passUnretained(c).toOpaque()` does not make it. – OOPer Sep 20 '17 at 21:26
  • `let obj = Unmanaged.fromOpaque(rawPointer).takeUnretainedValue()` *is* a valid typed pointer to the C instance, and is the correct answer to your question. Compare https://stackoverflow.com/questions/33294620/how-to-cast-self-to-unsafemutablepointervoid-type-in-swift. – Martin R Sep 23 '17 at 11:36
  • @MartinR `takeUnretainedValue()` returns the `c` instance when I would like to get a `Unsafe[Mutable]Pointer` instance (from the `UnsafeMutableRawPointer`). – nyg Sep 23 '17 at 11:48
  • @nyg: Your `c` variable is already a pointer to a `C` instance (because classes are reference types). And `obj` will also be a pointer to a `C` instance. – Martin R Sep 23 '17 at 11:49
  • @MartinR So you mean that `c` (and not an instance of `UnsafeMutablePointer`) is the typed version of an `UnsafeMutableRawPointer`? I can understand this but it would seem quite strange to me. Can't I have then a pointer on that pointer? Such as `pointer.pointee == c`? – nyg Sep 23 '17 at 12:00
  • If you declare `c` as a *variable* then `withUnsafeMutablePointer(to: &c)` gives you a pointer to that variable. That would allow you to modify that variable. Is that really what you want? – Martin R Sep 23 '17 at 12:04
  • @nyg: Perhaps you can provide more information what the pointer is needed for (some concrete code). – Martin R Sep 23 '17 at 13:40
  • @MartinR I was trying to do something like this: https://pastebin.com/1tGhYAsG. I guess I should rather not have that pointer property, I'd be better off storing the value for stringValue and intValue directly in the init... In my case, using withUnsafeMutablePointer would not be useful because a) the created pointer shouldn't be used outside the closure (says doc) and b) it doesn't show the address where the c instance is located. – nyg Sep 23 '17 at 22:33
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/155181/discussion-between-martin-r-and-nyg). – Martin R Sep 24 '17 at 08:30

1 Answers1

0

My rawPointer points to where the c instance data is located. It is not, as I expected, a pointer on the reference. Classes are reference types: the value of c is the memory address of where the class instance data is located. Yet the toOpaque() doc was clear:

Unsafely converts an unmanaged class reference to a pointer.

(toOpaque() actually calls unsafeBitCast(c, UnsafeMutableRawPointer.self))

To have a pointer on the reference one can simply do:

let referencePointer = UnsafeMutablePointer<C>(&c)

As my rawPointer points to the instance data, doing pointer.pointee tells the runtime that the first word of the instance data is a (or its) reference. Which of course isn't true nor makes sense.

Illustrating: (I've slightly change my initial code: both foo and bar are Int)

(lldb) frame variable c
(testmem.C) c = 0x0000000101833580 (foo = 42, bar = 84)

This (0x0000000101833580) is where the instance data is located. Let's see what the memory contains at this address:

(lldb) memory read 0x0000000101833580
0x101833580: e0 65 5b 00 01 00 00 00 02 00 00 00 00 00 00 00
0x101833590: 2a 00 00 00 00 00 00 00 54 00 00 00 00 00 00 00

I learned that the first word (e0 65 5b 00 01 00 00 00) is type data, the second (02 00 00 00 00 00 00 00) is reference counts (I don't know more about this), and the rest is the instance data. Indeed, 0x2a is 42 and 0x54 is 84. The value of foo and bar.

Doing pointer.pointee means telling the runtime that the first word (e0 65 5b 00 01 00 00 00 or 0x00000001005b65e0) is a reference pointing to where our instance data is located (which is obviously not the case)! Implying that pointer.pointee.foo is located at 0x00000001005b65e0 + 16 (0x00000001005b65f0) and bar at + 24 (0x00000001005b65f0).

(lldb) memory read 0x00000001005b65e0
0x1005b65e0: a9 65 5b 00 01 80 1d 00 80 62 5b 00 03 00 00 00
0x1005b65f0: 70 e9 10 9e ff 7f 00 00 00 00 00 00 00 00 00 00

foo contains 0x0000007fff9e10e970 which in decimal is 140735845296496 and corresponds to:

(lldb) frame variable -L pointee
0x00000001005e2e18: (testmem.C) pointee = 0x00000001005b65e0 {
0x00000001005b65f0:   foo = 140735845296496 // this
0x00000001005b65f8:   bar = 0
}

And as this data wasn't allocated by my program we don't have access to it, hence the EXC_BAD_ACCESS error.

Life makes sense now .

nyg
  • 2,380
  • 3
  • 25
  • 40