Thread overview
Bus error interfacing with C function returning large struct
Apr 16, 2013
Jacob Carlborg
Apr 16, 2013
Jacob Carlborg
Apr 16, 2013
John Colvin
Apr 17, 2013
Jacob Carlborg
Apr 17, 2013
John Colvin
Apr 17, 2013
John Colvin
Apr 17, 2013
Jacob Carlborg
Apr 17, 2013
John Colvin
Apr 17, 2013
Jacob Carlborg
April 16, 2013
The following code will result in a bus error on Mac OS X 10.8.2 using DMD 2.062 compiled for 32bit (segfault on 64bit). A couple of notes:

* This code runs fine on Mac OS X 10.6.3
* It seems the struct has to be over 64 bits in size
* "foo" need to take an argument

Dissassembly at the bottom.

I think this is the same problem I had with interfacing with the objc_msgSend_stret function, see other post:

http://forum.dlang.org/thread/kkefk8$2663$1@digitalmars.com

C code:

struct Foo
{
    int a;
    int b;
    int c;
};

typedef struct Foo Foo;

Foo foo (int a)
{
    Foo f;
    f.a = 1;
    f.b = 2;
    f.c = 3;
    return f;
}

D code:

struct Foo
{
    int a;
    int b;
    int c;
}

extern (C) Foo foo (int a);

Foo bar ()
{
    return foo(0);
}

extern (C) int printf(in char*, ...);

void main ()
{
    auto frame = bar();
    printf("a=%d b=%d c=%d\n".ptr, frame.a, frame.b, frame.c);
}

GDB session with dissassembly:

http://pastebin.com/rguwXucR

Dissassembly of the corresponding C program compiled with Clang:

http://pastebin.com/MG8Tnkzp

Dissassembly of "foo" on Mac OS X 10.8.2 using Clang 4.1:

http://pastebin.com/0jKqksxx

Dissassembly of "foo" on Mac OS X 10.6.3 using Clang 1.5:

http://pastebin.com/kbdfuVcB

-- 
/Jacob Carlborg
April 16, 2013
On 2013-04-16 21:26, Jacob Carlborg wrote:
> The following code will result in a bus error on Mac OS X 10.8.2 using
> DMD 2.062 compiled for 32bit (segfault on 64bit). A couple of notes:
>
> * This code runs fine on Mac OS X 10.6.3
> * It seems the struct has to be over 64 bits in size
> * "foo" need to take an argument

If I a store the return value of "foo" in a temporary variable in "bar" the bus error goes away.

-- 
/Jacob Carlborg
April 16, 2013
On Tuesday, 16 April 2013 at 19:26:09 UTC, Jacob Carlborg wrote:
> The following code will result in a bus error on Mac OS X 10.8.2 using DMD 2.062 compiled for 32bit (segfault on 64bit). A couple of notes:
>
> * This code runs fine on Mac OS X 10.6.3
> * It seems the struct has to be over 64 bits in size
> * "foo" need to take an argument
>
> Dissassembly at the bottom.
>
> I think this is the same problem I had with interfacing with the objc_msgSend_stret function, see other post:
>
> http://forum.dlang.org/thread/kkefk8$2663$1@digitalmars.com
>
> C code:
>
> struct Foo
> {
>     int a;
>     int b;
>     int c;
> };
>
> typedef struct Foo Foo;
>
> Foo foo (int a)
> {
>     Foo f;
>     f.a = 1;
>     f.b = 2;
>     f.c = 3;
>     return f;
> }
>
> D code:
>
> struct Foo
> {
>     int a;
>     int b;
>     int c;
> }
>
> extern (C) Foo foo (int a);
>
> Foo bar ()
> {
>     return foo(0);
> }
>
> extern (C) int printf(in char*, ...);
>
> void main ()
> {
>     auto frame = bar();
>     printf("a=%d b=%d c=%d\n".ptr, frame.a, frame.b, frame.c);
> }
>
> GDB session with dissassembly:
>
> http://pastebin.com/rguwXucR
>
> Dissassembly of the corresponding C program compiled with Clang:
>
> http://pastebin.com/MG8Tnkzp
>
> Dissassembly of "foo" on Mac OS X 10.8.2 using Clang 4.1:
>
> http://pastebin.com/0jKqksxx
>
> Dissassembly of "foo" on Mac OS X 10.6.3 using Clang 1.5:
>
> http://pastebin.com/kbdfuVcB

