www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Bus error interfacing with C function returning large struct

reply Jacob Carlborg <doob me.com> writes:
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
Apr 16 2013
next sibling parent Jacob Carlborg <doob me.com> writes:
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
Apr 16 2013
prev sibling next sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
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.
Apr 16 2013
next sibling parent Jacob Carlborg <doob me.com> writes:
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 -- /Jacob Carlborg
Apr 16 2013
prev sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
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.
Apr 17 2013
prev sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
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.
Apr 17 2013
parent reply Jacob Carlborg <doob me.com> writes:
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
Apr 17 2013
parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
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.
Apr 17 2013
parent Jacob Carlborg <doob me.com> writes:
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
Apr 17 2013