5
votes

I'm writing code to temporarily use my own stack for experimentation. This worked when I used literal inline assembly. I was hardcoding the variable locations as offsets off of ebp. However, I wanted my code to work without haivng to hard code memory addresses into it, so I've been looking into GCC's EXTENDED INLINE ASSEMBLY. What I have is the following:

volatile intptr_t new_stack_ptr = (intptr_t) MY_STACK_POINTER;
volatile intptr_t old_stack_ptr = 0;
asm __volatile__("movl %%esp, %0\n\t"
        "movl %1, %%esp"
        : "=r"(old_stack_ptr) /* output */
        : "r"(new_stack_ptr) /* input */
        );

The point of this is to first save the stack pointer into the variable old_stack_ptr. Next, the stack pointer (%esp) is overwritten with the address I have saved in new_stack_ptr.

Despite this, I found that GCC was saving the %esp into old_stack_ptr, but was NOT replacing %esp with new_stack_ptr. Upon deeper inspection, I found it actually expanded my assembly and added it's own instructions, which are the following:

mov    -0x14(%ebp),%eax
mov    %esp,%eax
mov    %eax,%esp
mov    %eax,-0x18(%ebp)

I think GCC is trying to preserve the %esp, because I don't have it explicitly declared as an "output" operand... I could be totally wrong with this...

I really wanted to use extended inline assembly to do this, because if not, it seems like I have to "hard code" the location offsets off of %ebp into the assembly, and I'd rather use the variable names like this... especially because this code needs to work on a few different systems, which seem to all offset my variables differently, so using extended inline assembly allows me to explicitly say the variable location... but I don't understand why it is doing the extra stuff and not letting me overwrite the stack pointer like it was before, ever since I started using extended assembly, it's been doing this.

I appreciate any help!!!

1
Not sure if this helps, but maybe -fomit-frame-pointer (which is enabled with -O1 and up) would eliminate the need to worry about %ebp.DaoWen
The extra stuff is probably there because you're doing a debug (no optimisation) build and GCC does it by default to catch potential errors. Search for "GCC Stack Frame Checks" to see what options GCC provides.Skizz

1 Answers

8
votes

Okay so the problem is gcc is allocating input and output to the same register eax. You want to tell gcc that you are clobbering the output before using the input, aka. "earlyclobber".

asm __volatile__("movl %%esp, %0\n\t"
        "movl %1, %%esp"
        : "=&r"(old_stack_ptr) /* output */
        : "r"(new_stack_ptr) /* input */
        );

Notice the & sign for the output. This should fix your code.

Update: alternatively, you could force input and output to be the same register and use xchg, like so:

asm __volatile__("xchg %%esp, %0\n\t"
        : "=r"(old_stack_ptr) /* output */
        : "0"(new_stack_ptr) /* input */
        );

Notice the "0" that says "put this into the same register as argument 0".