Calling an Objective-C method that returns a struct that is too big to fit in registers will result in a segmentation fault when accessing fields of the returned struct. Example:
// foo.m
#import <Foundation/Foundation.h>
typedef struct
{
int a, b, c, d, e;
} Bar;
@interface Foo : NSObject
-(Bar) getValue;
@end
@implementation Foo
-(Bar) getValue
{
Bar s = { 3, 3, 3, 3, 3 };
return s;
}
@end
// main.d
extern (C) int printf(in char*, ...);
struct Bar
{
int a, b, c, d, e;
}
extern (Objective-C)
interface Foo
{
static Foo alloc() @selector("alloc");
Foo init() @selector("init");
Bar getValue() @selector("getValue");
}
void main()
{
auto f = Foo.alloc.init;
auto b = f.getValue;
printf("%d\n", b.a);
}
Compile with:
clang foo.m -o foo.o
dmd main.d foo.o -L-framework -LFoundation
Running this in a debugger results in:
lldb main
(lldb) target create "main"
Current executable set to 'main' (x86_64).
(lldb) r
Process 84149 launched: 'main' (x86_64)
Process 84149 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #0: 0x00007fff6ac2030a libdyld.dylib`stack_not_16_byte_aligned_error
libdyld.dylib`stack_not_16_byte_aligned_error:
-> 0x7fff6ac2030a <+0>: movdqa %xmm0, (%rsp)
0x7fff6ac2030f <+5>: int3
libdyld.dylib`_dyld_func_lookup:
0x7fff6ac20310 <+0>: pushq %rbp
0x7fff6ac20311 <+1>: movq %rsp, %rbp
Target 0: (main) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
* frame #0: 0x00007fff6ac2030a libdyld.dylib`stack_not_16_byte_aligned_error
frame #1: 0x00007ffeefbff390
frame #2: 0x000000010001f4ac main`_D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ6runAllMFZ9__lambda1MFZv + 40
frame #3: 0x000000010001f33c main`_D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ7tryExecMFMDFZvZv + 32
frame #4: 0x000000010001f417 main`_D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ6runAllMFZv + 139
frame #5: 0x000000010001f33c main`_D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ7tryExecMFMDFZvZv + 32
frame #6: 0x000000010001f2aa main`_d_run_main + 486
frame #7: 0x0000000100000cd0 main`main + 16
frame #8: 0x00007fff6ac20015 libdyld.dylib`start + 1
An Objective-C call like "f.getValue" is supposed to be lowered to this C call [1]:
Bar tmp;
objc_msgSend_stret(&tmp, f, "getValue");
As far as I know this is the same ABI as a regular C function returning a struct.
[1] For more details see the Objective-C ABI documentation: https://github.com/dlang/dmd/blob/master/docs/objective-c_abi.md#returning-a-struct
Comment #1 by doob — 2019-05-10T08:28:20Z
Update the D code for the latest compiler:
extern (C) int printf(in char*, ...);
struct Bar
{
int a, b, c, d, e;
}
extern (Objective-C)
extern class Foo
{
static Foo alloc() @selector("alloc");
Foo init() @selector("init");
Bar getValue() @selector("getValue");
}
void main()
{
auto f = Foo.alloc.init;
auto b = f.getValue;
printf("%d\n", b.a);
}
Comment #2 by destructionator — 2023-10-09T14:45:13Z
Putting a `asm { pop RAX; }` after the getter return seems to correct things back to the proper setting but i doubt that is a right solution.
I also have some trouble calling NSView.setFrame but idk if it is related or not.
Comment #3 by robert.schadek — 2024-12-13T18:59:14Z