Some observations:

Assuming main is doing everything properly, it's passing a pointer to 12 bytes of stack space to bar in eax (as per the D ABI). bar then puts that pointer on the stack for foo (as per the IA32 OS X ABI). However, it looks to me like it's in the wrong place, because of this line:
0x00002673 <D4test3barFZS4test3Foo+11>: sub    $0x8,%esp

This is just from a quick glance, I may have added my hexes wrongly.
April 17, 2013
On 2013-04-17 01:03, John Colvin wrote:

> Some observations:
>
> Assuming main is doing everything properly, it's passing a pointer to 12
> bytes of stack space to bar in eax (as per the D ABI). bar then puts
> that pointer on the stack for foo (as per the IA32 OS X ABI). However,
> it looks to me like it's in the wrong place, because of this line:
> 0x00002673 <D4test3barFZS4test3Foo+11>: sub    $0x8,%esp
>
> This is just from a quick glance, I may have added my hexes wrongly.

I don't know, that's why I'm asking here :) This is what Martin Nowak said in the bug report:

"Seems like OSX deviates from the SysV IA-32 ABI for memory struct returns. The callee does NOT return the hidden pointer in EAX.
Instead the caller has to use the value passed as argument."

http://d.puremagic.com/issues/show_bug.cgi?id=9931#c7

The ABI documentation says:

"When a function returns a structure or union larger than 8 bytes, the caller passes a pointer to appropriate storage as the first argument to the function."

And:

"The called function returns structures according to their aligned size.

* Structures 1 or 2 bytes in size are placed in EAX.
* Structures 4 or 8 bytes in size are placed in: EAX and EDX.
* Structures of other sizes are placed at the address supplied by the caller. For example, the C++ language occasionally forces the compiler to return a value in memory when it would normally be returned in registers. See “Passing Arguments” for more information."

http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html#//apple_ref/doc/uid/TP40002492-SW4

-- 
/Jacob Carlborg
April 17, 2013
On Tuesday, 16 April 2013 at 23:03:44 UTC, John Colvin wrote:
> On Tuesday, 16 April 2013 at 19:26:09 UTC, Jacob Carlborg wrote:
>> The following code will result in a bus error on Mac OS X 10.8.2 using DMD 2.062 compiled for 32bit (segfault on 64bit). A couple of notes:
>>
>> * This code runs fine on Mac OS X 10.6.3
>> * It seems the struct has to be over 64 bits in size
>> * "foo" need to take an argument
>>
>> Dissassembly at the bottom.
>>
>> I think this is the same problem I had with interfacing with the objc_msgSend_stret function, see other post:
>>
>> http://forum.dlang.org/thread/kkefk8$2663$1@digitalmars.com
>>
>> C code:
>>
>> struct Foo
>> {
>>    int a;
>>    int b;
>>    int c;
>> };
>>
>> typedef struct Foo Foo;
>>
>> Foo foo (int a)
>> {
>>    Foo f;
>>    f.a = 1;
>>    f.b = 2;
>>    f.c = 3;
>>    return f;
>> }
>>
>> D code:
>>
>> struct Foo
>> {
>>    int a;
>>    int b;
>>    int c;
>> }
>>
>> extern (C) Foo foo (int a);
>>
>> Foo bar ()
>> {
>>    return foo(0);
>> }
>>
>> extern (C) int printf(in char*, ...);
>>
>> void main ()
>> {
>>    auto frame = bar();
>>    printf("a=%d b=%d c=%d\n".ptr, frame.a, frame.b, frame.c);
>> }
>>
>> GDB session with dissassembly:
>>
>> http://pastebin.com/rguwXucR
>>
>> Dissassembly of the corresponding C program compiled with Clang:
>>
>> http://pastebin.com/MG8Tnkzp
>>
>> Dissassembly of "foo" on Mac OS X 10.8.2 using Clang 4.1:
>>
>> http://pastebin.com/0jKqksxx
>>
>> Dissassembly of "foo" on Mac OS X 10.6.3 using Clang 1.5:
>>
>> http://pastebin.com/kbdfuVcB
>
> Some observations:
>
> Assuming main is doing everything properly, it's passing a pointer to 12 bytes of stack space to bar in eax (as per the D ABI). bar then puts that pointer on the stack for foo (as per the IA32 OS X ABI). However, it looks to me like it's in the wrong place, because of this line:
> 0x00002673 <D4test3barFZS4test3Foo+11>: sub    $0x8,%esp
>
> This is just from a quick glance, I may have added my hexes wrongly.

