Skip to content
This repository was archived by the owner on Aug 31, 2021. It is now read-only.

Commit c45076b

Browse files
committed
[[ Bug 20644 ]] Fix calls to struct-returning obj-c methods
Sometimes structs are returned from Objective-C methods in registers instead of using the address provided as a parameter. In the latter case `objc_msgSend_stret` should be used instead of `objc_msgSend`. The rules for when this happens are architecture and struct-size dependent and FFI calls will crash if the wrong form is used. This patch updates the code to use `objc_msgSend_stret` just in case the method returns a struct and - on arm64, the struct size > 64 bytes - on arm, the struct size > 4 bytes - on x86_64, the struct size > 16 bytes - on i386, the struct size > 8 bytes or a power of 2.
1 parent a615a61 commit c45076b

File tree

3 files changed

+46
-5
lines changed

3 files changed

+46
-5
lines changed

docs/notes/bugfix-20644.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Fix calls to struct-returning obj-c methods

libscript/src/script-execute.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,11 @@ class MCScriptForeignInvocation
268268
/* There are different variants of objc_msgSend we need to use
269269
* depending on architecture and return type:
270270
* struct return
271-
* all but arm64: _stret
271+
* arm64: _stret if struct size > 64 bytes
272+
* arm: _stret if struct size > 4 bytes
273+
* x86-64: _stret if struct size > 16 bytes
274+
* i386: _stret if struct size >= 8 bytes or
275+
* exactly 1, 2, or 4 bytes
272276
* float return
273277
* arm: no difference
274278
* i386: _fpret
@@ -289,22 +293,31 @@ class MCScriptForeignInvocation
289293

290294
ffi_cif *t_cif = (ffi_cif *)p_handler->objc.function_cif;
291295
void (*t_objc_msgSend)() = nullptr;
296+
size_t t_rsize = t_cif->rtype->size;
292297
#if defined(__ARM64__)
293-
t_objc_msgSend = (void(*)())objc_msgSend;
298+
if (t_cif->rtype->type == FFI_TYPE_STRUCT &&
299+
t_rsize > 64)
300+
t_objc_msgSend = (void(*)())objc_msgSend_stret;
301+
else
302+
t_objc_msgSend = (void(*)())objc_msgSend;
294303
#elif defined(__ARM__)
295-
if (t_cif->rtype->type == FFI_TYPE_STRUCT)
304+
if (t_cif->rtype->type == FFI_TYPE_STRUCT &&
305+
t_rsize > 4)
296306
t_objc_msgSend = (void(*)())objc_msgSend_stret;
297307
else
298308
t_objc_msgSend = (void(*)())objc_msgSend;
299309
#elif defined(__X86_64__)
300-
if (t_cif->rtype->type == FFI_TYPE_STRUCT)
310+
if (t_cif->rtype->type == FFI_TYPE_STRUCT &&
311+
t_rsize > 16)
301312
t_objc_msgSend = (void(*)())objc_msgSend_stret;
302313
else if (t_cif->rtype->type == FFI_TYPE_LONGDOUBLE)
303314
t_objc_msgSend = (void(*)())objc_msgSend_fpret;
304315
else
305316
t_objc_msgSend = (void(*)())objc_msgSend;
306317
#elif defined(__I386__)
307-
if (t_cif->rtype->type == FFI_TYPE_STRUCT)
318+
if (t_cif->rtype->type == FFI_TYPE_STRUCT &&
319+
(t_rsize >= 8 ||
320+
(t_rsize & (t_rsize - 1)) == 0))
308321
t_objc_msgSend = (void(*)())objc_msgSend_stret;
309322
else if (t_cif->rtype->type == FFI_TYPE_FLOAT ||
310323
t_cif->rtype->type == FFI_TYPE_DOUBLE ||

tests/lcb/vm/interop-objc.lcb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,31 @@ end handler
121121

122122
---------
123123

124+
// NSRange is a pair of (natural sized) unsigned integers
125+
public foreign type NSRange binds to "MCAggregateTypeInfo:pp"
126+
127+
// NSRange is 8 bytes on 64-bit arch, 4 bytes on 32-bit
128+
// so will use objc_msgSend_stret on 32-bit arch, but objc_msgSend on
129+
// 64 bit.
130+
foreign handler RangeOfString(in pHaystack as ObjcId, in pNeedle as ObjcId) returns NSRange binds to "objc:NSString.-rangeOfString:"
131+
132+
public handler TestReturnNSRange()
133+
if not the operating system is in ["mac", "ios"] then
134+
skip test "return NSRange struct succeeds" because "not implemented on" && the operating system
135+
return
136+
end if
137+
138+
variable tRange as NSRange
139+
unsafe
140+
put RangeOfString(StringToNSString("cartoon"), \
141+
StringToNSString("art")) into tRange
142+
end unsafe
143+
144+
variable tRangeList as List
145+
put tRange into tRangeList
146+
147+
test "return NSRange struct succeeds" when \
148+
tRangeList is [1,3]
149+
end handler
150+
124151
end module

0 commit comments

Comments
 (0)