1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

Compiler question

Discussion in 'Plugins' started by vefatica, Jun 5, 2010.

  1. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,949
    Likes Received:
    30
    This does what I want, namely, gives a correct string.

    Code:
    #define DW (pDnsRecord->Data.A.IpAddress) // a DWORD, network order
    Sprintf(psz, L"%u.%u.%u.%u", *((BYTE*)&DW) , *((BYTE*)&DW+1) , *((BYTE*)&DW+2) , *((BYTE*)&DW+3));
    #undef DW
    But I don't quite understand how it works (functions with variable argument lists in general). The compiler can't be simply giving pointers to the 4 bytes in DW to Sprintf() because Sprintf() can't know that I want only bytes. So what's the compiler doing?

    Thanks.
     
  2. samintz

    samintz Scott Mintz

    Joined:
    May 20, 2008
    Messages:
    1,189
    Likes Received:
    11
    In a C-style or cdecl function the arguments are pushed onto the stack in
    reverse order. In that way the offset within the stack frame of the first
    argument is always known. The called function parses the arguments off
    the stack as needed. If I pull two bytes from the stack for arg1, then I
    need to pull arg2 at offset 2 from that. And so on. Take a look at the
    va_arg functions.

    Also, in a cdecl function, the caller is responsible for cleaning up the
    stack. For example I can call printf("%u",1,2,3,4,5,6,7,8,9); and that is
    perfectly ok. The callee (printf) would only use the 1st two arguments
    (format string and 1) but the caller knows how many items it pushed and
    cleans up the stack correctly. In a stdcall function, the arguments are
    pushed in order and the callee cleans up before returning.

    Your format string instructs the printf function how to pull the arguments
    from the stack. There are also ANSI rules for automatically making things
    correct sizes when pushed onto the stack. So, in your example, a BYTE
    gets zero extended to WORD and pushed onto the stack. %u expects a WORD
    argument.

    -Scott

    vefatica <> wrote on 06/05/2010 04:36:11 PM:


     
  3. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,949
    Likes Received:
    30
    On Sat, 05 Jun 2010 17:08:44 -0400, samintz <> wrote:

    |Your format string instructs the printf function how to pull the arguments
    |from the stack. There are also ANSI rules for automatically making things
    |correct sizes when pushed onto the stack. So, in your example, a BYTE
    |gets zero extended to WORD and pushed onto the stack. %u expects a WORD
    |argument.

    |---Quote---
    |> This does what I want, namely, gives a correct string.
    |>
    |>
    |> Code:
    |> ---------
    |> #define DW (pDnsRecord->Data.A.IpAddress) // a DWORD, network order
    |> Sprintf(psz, L"%u.%u.%u.%u", *((BYTE*)&DW) , *((BYTE*)&DW+1) ,
    |> *((BYTE*)&DW+2) , *((BYTE*)&DW+3));
    |> #undef DW
    |> ---------

    Actually, "u" means "unsigned" to Sprintf (and AFAICT works with all sizes up to
    32). And why would the compiler decide on WORD? I told it BYTE! But it can't
    push 1 byte onto the stack; there's nothing signalling Sprintf to pop 1 byte. I
    figure the compiler must be pushing a 32-bit value onto the stack and Sprintf
    pops it and treats it according to the format element. eh?
    --
    - Vince
     
  4. Jim Cook

    Joined:
    May 20, 2008
    Messages:
    604
    Likes Received:
    0
    Sprintf doesn't pop anything, and doesn't know what or how much was pushed,
    but instead the caller pushes X items and then pops them back off after
    Sprintf returns. According to n1124.pdf (the C standard), 7.1.4, "If an
    argument to a function has ... a type (after promotion) not expected by a
    function with variable number of arguments, the behavior is undefined." The
    "after promotion" is key here. When you call with a char or word or int or
    long parameter, the (32-bit) C compiler pushes 32 bits in each case. That's
    why your %u works, because %hu and %u and %lu all (in this compiler) use 32
    bits from the stack but may represent the output differently.

    I didn't find the specific mention in the standard where the promotions of
    arguments to functions are described.

    If Sprintf did pop things based on the format string, doing Sprintf("%u %u",
    1) or Sprintf("%u %u", 1, 2, 3) would both cause stack faults, and neither
    will. The first one is undefined behavior, but will probably just display
    left over stack data.

    --
    Jim Cook
    2010 Sundays: 4/4, 6/6, 8/8, 10/10, 12/12 and 5/9, 9/5, 7/11, 11/7.
    Next year they're Monday.
     
  5. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,949
    Likes Received:
    30
    On Sat, 05 Jun 2010 22:48:22 -0400, Jim Cook <> wrote:

    |---Quote---
    |>
    |> Actually, "u" means "unsigned" to Sprintf (and AFAICT works with all sizes
    |> up to
    |> 32). And why would the compiler decide on WORD? I told it BYTE! But it
    |> can't
    |> push 1 byte onto the stack; there's nothing signalling Sprintf to pop 1
    |> byte. I
    |> figure the compiler must be pushing a 32-bit value onto the stack and
    |> Sprintf
    |> pops it and treats it according to the format element. eh?
    |>
    |>
    |---End Quote---
    |Sprintf doesn't pop anything, and doesn't know what or how much was pushed,
    |but instead the caller pushes X items and then pops them back off after
    |Sprintf returns. According to n1124.pdf (the C standard), 7.1.4, "If an
    |argument to a function has ... a type (after promotion) not expected by a
    |function with variable number of arguments, the behavior is undefined." The
    |"after promotion" is key here. When you call with a char or word or int or
    |long parameter, the (32-bit) C compiler pushes 32 bits in each case. That's
    |why your %u works, because %hu and %u and %lu all (in this compiler) use 32
    |bits from the stack but may represent the output differently.

    OK, that makes sense. What do you suppose happens when an arg is 64-bits (say
    ULONGLONG) and the format is "%I64u"? ... a 32-bit pointer is pushed (and
    Sprintf acts as if it's getting a pointer)?
    --
    - Vince
     
  6. Jim Cook

    Joined:
    May 20, 2008
    Messages:
    604
    Likes Received:
    0
    On Sat, Jun 5, 2010 at 9:31 PM, vefatica <> wrote:



    I don't believe that Sprintf dereferences any pointers unless %s is passed
    in.

    First, I the C standard allows for considerable lattitude in exactly what
    happens in the details, but imposes requirements on the results, so my
    statements here apply to the Microsoft compilers that I'm familiar with.
    Certainly don't fall into the trap and expect Sprintf("%u %u", func1(),
    func2()) will actually call either function before the other -- that is
    specifically left implementation defined. Some compilers and/or switches
    will pass things in registers instead of on a stack in cases as well.
    Specific knowledge in this case is only of academic interest and the
    standard requires that code does not rely on it.

    Ok, on to the answer.

    It pushes 64 bits in that case. If you have a 64-bit var v64 and do
    Sprintf("%08lX%08lX %016I64X", v64, v64) you'll see the same result in both
    cases. I've done this in my 16-bit compiler which was unaware of 64-bit
    quantities and I had to simulate them.

    To be portable, Sprintf and the family use the va_list / va_arg mechanism,
    which simply index into the stack to get their arguments. I've written
    plenty of routines that use that myself. va_list returns a pointer into
    stack memory where the first argument was pushed. Using va_arg dereferences
    that pointer after typecasting to what you said you were expecting, then
    advances the pointer by the right amount, which is almost always 4 in this
    compiler, but may be larger for ULONGLONG or double or directly pushed
    structures (as opposed to a pointer to a structure).

    --
    Jim Cook
    2010 Sundays: 4/4, 6/6, 8/8, 10/10, 12/12 and 5/9, 9/5, 7/11, 11/7.
    Next year they're Monday.
     
  7. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,949
    Likes Received:
    30
    On Sun, 06 Jun 2010 09:27:24 -0400, Jim Cook <> wrote:

    |To be portable, Sprintf and the family use the va_list / va_arg mechanism,
    |which simply index into the stack to get their arguments. I've written
    |plenty of routines that use that myself. va_list returns a pointer into
    |stack memory where the first argument was pushed. Using va_arg dereferences
    |that pointer after typecasting to what you said you were expecting, then
    |advances the pointer by the right amount, which is almost always 4 in this
    |compiler, but may be larger for ULONGLONG or double or directly pushed
    |structures (as opposed to a pointer to a structure).

    Thanks. I think I get it now.
    --
    - Vince
     
  8. samintz

    samintz Scott Mintz

    Joined:
    May 20, 2008
    Messages:
    1,189
    Likes Received:
    11
    Sorry, my description was from the older 16-bit integer days. Today,
    smaller types like 8-bit and 16-bit values get either sign extended or
    zero extended depending on whether signed or unsigned and pushed onto the
    stack. %u pulls a 32-bit unsigned value from the stack.

    -Scott

    vefatica <> wrote on 06/05/2010 05:29:09 PM:


    arguments

    things

    WORD

    Sprintf

     
  9. samintz

    samintz Scott Mintz

    Joined:
    May 20, 2008
    Messages:
    1,189
    Likes Received:
    11
    I would expect I64 or U64 to push two 32 bit values onto the stack. Those
    are native data types from the compiler's perspective. The printf format
    specifier will pull a 64 bit value from the stack. You'll notice I said
    pull and not pop. The va_arg functions essentially create a pointer into
    the stack. And the data is then obtained by dereferencing the pointer.

    -Scott

    vefatica <> wrote on 06/06/2010 12:31:14 AM:


    sizes

    it

    1

    pushed,

    a

    The

    or

    That's

    use 32

    64-bits (say

    (and

     

Share This Page