Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC INLINE ASSEMBLY Won't Let Me Overwrite $esp

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!!!

like image 477
Chad Avatar asked Oct 15 '13 22:10

Chad


1 Answers

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".

like image 109
Jester Avatar answered Sep 24 '22 17:09

Jester