I was wrong. Ignore the previous post.
April 17, 2013
On Tuesday, 16 April 2013 at 19:26:09 UTC, Jacob Carlborg wrote:
> The following code will result in a bus error on Mac OS X 10.8.2 using DMD 2.062 compiled for 32bit (segfault on 64bit). A couple of notes:
>
> * This code runs fine on Mac OS X 10.6.3
> * It seems the struct has to be over 64 bits in size
> * "foo" need to take an argument
>
> Dissassembly at the bottom.
>
> I think this is the same problem I had with interfacing with the objc_msgSend_stret function, see other post:
>
> http://forum.dlang.org/thread/kkefk8$2663$1@digitalmars.com
>
> C code:
>
> struct Foo
> {
>     int a;
>     int b;
>     int c;
> };
>
> typedef struct Foo Foo;
>
> Foo foo (int a)
> {
>     Foo f;
>     f.a = 1;
>     f.b = 2;
>     f.c = 3;
>     return f;
> }
>
> D code:
>
> struct Foo
> {
>     int a;
>     int b;
>     int c;
> }
>
> extern (C) Foo foo (int a);
>
> Foo bar ()
> {
>     return foo(0);
> }
>
> extern (C) int printf(in char*, ...);
>
> void main ()
> {
>     auto frame = bar();
>     printf("a=%d b=%d c=%d\n".ptr, frame.a, frame.b, frame.c);
> }
>
> GDB session with dissassembly:
>
> http://pastebin.com/rguwXucR
>
> Dissassembly of the corresponding C program compiled with Clang:
>
> http://pastebin.com/MG8Tnkzp
>
> Dissassembly of "foo" on Mac OS X 10.8.2 using Clang 4.1:
>
> http://pastebin.com/0jKqksxx
>
> Dissassembly of "foo" on Mac OS X 10.6.3 using Clang 1.5:
>
> http://pastebin.com/kbdfuVcB

Martins reply in the bug report correctly identifies the problem, it's a bug in dmds implementation of the OS X IA32 ABI.

This is quite a severe bug, it's only by luck that eax was set to 0 causing an immediate error.

I suggest it should be marked critical.
April 17, 2013
On 2013-04-17 12:05, John Colvin wrote:

> Martins reply in the bug report correctly identifies the problem, it's a
> bug in dmds implementation of the OS X IA32 ABI.

I see. I'm still wondering why it works on Mac OS X 10.6.3, just luck?

> This is quite a severe bug, it's only by luck that eax was set to 0
> causing an immediate error.
>
> I suggest it should be marked critical.

Ok, perhaps we can come up with a better description for the issue as well.

-- 
/Jacob Carlborg
April 17, 2013
On Wednesday, 17 April 2013 at 11:14:52 UTC, Jacob Carlborg wrote:
> On 2013-04-17 12:05, John Colvin wrote:
>
>> Martins reply in the bug report correctly identifies the problem, it's a
>> bug in dmds implementation of the OS X IA32 ABI.
>
> I see. I'm still wondering why it works on Mac OS X 10.6.3, just luck?

clang just happened to put the arguments to foo in to registers different on 10.6.3
Instead of loading the 0 argument in to eax it loaded the pointer to the struct like it would in system V ABI (linux)

I have no idea whether this is a complete coincidence, or whether clang has changed it's approach to the OS X abi, or whether the ABI itself has changed.
April 17, 2013
On 2013-04-17 13:43, John Colvin wrote:

> clang just happened to put the arguments to foo in to registers
> different on 10.6.3
> Instead of loading the 0 argument in to eax it loaded the pointer to the
> struct like it would in system V ABI (linux)
>
> I have no idea whether this is a complete coincidence, or whether clang
> has changed it's approach to the OS X abi, or whether the ABI itself has
> changed.

Ok, I see. The docs for the ABI says:

The called function returns structures according to their aligned size.

* Structures 1 or 2 bytes in size are placed in EAX.
* Structures 4 or 8 bytes in size are placed in: EAX and EDX.
* Structures of other sizes are placed at the address supplied by the caller. For example, the C++ language occasionally forces the compiler to return a value in memory when it would normally be returned in registers. See “Passing Arguments” for more information.



-- 
/Jacob Carlborg