Advertisement

Mac OS X 32-bits crash

Started by March 25, 2012 11:00 PM
27 comments, last by WitchLord 12 years, 7 months ago
The disassembly of the StringFactory doesn't show any difference in the calling convention. It is taking the function arguments on the stack, returning the value in eax, and makes sure not to modify the registers ebp, ebx, esi, and edi.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

It might be a difference in one of the other calling conventions. The fact that it fails when calling the StringFactory may not mean the problem is with the StringFactory itself, it could be a previous call that is causing the problem.

Can you run the test_features project (from the svn)? I need to see which of the following tests pass:



// The following tests are designed specifically to test the native calling conventions.
// These are grouped by calling convention and ordered in increasing complexity.
{
// cdecl
if( TestExecute() ) goto failed; else printf("-- TestExecute passed\n");
if( TestCDeclReturn::Test() ) goto failed; else printf("-- TestCDeclReturn passed\n");
if( TestExecute1Arg() ) goto failed; else printf("-- TestExecute1Arg passed\n");
if( TestExecute2Args() ) goto failed; else printf("-- TestExecute2Args passed\n");
if( TestExecute4Args() ) goto failed; else printf("-- TestExecute4Args passed\n");
if( TestExecute4Argsf() ) goto failed; else printf("-- TestExecute4Argsf passed\n");
if( TestExecuteMixedArgs() ) goto failed; else printf("-- TestExecuteMixedArgs passed\n");
if( TestExecute32Args() ) goto failed; else printf("-- TestExecute32Args passed\n");
if( TestExecute32MixedArgs() ) goto failed; else printf("-- TestExecute32MixedArgs passed\n");
if( TestCDecl_Class() ) goto failed; else printf("-- TestCDecl_Class passed\n");
if( TestCDecl_ClassA() ) goto failed; else printf("-- TestCDecl_ClassA passed\n");
if( TestCDecl_ClassC() ) goto failed; else printf("-- TestCDecl_ClassC passed\n");
if( TestCDecl_ClassD() ) goto failed; else printf("-- TestCDecl_ClassD passed\n");
if( TestCDecl_ClassK() ) goto failed; else printf("-- TestCDecl_ClassK passed\n");

// cdecl_objlast and cdecl_objfirst
if( TestReturnString::Test() ) goto failed; else printf("-- TestReturnString passed\n");
if( TestNegateOperator() ) goto failed; else printf("-- TestNegateOperator passed\n");
if( TestReturnWithCDeclObjFirst() ) goto failed; else printf("-- TestReturnWithCDeclObjFirst passed\n");

// thiscall
if( TestExecuteThis32MixedArgs() ) goto failed; else printf("-- TestExecuteThis32MixedArgs passed\n");
if( TestNotComplexThisCall() ) goto failed; else printf("-- TestNotComplexThisCall passed\n");
if( TestVirtualMethod() ) goto failed; else printf("-- TestVirtualMethod passed\n");
if( TestMultipleInheritance() ) goto failed; else printf("-- TestMultipleInheritance passed\n");
if( TestVirtualInheritance() ) goto failed; else printf("-- TestVirtualInheritance passed\n");

// stdcall
if( TestStdcall4Args() ) goto failed; else printf("-- TestStdcall4Args passed\n");
if( TestNotComplexStdcall() ) goto failed; else printf("-- TestNotComplexStdcall passed\n");
}


Comment out all the other tests above these in the main() function (in main.cpp), as they test features, etc. But before those will work the native calling conventions must work.

Based on which of the tests for the calling conventions that work or not it should be easier to narrow in on where the problem really lies.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Advertisement
Every one of the tests crashes inside [font=courier new,courier,monospace]CallSystemFunctionNative[/font] when it gets to "[font=courier new,courier,monospace]asm __volatile__[/font]"

[font=courier new,courier,monospace]TestExecute [/font]crashes calling "[font=courier new,courier,monospace]cfunction[/font]" from "[font=courier new,courier,monospace]ExecuteString[/font]"
[font=courier new,courier,monospace]TestCDeclReturn::Test[/font] crashes calling "[font=courier new,courier,monospace]reti64[/font]" from "[font=courier new,courier,monospace]ExecuteString[/font]"
[font=courier new,courier,monospace]TestExecute1Arg [/font]crashes calling "[font=courier new,courier,monospace]cfunction[/font]"
...etc

Only one that didn't crash was [font=courier new,courier,monospace]TestVirtualInheritance[/font], but I think it got skipped because virtual inheritance wasn't detected.

Let me know if there's anything more I can do.

Edit: I'm on

OS 10.7.3 and Xcode 4.3.2



Edit 2: If I compile for 64 bit, all tests pass (Except TextVirtualInheritance which is skipped).

If all of them crashes, then I can only think that the inline assembly in as_callfunc_x86.cpp is causing this.

Something is going wrong when the code is being compiled.

I'll need the assembler code produced for the CallCDeclFunction(). The same way you sent it for the StringFactory.

