xxking
xxking

Reputation: 79

How get EIP from x86 inline assembly by gcc

I want to get the value of EIP from the following code, but the compilation does not pass

Command : gcc -o xxx x86_inline_asm.c -m32 && ./xxx

file contetn x86_inline_asm.c:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    unsigned int eip_val;
    __asm__("mov %0,%%eip":"=r"(eip_val)); 
    return 0;
}

How to use the inline assembly to get the value of EIP, and it can be compiled successfully under x86. How to modify the code and use the command to complete it?

Upvotes: 1

Views: 1570

Answers (2)

rcgldr
rcgldr

Reputation: 28826

I don't know gcc inline assembly syntax for this, but for masm:

        call    next0
next0:  pop     eax           ;eax = eip for this line

In the case of Masm, $ represents the current location, and since call is a 5 byte instruction, an alternative syntax without a label would be:

        call    $+5
        pop     eax

Upvotes: 2

Peter Cordes
Peter Cordes

Reputation: 364338

This sounds unlikely to be useful (vs. just taking the address of the whole function like void *tmp = main), but it is possible.


Just get a label address, or use . (the address of the current line), and let the linker worry about getting the right immediate into the machine code. So you're not architecturally reading EIP, just reading the value it currently has from an immediate.

asm volatile("mov $., %0" : "=r"(address_of_mov_instruction) );

AT&T syntax is mov src, dst, so what you wrote would be a jump if it assembled.

(Architecturally, EIP = the end of an instruction while it's executing, so arguably you should do

asm volatile(
  "mov $1f, %0  \n\t"      // reference label 1 forward
  "1:"               // GAS local label
  "=r"(address_after_mov)
);

I'm using asm volatile in case this asm statement gets duplicated multiple times inside the same function by inlining or something. If you want each case to get a different address, it has to be volatile. Otherwise the compiler can assume that all instances of this asm statement produce the same output. Normally that will be fine.


Architecturally in 32-bit mode you don't have RIP-relative addressing for LEA so the only good way to actually read EIP is call / pop. Reading program counter directly. It's not a general-purpose register so you can't just use it as the source or destination of a mov or any other instruction.


But really you don't need inline asm for this at all. Is it possible to store the address of a label in a variable and use goto to jump to it? shows how to use the GNU C extension where &&label takes its address.

int foo;
void *addr_inside_function() {
    foo++;

    lab1:  ;  // labels only go on statements, not declarations
    void *tmp = &&lab1;

    foo++;
    return tmp;
}

There's nothing you can safely do with this address outside the function; I returned it just as an example to make the compiler put a label in the asm and see what happens. Without a goto to that label, it can still optimize the function pretty aggressively, but you might find it useful as an input for an asm goto(...) somewhere else in the function.

But anyway, it compiles on Godbolt to this asm

# gcc -O3 -m32
addr_inside_function:
.L2:
        addl    $2, foo
        movl    $.L2, %eax
        ret
#clang -O3 -m32
addr_inside_function:
        movl    foo, %eax
        leal    1(%eax), %ecx
        movl    %ecx, foo
.Ltmp0:                                 # Block address taken
        addl    $2, %eax
        movl    %eax, foo
        movl    $.Ltmp0, %eax        # retval = label address
        retl

So clang loads the global, computes foo+1 and stores it, then after the label computes foo+2 and stores that. (Instead of loading twice). So you still can't usefully jump to the label from anywhere, because it depends on having foo's old value in eax, and on the desired behaviour being to store foo+2

Upvotes: 4

Related Questions