Thread: Int64 and naked call

  1. #1
    Ugly C Lover audinue's Avatar
    Join Date
    Jun 2008
    Location
    Indonesia
    Posts
    489

    [SOLVED] Int64 and naked call

    Oops. What's the meaning of this error:

    Code:
    #include <stdio.h>
    
    void lltest(long long ll) {
      printf("%I64d ??\n", ll);
    }
    
    int main(void) {
      //lltest(100000000001); //OK
    
      long long ll = 100000000001;
      printf("%I64d OK\n", ll);
    
      __asm {
        push ll
        call lltest
        add esp, 4 //clean up call stack
      };
    
      /* 
       Run-Time Check Failure #0 - The value of ESP was not 
       properly saved across a function call. This is usually 
       a result of calling a function declared with one
       calling convention with a function pointer declared
       with a different calling convention.
      */
    
      getchar();
      return 0;
    }
    What's happened here? Any clue please?
    What should I do to get the lltest function to work?

    Thanks in advance.

    EDIT:
    This even wouldn't work.

    Code:
    void __stdcall lltest(long long ll) {
    Last edited by audinue; 02-21-2009 at 12:01 PM.
    Just GET it OFF out my mind!!

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    gcc -S prog.c
    And see how it generates the code to push a long-long.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Ugly C Lover audinue's Avatar
    Join Date
    Jun 2008
    Location
    Indonesia
    Posts
    489
    Thanks, I'm using VC currently. And it seems VC pushes 2 parameters into lltest.

    Yeah, I think I will change the question somehow.

    Code:
    10000000001 --> 2 and 0x540BE401 (1410065409)
    Anyone know how to count those two numbers?

    I've got:
    Code:
    0x540BE401 = 10000000001 ^ 1 + 1 (^ is XOR)
    Is it right?
    How do I get the 2?
    Just GET it OFF out my mind!!

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    10000000001 = 0x2540BE401
    No magic, just two 32-bit numbers, one is the MSB and the other is the LSB (as it were).
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Ugly C Lover audinue's Avatar
    Join Date
    Jun 2008
    Location
    Indonesia
    Posts
    489
    I found this (based on speculation):
    Code:
      long long x = 100082738001;
      long high = x >> 32;
      long low  = (long)(x ^ 1 - 1);
    I'm not sure if right (because of casting).
    Just GET it OFF out my mind!!

  6. #6
    Ugly C Lover audinue's Avatar
    Join Date
    Jun 2008
    Location
    Indonesia
    Posts
    489
    Sorry for troubling you Salem, and thanks for your replies.

    I forgot LOWORD and HIWORD macros. xD

    Code:
    #define HILONG(ll) (ll >> 32 & LONG_MAX)
    #define LOLONG(ll) ((long)(ll))
    Modified them LOL.

    And about:
    Run-Time Check Failure #0 - The value of ESP was not
    properly saved across a function call. This is usually
    a result of calling a function declared with one
    calling convention with a function pointer declared
    with a different calling convention.
    Fixed with:
    Code:
    add esp, 8
    Truly I know nothing about this esp thing, but I just know, if we pushed x parameter then we should clean the call stack by
    Code:
    add esp, 2 ^ (x + 1) (^ is power)
    Awesome! Thank you. EXP++.
    Last edited by audinue; 02-21-2009 at 11:16 AM.
    Just GET it OFF out my mind!!

  7. #7
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Esp is the stack pointer, I believe.
    In debug mode, VS makes a copy of it before calling a function and then restores it and compares the backup to the current value of esp after cleaning the stack (if it's responsible for it) and raises this warning if they don't match.
    Basically, what it says is that either the function or you failed to clean the stack properly.
    Can also happen if you call a function with a different calling convention that you've specified.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  8. #8
    Ugly C Lover audinue's Avatar
    Join Date
    Jun 2008
    Location
    Indonesia
    Posts
    489
    Wow, thanks Elysia for the info.

    Somewhat I just curious about registers.

    Code:
    __asm {
        mov  eax, dword ptr [ebp-8]
        push eax
        mov  ecx, dword ptr [ll]
        push ecx
        call lltest
        add esp, 8
      };
    Why do people write code like above instead of:
    Code:
      __asm {
        push dword ptr [ebp-8]
        push dword ptr [ll]
        call lltest
        add esp, 8
      };
    Is it about calling convention (cledcl, ... -_-')?
    What will happen if we broke the convention (by not using the register).

    Register leak? Is it exists?
    Just GET it OFF out my mind!!

  9. #9
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    I don't know why people do x vs y. You'll have to ask the assembly expert.
    The calling convention only comes to play if you call function generated by the compiler from your assembly, mostly. It can also come into play with DLLs or calls to external functions. Basically, that boils down to function prototypes being correct.
    And you define your own convention when calling your own assembly routine from your assembly code. Register leak does not exist.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  10. #10
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    You are pushing a long-long, which is 8 bytes, but you only adjust esp by 4 bytes after the call. This leaves the whole stack in disarray. Also, if the calling convention is stdcall, then the callee cleans the stack, not the caller (as it would be for a cdecl call). So you're completely hosing the stack either way.

    On top of that, are you ensuring that the stack is aligned to 8 bytes before you push? If not, you're producing an inefficiency by unaligned memory access.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  11. #11
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by audinue View Post
    Wow, thanks Elysia for the info.

    Somewhat I just curious about registers.

    Code:
    __asm {
        mov  eax, dword ptr [ebp-8]
        push eax
        mov  ecx, dword ptr [ll]
        push ecx
        call lltest
        add esp, 8
      };
    Why do people write code like above instead of:
    Code:
      __asm {
        push dword ptr [ebp-8]
        push dword ptr [ll]
        call lltest
        add esp, 8
      };
    Is it about calling convention (cledcl, ... -_-')?
    You don't have to do it one way or the other. The first version is more like something a compiler would generate. The second version is actually the only example of a mem-to-mem move operation that's even possible on x86.

    What will happen if we broke the convention (by not using the register).
    Nothing. It's not using a register, it just happens to be written that way. The only thing that matters is what ends up on the stack. If this were a fastcall convention, you wouldn't be pushing anything at all, just passing parameters in the registers.

    Whether a call is stdcall or cdecl can't be determined by looking at the code leading up to the call instruction. You have to examine the function which gets called to see if it cleans the stack itself -- if it does, it's stdcall. If not, it's cdecl.

    It's more difficult to determine calling convention by examining the calling function, because stack pops can be deferred up until the last moment. If not many functions are being called, it's less efficient to pop the args each time -- instead, you can wait until the end of the calling function and fix the entire stack at once.
    Last edited by brewbuck; 02-21-2009 at 02:54 PM.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

Popular pages Recent additions subscribe to a feed