Hopefully I'll spot what is wrong. It might be a register that is getting incorrectly mangled (though the inline assembler is using the clobber lists).




[edit]

Matt, in your previous post you were using XCode 4.2.1. Was that with Mac OS 10.7.3? or a previous version of Mac OS too? At that time we got it to work. I'm wondering if this might not be a bug in XCode itself (or rather the compiler it is using).

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

I think I was on 10.7.2 and I updated to 10.7.3 when I updated Xcode.
Can't remember if I tried 2.23.0 before updating or not, sorry.

Here's the assembly for CallCDeclFunction, I think
http://pastebin.com/VpMwk3HF

Thanks

Edit: Btw, it's crashing on line 47 of that paste, 2 lines after "[font=courier new,courier,monospace]copyloop:"[/font] at "[font=courier new,courier,monospace][color=#000000]pushl [color=#009900]([color=#339933]%[color=#00007F]eax[color=#009900])[color=#000000]"[/font]
In which test does it crash on this line? The TextExecute() test? It shouldn't even be hitting that instruction in that test.

I'm not seeing anything wrong with the assembly that was produced.

I can only imagine that LLVM that XCode is using is not recognizing some instructions, possibly "pushl (%eax)". It's a longshot, but perhaps changing the inline assembly a bit might work. Can you try changing the inline assembly in CallCDeclFunction to the following:



asm __volatile__(
_S(CLEAR_FPU_STACK) "\n"
"pushl %%ebx \n"
"movl %%edx, %%ebx \n"

// Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
// It is assumed that when entering this function, the stack pointer is already aligned, so we need
// to calculate how much we will put on the stack during this call.
"movl 4(%%ebx), %%eax \n" // paramSize
"addl $4, %%eax \n" // counting esp that we will push on the stack
"movl %%esp, %%ecx \n"
"subl %%eax, %%ecx \n"
"andl $15, %%ecx \n"
"movl %%esp, %%eax \n"
"subl %%ecx, %%esp \n"
"pushl %%eax \n" // Store the original stack pointer

"movl 4(%%ebx), %%ecx \n" // paramSize
"movl 0(%%ebx), %%eax \n" // args
"addl %%ecx, %%eax \n" // push arguments on the stack
"cmp $0, %%ecx \n"
"je endcopy \n"
"copyloop: \n"
"subl $4, %%eax \n"
// "pushl (%%eax) \n" <----- comment out
"movl (%%eax), %%edx \n" <----- add
"pushl %%edx \n" <----- add
"subl $4, %%ecx \n"
"cmp $0, %%ecx \n" <------ add
"jne copyloop \n"
"endcopy: \n"
"call *8(%%ebx) \n"
"addl 4(%%ebx), %%esp \n" // pop arguments

// Pop the alignment bytes
"popl %%esp \n"
"popl %%ebx \n"
: // output
: "d"(&args) // input - pass pointer of args in edx
: "%eax", "%ecx", "%esp" // clobber
);


I replaced the 'pushl (%%eax)' with 'movl (%%eax), %%edx' and 'pushl %%edx'. Then I also added an explicit 'cmp $0, %%ecx' before the jne instruction.

Neither of these changes should be necessary, but who knows what LLVM does.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Advertisement
Yep, this is in [font=courier new,courier,monospace]TestExecute()[/font]
I tried that code, it still crashes. This is what it looks like when it crashes with call stack and disassembly:
http://snag.gy/jDB74.jpg
OK. So the code changes didn't make a difference. The problem is happening when attempting to access the memory pointed to by the address in eax.

TestExecute() is calling a global function without any arguments, so it shouldn't even try to push any arguments on the stack.

Do you think you can single step through the CallCDeclFunction() at assembler level and check what the values are on the registers? The code really should be jumping to endcopy at the first 'je endcopy'. For some reason it isn't doing that, and instead tries to read from 'eax' that is seemingly pointing to some invalid memory position.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Nevermind. I figured it out.

In the inline assembly I'm asking the compiler to pass the address of the parameter 'args' in the edx register. I'm then using this address to determine the location of the other two parameters so I do not have to rely on ebp or esp to get to them. Relying on ebp or esp is not possible because they are used differently in different optimization levels.

The problem here is that for some reason the compiler is not passing the address of the parameter, but instead the address of a local temporary variable with the same value. Because of this my code fails to retrieve the correct values for 'paramSize' and 'func'.

I'll have to rewrite the inline assembly to pass each of the values in explicit registers to avoid this unexpected behaviour.

Thanks for all the help in figuring this out.

Unfortunately I'll be traveling today, and will only be back in a week. I will only be able to work on this fix when I get back.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

One thing that might resolve the problem until I get to reimplement the inline assembler code is to remove the macros "UNUSED_VAR()" in the CallCDeclFunction, etc. It might be because of these macros that the compiler decides to copy the parameters to local variables.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

This topic is closed to new replies.

Advertisement