GCC inline - the push address, not its value for the stack

I am experimenting with GCC inline assembler (I am using MinGW, my OS is Win7). I am currently only getting some basic C stdlib functionality. I'm generally familiar with Intel syntax, but new to AT&T.

The following code works well:

char localmsg[] = "my local message";
asm("leal %0, %%eax" : "=m" (localmsg));
asm("push %eax");
asm("call %0" : : "m" (puts));
asm("add $4,%esp");

      

What LEA seems like overkill, however, I can just push the value right onto the stack. Well, due to what I believe is a feature of AT&T, doing this:

asm("push %0" : "=m" (localmsg));

      

will generate the following assembly code in the final executable:

PUSH DWORD PTR SS:[ESP+1F]

      

So, instead of pushing the address to my line, its contents were clicked because the "pointer" was "dereferenced" in C terms. This obviously crashes.

I believe this is just normal GAS behavior, but I haven't been able to find any information on how to overcome this. I would appreciate any help.

PS I know this is a trivial question for anyone who has survived this question. I expect to be slowed down, but I just spent 45 minutes looking for a solution and couldn't find anything.

PPS I understand that the correct way to do this is to call puts( )

in C code. This is for educational / experimental reasons only.

+3


source to share


1 answer


While inline asm is always a little more complex, calling functions from it are especially complex. Not what I would suggest for a "get fame inline asm" project. If you haven't already, I suggest looking at the most recent inline asm docs . A lot of work has been done to try and explain how asm asm works.

So here are a few thoughts:

1) Using multiple asm operators like this is a bad idea. As the docs say: Don't expect the sequence of asm statements to remain the same after compilation is complete. If some instructions must remain sequential in the output, put them in a single asm statement with multiple instructions.

2) Modifying registers directly (like you do with eax) without letting gcc know that you are doing this is also a bad idea. You must either use case constraints (so gcc can choose its own registers) or clobbers so gcc knows that you are stomping on them.

3) When a function is called (e.g. puts), while some registers need to return their values ​​before returning, some registers may be treated like scratch registers by the called function (i.e. modified and not restored before returning). As I mentioned in # 2, if you change registers without telling gcc it is a very bad idea. If you know the ABI for the function you are calling, you can add your zero registers to the clobber asm list.

4) Although you are using a constant string in this particular example, generally when passing asm pointers to strings, structures, arrays, etc. you will most likely need "memory cloning" to ensure that any pending memory writes are done before your asm starts executing.

5) Is actually lea

doing something very important. The esp value is unknown at compile time, so you don't like to execute push $12345

. Someone has to compute (esp + offset localmsg) before it can be pushed onto the stack. Also see second example below.

6) If you prefer intel format (and what sane person wouldn't be?), You can use -masm = intel.



With all this in mind, my first cut of this code looks like this. Note that this does not clobber "registers of zero". It remained as an exercise ...

#include <stdio.h>

int main()
{
  const char localmsg[] = "my local message";

  int result;

  /* Use 'volatile' since 'result' is usually not going to get used,
     which might tempt gcc to discard this asm statement as unneeded. */

  asm volatile ("push %[msg] \n\t"   /* Push the address of the string. */
                "call %[puts] \n \t" /* Call the print function. */
                "add $4,%%esp"       /* Clean up the stack. */

                : "=a" (result) /* The result code from puts. */
                : [puts] "m" (puts), [msg] "r" (localmsg)
                : "memory", "esp");

   printf("%d\n", result);
}

      

True, this does not rule lea

out because of # 5. However, if it is really important , try this:

#include <stdio.h>
const char localmsg[] = "my local message";

int main()
{

  int result;

  /* Use 'volatile' since 'result' is usually not going to get used. */

  asm volatile ("push %[msg] \n\t" /* Push the address of the string. */
                "call %[puts] \n \t" /* Call the print function. */
                "add $4,%%esp"       /* Clean up the stack. */

                : "=a" (result) /* The result code. */
                : [puts] "m" (puts), [msg] "i" (localmsg)
                : "memory", "esp");

   printf("%d\n", result);
}

      

As the global address localmsg is now known at compile time (okay, I'm simplifying a bit), the generated asm looks like this:

push $__ZL8localmsg
call _puts
add $4,%esp

      

TA-dah.

+3


source







All Articles