How to print EIP address in C? [duplicate] - c

This question already has an answer here:
Save CPU registers to variables in GCC
(1 answer)
Closed 4 years ago.
This is my C program ... I was trying to print out ESP, EBP and EIP.
#include <stdio.h>
int main() {
register int i asm("esp");
printf("%#010x <= $ESP\n", i);
int a = 1;
int b = 2;
char c[] = "A";
char d[] = "B";
printf("%p d = %s \n", &d, d);
printf("%p c = %s \n", &c, c);
printf("%p b = %d \n", &b, b);
printf("%p a = %d \n", &a, a);
register int j asm("ebp");
printf("%#010x <= $EBP\n", j);
//register int k asm("eip");
//printf("%#010x <= $EIP\n", k);
return 0;
}
I don't have problem with ESP and EBP.
user#linux:~# ./memoryAddress
0xbffff650 <= $ESP
0xbffff654 d = B
0xbffff656 c = A
0xbffff658 b = 2
0xbffff65c a = 1
0xbffff668 <= $EBP
user#linux:~#
But when I try to put EIP code, I'm getting the following error when compiling it.
user#linux:~# gcc memoryAddress.c -o memoryAddress -g
memoryAddress.c: In function ‘main’:
memoryAddress.c:20:15: error: invalid register name for ‘k’
register int k asm("eip");
^
user#linux:~#
What's wrong with this code?
register int k asm("eip");
printf("%#010x <= $EIP\n", k);
Is it possible to print out EIP value via C programming?
If yes, please let me know how to do it.
Update
I've tested the code here ...
user#linux:~/c$ lscpu
Architecture: i686
CPU op-mode(s): 32-bit
Byte Order: Little Endian
Thanks #Antti Haapala and others for your help. The code works ... However, when I load it into GDB, the EIP value is different.
(gdb) b 31
Breakpoint 1 at 0x68f: file eip.c, line 31.
(gdb) i r $eip $esp $ebp
The program has no registers now.
(gdb) r
Starting program: /home/user/c/a.out
0x00000000 <= Low Memory Address
0x40055d <= main() function
0x4005a5 <= $EIP 72 bytes from main() function (start)
0xbffff600 <= $ESP (Top of the Stack)
0xbffff600 d = B
0xbffff602 c = A
0xbffff604 b = 2
0xbffff608 a = 1
0xbffff618 <= $EBP (Bottom of the Stack)
0xffffffff <= High Memory Address
Breakpoint 1, main () at eip.c:31
31 return 0;
(gdb) i r $eip $esp $ebp
eip 0x40068f 0x40068f <main+306>
esp 0xbffff600 0xbffff600
ebp 0xbffff618 0xbffff618
(gdb)
Here is the new code
#include <stdio.h>
#include <inttypes.h>
int main() {
register int i asm("esp");
printf("0x00000000 <= Low Memory Address\n");
printf("%p <= main() function\n", &main);
uint32_t eip;
asm volatile("1: lea 1b, %0;": "=a"(eip));
printf("0x%" PRIx32 " <= $EIP %" PRIu32 " bytes from main() function (start)\n",
eip, eip - (uint32_t)main);
int a = 1;
int b = 2;
char c[] = "A";
char d[] = "B";
printf("%#010x <= $ESP (Top of the Stack)\n", i);
printf("%p d = %s \n", &d, d);
printf("%p c = %s \n", &c, c);
printf("%p b = %d \n", &b, b);
printf("%p a = %d \n", &a, a);
register int j asm("ebp");
printf("%#010x <= $EBP (Bottom of the Stack)\n", j);
printf("0xffffffff <= High Memory Address\n");
return 0;
}

Please first read the QA Reading program counter directly - from there we can see that there are no mov commands to access the EIP/RIP directly, therefore you cannot use register asm to get access to it. Instead at any point you can use those tricks. It is easiest in 64-bit mode, use
uint64_t rip;
asm volatile("1: lea 1b(%%rip), %0;": "=a"(rip));
to get the 64-bit instruction (thanks Michael Petch for pointing out that a label works with lea here.
Demonstration:
#include <stdio.h>
#include <inttypes.h>
int main(void) {
uint64_t rip;
asm volatile("1: lea 1b(%%rip), %0;": "=a"(rip));
printf("%" PRIx64 "; %" PRIu64 " bytes from main start\n",
rip, rip - (uint64_t)main);
}
Then
% gcc -m64 rip.c -o rip; ./rip
55b7bf9e8659; 8 bytes from start of main
Proof that it is correct:
% gdb -batch -ex 'file ./rip' -ex 'disassemble main'
Dump of assembler code for function main:
0x000000000000064a <+0>: push %rbp
0x000000000000064b <+1>: mov %rsp,%rbp
0x000000000000064e <+4>: sub $0x10,%rsp
0x0000000000000652 <+8>: lea -0x7(%rip),%rax # 0x652 <main+8>
For 32-bit code it seems you can use lea with a label - this didn't work for 64-bit code though.
#include <stdio.h>
#include <inttypes.h>
int main(void) {
uint32_t eip;
asm volatile("1: lea 1b, %0;": "=a"(eip));
printf("%" PRIx32 "; %" PRIu32 " bytes from main start\n",
eip, eip - (uint32_t)main);
}
Then
% gcc -m32 eip.c -o eip; ./eip
5663754a; 29 bytes from main start
Proof that it is correct:
% gdb -batch -ex 'file ./eip' -ex 'disassemble main'
Dump of assembler code for function main:
0x0000052d <+0>: lea 0x4(%esp),%ecx
0x00000531 <+4>: and $0xfffffff0,%esp
0x00000534 <+7>: pushl -0x4(%ecx)
0x00000537 <+10>: push %ebp
0x00000538 <+11>: mov %esp,%ebp
0x0000053a <+13>: push %ebx
0x0000053b <+14>: push %ecx
0x0000053c <+15>: sub $0x10,%esp
0x0000053f <+18>: call 0x529 <__x86.get_pc_thunk.dx>
0x00000544 <+23>: add $0x1a94,%edx
0x0000054a <+29>: lea 0x54a,%eax
(in the 32-bit version there are many more lea commands, but this one is the "load my constant address here", which then will be corrected by the dynamic linker when it loads the exe).

EIP can't be read directly. RIP can, with lea 0(%rip), %rax, but it's not a general-purpose register.
Instead of reading an address from a register you can just use a code address directly.
void print_own_address() {
printf("%p\n", print_own_address);
}
If you compile this as PIC (position-independent code), the compiler will get the run-time address of the function by reading EIP or RIP for you. You don't need inline asm for this.
Or for addresses other than functions, GNU C allows labels as values.
void print_label_address() {
for (int i=0 ; i<1000; i++) {
volatile int sink = i;
}
mylabel:
for (int i=0 ; i<1000; i++) {
volatile int sink2 = i;
}
printf("%p\n", &&mylabel); // Take the label address with && GNU C syntax.
}
Compiled on the Godbolt compiler explorer with an without -fPIE to generate position-independent code, we get:
# PIE version:
xor eax, eax # i=0
.L4: # do {
mov DWORD PTR -16[rsp], eax # sink=i
add eax, 1
cmp eax, 1000
jne .L4 # } while(i!=1000);
xor eax, eax # i=0
.L5: # do {
mov DWORD PTR -12[rsp], eax # sink2 = i
add eax, 1
cmp eax, 1000
jne .L5 # }while(i != 1000);
lea rsi, .L5[rip] # address of .L5 = mylabel
lea rdi, .LC0[rip] # format string
xor eax, eax # 0 FP args in XMM regs for a variadic function
jmp printf#PLT # tailcall printf
Without -fPIE, the addresses are link-time constants (and fit in a 32-bit constant), so we get
mov esi, OFFSET FLAT:.L5
mov edi, OFFSET FLAT:.LC0
xor eax, eax
jmp printf
Whether you get a meaningful address from your label or not depends on how aggressively the compiler optimized the code where you put it. Putting a label somewhere may inhibit optimization (like autovectorization) if you even take the label address, but IDK. Maybe it would only hurt if you actually had a goto to it.

You can read rip with another small hack if you are interested. here your full code which reads rip too:
#include <stdio.h>
#include <inttypes.h>
int main()
{
register uint64_t i asm("rsp");
printf("%" PRIx64 " <= $RSP\n", i);
int a = 1;
int b = 2;
char c[] = "A";
char d[] = "B";
printf("%p d = %s \n", &d, d);
printf("%p c = %s \n", &c, c);
printf("%p b = %d \n", &b, b);
printf("%p a = %d \n", &a, a);
register uint64_t j asm("rbp");
printf("%" PRIx64 " <= $RBP\n", j);
uint64_t rip = 0;
asm volatile ("call here2\n\t"
"here2:\n\t"
"pop %0"
: "=m" (rip));
printf("%" PRIx64 " <= $RIP\n", rip);
return 0;
}
Hack here is a fun one. You just call next assembly line. now because return address which is rip in stack, you can retrieve it by a pop instruction from stack. :)
Update:
The main reason for this approach is data injection. see following code:
#include <stdio.h>
#include <inttypes.h>
int main()
{
uint64_t rip = 0;
asm volatile ("call here2\n\t"
".byte 0x41\n\t" // A
".byte 0x42\n\t" // B
".byte 0x43\n\t" // C
".byte 0x0\n\t" // \0
"here2:\n\t"
"pop %0"
: "=m" (rip));
printf("%" PRIx64 " <= $RIP\n", rip);
printf("injected data:%s\n", (char*)rip);
return 0;
}
This approach can inject data inside code segment(which can be usefull for code injection). If you compile and run, you see following output:
400542 <= $RIP
injected data:ABC
You have used rip as placeholder for your data. I personally like this approach, but it can have efficiency impacts as mentioned in comments.
I have tested both codes in 64-bit Ubuntu bash for Windows(Linux subsystem for Windows) and both works.
Update 2:
Please make sure to read comments about red zones. Thanks michael a lot for mentioning this problem and providing an example. :)
If you need to use this code without red zone problem, you need to write it as following (from micheal's sample):
asm volatile ("sub $128, %%rsp\n\t"
"call 1f\n\t"
".byte 0x41\n\t" // A
".byte 0x42\n\t" // B
".byte 0x43\n\t" // C
".byte 0x0\n\t" // \0
"1:\n\t"
"pop %0\n\t"
"add $128, %%rsp"
: "=r" (rip));

Related

Strange assembly output when optimizing string hashing

when trying to create a compile-time hash macro, it worked but it had its problems. So I thought if the strings are known at compile time(which they are), the whole hashing should get optimized away to a constant.
This gcc C99 code with optimization level -O3 enabled:
#include <stdio.h>
int main(void)
{
char const *const string = "hello";
int hash = 0;
for (unsigned char i=0; i < sizeof string; ++i)
{
hash += string[i]; //reeaally simple hash :)
}
printf("%i", hash);
return 0;
}
produced the following assembly code:
.LC0:
.string "hello"
.LC1:
.string "%i"
main:
sub rsp, 8
movsx eax, BYTE PTR .LC0[rip+6]
movsx edx, BYTE PTR .LC0[rip+7]
mov edi, OFFSET FLAT:.LC1
lea esi, [rax+532+rdx]
xor eax, eax
call printf
xor eax, eax
add rsp, 8
ret
whilst the same code, I only changed "hello" to "hello w", produces this assembly code, which completely optimized the hashing away:
.LC0:
.string "%i"
main:
sub rsp, 8
mov esi, 683
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
xor eax, eax
add rsp, 8
ret
Try it yourself
What is the reason? Does this mean I can't use this way of hashing because it may be that the overhead won't get optimized out? How can I make sure there won't be any overhead, what are alternatives?
EDIT 1:
I have played around a bit and it seems if the number of chars in the string is 6, it won't get optimized away if the number of chars is 7, it will
sizeof is wrong here. It returns the size of the char pointer not the length of the string.
In your case it is an UB and the compiler cannot optimize it out as you read outside string literal bounds. it is a clang bug not the feature.
if you do it properly gcc will optimize it as well
int main(void)
{
char const string[] = "hello";
int hash = 0;
for (unsigned char i=0; i < sizeof(string); ++i)
{
hash += string[i]; //reeaally simple hash :)
}
printf("%i", hash);
return 0;
}
https://godbolt.org/z/YCCNCt

Why is there a 4 byte gap between the parameters on Microsoft VC++

I have the following program that I am compiling using Microsoft's Visual C++ command line compiler.
#include<stdio.h>
void foo(int, int);
void main(void) {
foo(5,4);
}
void foo(int a, int b)
{
printf("%u\n", sizeof(int));
printf("%u\n", &a);
printf("%u\n", &b);
}
When I print out the addresses I get addresses like -:
3799448016
3799448024
The gap is always 8 byes between addresses, while sizeof(int) = 4
There is always an extra 4 byte gap between the parameters (The size of int on my machine is 4 bytes). Why ?
What is the extra 4 bytes of space for ?
I am on a 64-bit Windows 8.1 machine with VS 2013.
Well, I cannot check the code produced by VS 2013, but Godbolt does support some CL version; the assembly below is an excerpt of what it generated:
main PROC
sub rsp, 40 ; 00000028H
mov edx, 4
mov ecx, 5
call void __cdecl foo(int,int)
...
main ENDP
a$ = 48
b$ = 56
foo PROC
mov DWORD PTR [rsp+16], edx
mov DWORD PTR [rsp+8], ecx
sub rsp, 40 ; 00000028H
mov edx, 4
lea rcx, OFFSET FLAT:$SG4875
call printf
lea rdx, QWORD PTR a$[rsp]
lea rcx, OFFSET FLAT:$SG4876
call printf
lea rdx, QWORD PTR b$[rsp]
lea rcx, OFFSET FLAT:$SG4877
call printf
add rsp, 40 ; 00000028H
ret 0
foo ENDP
First of all, the parameters are not passed on stack, but in registers - first in *CX and second in *DX - as these are 32-bit, they're passed in EDX and ECX .
Thus, the parameters do not have addresses, and would not stored in memory, unless they have their address taken. Since the & operator is used within the function, these now have to be stored on stack. I don't have any good explanation though on why they're stored with a 4-byte gap ([rsp+16] and [rsp+8]) - but they are. **EDIT: Here's the relevant Visual Studio documentation - from Hans Passant's comment - it clearly shows that VS 2013 uses a fixed layout to store the parameters wherever needed - despite their types.
This is unlike my Linux GCC, which would generate code that stores them in adjacent 32-bit locations:
foo:
.LFB1:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR -4[rbp], edi
mov DWORD PTR -8[rbp], esi
...
And the conversion specification for printing pointers is %p and for size_t you should use %zu. But since VS 2013 does not have a standards-compliant C compiler, you cannot use z - therefore cast it to unsigned long long and print with %llu or get a C compiler.
Because your OS is 64-bit, so addresses are too.
Parameters are passed with push instruction. This instruction pushes operand into stack and decreases esp based on stack frame size. In 64-bit OS, esp is decreased by 8 for next address. In 32-bit OS, you will see 4 byte difference only.
If it was not function argument, you would not have seen this behavior. For example, in the following code you should see 4 byte difference in addresses:
void foo()
{
int a = 0;
int b = 0;
printf("%u\n", sizeof(int));
printf("%u\n", &a);
printf("%u\n", &b);
}
EDIT:
When you pass parameters, they are allocated on stack, so this following example:
struct _asd
{
int a;
int b;
char c[6];
};
void foo2(struct _asd a, int b)
{
printf("%u\n", sizeof(struct _asd));
printf("%u\n", &a);
printf("%u\n", &b);
}
In this case, sizeof(struct _asd)==16, so you see a result like this:
16
3754948864
3754948880
and as you see difference is 16. Just remember that you need to test this when you build Release mode, in Debug mode you see something like this:
16
3754948864
3754948884
a 20 byte difference, Because in Debug mode, compiler allocates more memory for structures. Now if you write your code like this:
struct _asd
{
int a;
int b;
char c[6];
};
void foo2(struct _asd *a, int b)
{
printf("%u\n", sizeof(struct _asd));
printf("%u\n", a);
printf("%u\n", &b);
}
Result will be like this:
16
827849528
827849520
Only 8 bytes difference, because now only addresses is sent to function which is only 8 bytes long.
As a side note, in 64-bit OS, arguments are mostly passed by registers. For example first 4 parameters are normally passed by registers when you compile your code in MSVC. But in current topic, parameters are accessed by address and this will be meaningless for registers. So I think compiler automatically passed such parameters by pushing them into stack.
The gap is always 8 byes between addresses, while sizeof(int) = 4
1) This is compiler and implementation depended. The C standard does not specify memory arrangement for the individual integers.
2) To print addresses use "%p" format after casting it to (void *). The size_t should be print with "%zu" format.
For example:
#include<stdio.h>
struct my_struct
{
int a;
int b;
int c;
int d;
};
void foo(int a, int b, struct my_struct f, struct my_struct g )
{
printf("%zu\n", sizeof(int));
printf("&a= %p\n", (void *) &a);
printf("&b= %p\n", (void *) &b);
printf("&f= %p\n", (void *) &f);
printf("&g= %p\n", (void *) &g);
}
int main(void)
{
int c=0;
int d=1;
struct my_struct e,f;
foo(5, 4, e, f);
printf("&c= %p\n", (void *) &c);
printf("&d= %p\n", (void *) &d);
return 0;
}
Output:
4
&a= 0x7fff88da09fc
&b= 0x7fff88da09f8
&f= 0x7fff88da09e0
&g= 0x7fff88da09d0
&c= 0x7fff88da0a18
&d= 0x7fff88da0a1c

C printf Segmentation fault (core dumped) while mixing with Assembly code

I tried some basic example of mixing C code and x86 Assembly code.
It is simple Find smaller number example.
Here is Assembly code in b.s file (I have to use intel syntax with no prefix and i386 architecture):
.intel_syntax noprefix
.arch i386
.data
.globl out_format
out_format:
.asciz "min(%i, %i) = %i\n"
.text
.globl min
.type min, #function
min:
enter 0, 0
mov ebx, [ebp + 8]
cmp ebx, [ebp + 12]
jle 1f
mov eax, [ebp] + 12
jmp 2f
1:
mov eax, ebx
2:
leave
ret 8
.end
Here is the C code in a.c file:
#include<stdio.h>
extern char out_format;
extern int min(int x, int y);
int main() {
int x1, x2, r;
scanf("%i%i", &x1, &x2);
r = min(x1, x2);
printf(&out_format, x1, x2, r);
return 0;
}
I compile it with gcc on x64 Arch Linux:
gcc -m32 -Wall -Werror -g -o p b.s a.c
But when I start the executable file p, and insert 2 numbers and hit enter
I get:
Segmentation fault (core dumped)
I researched all previous similar questions, and I tried several things but every time I get the same error.
I also tried to debug it with gdb, and I see that Assembly function min returns the right result, but somehow it always stuck on C printf function.
I tried to not import string out_format from Assembly file, and define it directly in C file (I defined it as simple array of chars, and also as char pointer using malloc and free functions) but it didn't help.
Otherwise the program doesn't give such error if I intialize variable r with some integer instead with result of Assembly function min. But it doesn't have any sense to me as I already checked it with gdb and the Assembly function correctly intialize the variable r with correct smaller number.
Please, can anyone help me with this?
Thanks in advance
At the end I just want to leave the code that works.
File b.s:
.intel_syntax noprefix
.arch i386
.data
.globl out_format
out_format:
.asciz "min(%i, %i) = %i\n"
.text
.globl min
.type min, #function
min:
enter 0, 0
push ebx
mov ebx, [ebp + 8]
cmp ebx, [ebp + 12]
jle 1f
mov eax, [ebp] + 12
jmp 2f
1:
mov eax, ebx
2:
pop ebx
leave
ret 8
.end
File a.c:
#include<stdio.h>
extern char out_format;
extern int __attribute__((stdcall)) min(int x, int y);
int main() {
int x1, x2, r;
scanf("%i%i", &x1, &x2);
r = min(x1, x2);
printf(&out_format, x1, x2, r);
return 0;
}
Thanks everyone for their help.
if min() works good, check out_format.
following code works.
printf("%d %d %d\n", x1, x2, r);
full code
#include<stdio.h>
int min(int x, int y)
{
if (x<y) return x;
return y;
}
int main() {
int x1, x2, r;
scanf("%i%i", &x1, &x2);
r = min(x1, x2);
printf("%d %d %d\n", x1, x2, r);
return 0;
}
The asm uses ret 8 to pop the args from the stack as it returns.
The standard 32-bit x86 Linux calling convention (i386 System V ABI) is caller-pops. Functions just use ret to return and the caller can clean up the stack if / when it wants. (e.g. use mov stores to write new args before another call instead of adjusting esp and using push again.)
As the OP's self answer points you, you could instead declare min as
extern int __attribute__((stdcall)) min(int x, int y);
so the compiler-generated code will agree with the asm on the calling convention. This calling convention is sometimes used on Windows.
Single-stepping with a debugger would have revealed this (if you knew what to look for). Always use a debugger to figure out where your code segfaults, then look at registers and work backwards to find out why.

C float in NASM x86 assembly

In my university project i have to use binary representation of float number in x86 assembly for arithmetic operations. Using FPU is forbidden so i try to read float number and return it as DWORD but whatever i try to do i get "-nan". Any advices?
Edit:
I use gcc and it's 32 bit code
Declaration in C (i can't change that)
extern "C" float func(float num);
*.asm file
section .text
global func
func:
;prolog
push ebp
mov ebp, esp
; zapamiętanie rejestrów zachowywanych
push ebx
push esi
push edi
mov eax, DWORD [ebp+8]
;mov eax, 0xffffffff i checked that but i still get the same result
; odtworzenie rejestrów, które były zapamiętane
pop edi
pop esi
pop ebx
;epilog
pop ebp
ret
Example result (for 256)
01000011100000000000000000000000
11111111110000000000000000000000
num1: 256.000000
num2: -nan
Edit:
C code without checking bits part
#include <stdio.h>
extern "C" float func(float num);
int main()
{
float num1;
float num2;
scanf("%f", &num1);
num2=func(num1);
printf("num1: %f\nnum2: %f\n", num1, num2);
return 0;
}
If you declare the return type func as float the result will be returned in the FPU (ST0). For returning a value in EAX you have to declare it as an integer type. For printf you have to fake a float. Example:
caller.c:
#include <stdio.h>
#include <stdint.h>
extern float asmfunc1(float);
extern uint32_t asmfunc2(float);
int main (void)
{
printf ("asmfunc1: %f\n", asmfunc1(456.78));
uint32_t ifl = asmfunc2(123.45);
float* pfl = (float*) &ifl; // Faking a float
printf ("asmfunc2: %f\n", *pfl);
return 0;
}
callee.asm:
section .text
global asmfunc1, asmfunc2
asmfunc1:
fld dword [esp+4]
ret
asmfunc2:
push ebp
mov ebp, esp
mov eax, [ebp+8]
leave
ret
Build & run:
nasm -felf callee.asm
gcc -m32 callee.o caller.c
./a.out
In the 32 bit Linux ABI, float values are actually returned as long double at the top of the 8087 FP stack. You cannot return a float without using the FPU.
What you are probably restricted from doing is FP operations for addition, subtraction... But you still need to load the result in the FP stack to return it. In 64 bits mode, you would return float values as double in the xmm0 register.
Try changing the code to this:
section .text
global func
func:
push ebp
mov ebp, esp
flds 8(%ebp)
pop ebp
ret

Obtaining frame pointer in C

I'm trying to get the FP in my C program, I tried two different ways, but they both differ from what I get when I run GDB.
The first way I tried, I made a protocol function in C for the Assembly function:
int* getEbp();
and my code looks like this:
int* ebp = getEbp();
printf("ebp: %08x\n", ebp); // value i get here is 0xbfe2db58
while( esp <= ebp )
esp -= 4;
printf( "ebp: %08x, esp" ); //value i get here is 0xbfe2daec
My assembly code
getEbp:
movl %ebp, %eax
ret
I tried making the prototype function to just return an int, but that also doesn't match up with my GDB output. We are using x86 assembly.
EDIT: typos, and my getEsp function looks exactly like the other one:
getEsp:
movl %esp, %eax
ret
For reading a register, it's indeed best to use GCC extended inline assembly syntax.
Your getEbp() looks like it should work if you compiled it in a separate assembler file.
Your getEsp() is obviously incorrect since it doesn't take the return address pushed by the caller into account.
Here's a code snippet that gets ebp through extended inline asm and does stack unwinding by chasing the frame pointer:
struct stack_frame {
struct stack_frame *prev;
void *return_addr;
} __attribute__((packed));
typedef struct stack_frame stack_frame;
void backtrace_from_fp(void **buf, int size)
{
int i;
stack_frame *fp;
__asm__("movl %%ebp, %[fp]" : /* output */ [fp] "=r" (fp));
for(i = 0; i < size && fp != NULL; fp = fp->prev, i++)
buf[i] = fp->return_addr;
}
I'll show two working implementations of reading the registers below. The pure asm functions are get_ebp() and get_esp() in getbp.S. The other set implemented as inline functions are get_esp_inline() and get_ebp_inline() at the top of test-getbp.c.
In getbp.S
.section .text
/* obviously incurring the cost of a function call
to read a register is inefficient */
.global get_ebp
get_ebp:
movl %ebp, %eax
ret
.global get_esp
get_esp:
/* 4: return address pushed by caller */
lea 4(%esp), %eax
ret
In test-getbp.c
#include <stdio.h>
#include <stdint.h>
/* see http://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation */
#include <sys/sdt.h>
int32_t *get_ebp(void);
int32_t *get_esp(void);
__attribute__((always_inline)) uintptr_t *get_ebp_inline(void)
{
uintptr_t *r;
__asm__ volatile ("movl %%ebp, %[r]" : /* output */ [r] "=r" (r));
return r;
}
__attribute__((always_inline)) uintptr_t *get_esp_inline(void)
{
uintptr_t *r;
__asm__ volatile ("movl %%esp, %[r]" : /* output */ [r] "=r" (r));
return r;
}
int main(int argc, char **argv)
{
uintptr_t *bp, *sp;
/* allocate some random data on the stack just for fun */
int a[10] = { 1, 3, 4, 9 };
fprintf(fopen("/dev/null", "r"), "%d\n", a[3]);
STAP_PROBE(getbp, getbp); /* a static probe is like a named breakpoint */
bp = get_ebp();
sp = get_esp();
printf("asm: %p, %p\n", (void*)bp, (void*)sp);
bp = get_ebp_inline();
sp = get_esp_inline();
printf("inline: %p, %p\n", (void*)bp, (void*)sp);
return 0;
}
We can now write a GDB script to dump ebp and esp while making use of the getbp static probe defined in test-getbp.c above.
In test-getbp.gdb
file test-getbp
set breakpoint pending on
break -p getbp
commands
silent
printf "gdb: 0x%04x, 0x%04x\n", $ebp, $esp
continue
end
run
quit
To verify that the functions return the same data as GDB:
$ gdb -x test-getbp.gdb
< ... >
gdb: 0xffffc938, 0xffffc920
asm: 0xffffc938, 0xffffc920
inline: 0xffffc938, 0xffffc920
< ... >
Disassembling test-getbp main() produces:
0x08048370 <+0>: push %ebp
0x08048371 <+1>: mov %esp,%ebp
0x08048373 <+3>: push %ebx
0x08048374 <+4>: and $0xfffffff0,%esp
0x08048377 <+7>: sub $0x10,%esp
0x0804837a <+10>: movl $0x8048584,0x4(%esp)
0x08048382 <+18>: movl $0x8048586,(%esp)
0x08048389 <+25>: call 0x8048360 <fopen#plt>
0x0804838e <+30>: movl $0x9,0x8(%esp)
0x08048396 <+38>: movl $0x8048590,0x4(%esp)
0x0804839e <+46>: mov %eax,(%esp)
0x080483a1 <+49>: call 0x8048350 <fprintf#plt>
0x080483a6 <+54>: nop
0x080483a7 <+55>: call 0x80484e4 <get_ebp>
0x080483ac <+60>: mov %eax,%ebx
0x080483ae <+62>: call 0x80484e7 <get_esp>
0x080483b3 <+67>: mov %ebx,0x4(%esp)
0x080483b7 <+71>: movl $0x8048594,(%esp)
0x080483be <+78>: mov %eax,0x8(%esp)
0x080483c2 <+82>: call 0x8048320 <printf#plt>
0x080483c7 <+87>: mov %ebp,%eax
0x080483c9 <+89>: mov %esp,%edx
0x080483cb <+91>: mov %edx,0x8(%esp)
0x080483cf <+95>: mov %eax,0x4(%esp)
0x080483d3 <+99>: movl $0x80485a1,(%esp)
0x080483da <+106>: call 0x8048320 <printf#plt>
0x080483df <+111>: xor %eax,%eax
0x080483e1 <+113>: mov -0x4(%ebp),%ebx
0x080483e4 <+116>: leave
0x080483e5 <+117>: ret
The nop at <main+54> is the static probe. See the code around the two printf calls for how the registers are read.
BTW, this loop in your code seems strange to me:
while( esp <= ebp )
esp -= 4;
Don't you mean
while (esp < ebp)
esp +=4
?
Because you're relying on implementation specific details, you need to provide more information about your target to get an accurate answer. You didn't specify architecture, compiler or operating system, which are really required to answer your question.
Making an educated guess based on the register names you referenced and the fact that you're using at&t syntax, I'm going to assume this is i386 and you're using gcc.
The simplest way to achieve this is using gcc variable attributes, you can try this, which is a gcc specific syntax to request a specific register.
#include <stdint.h>
#include <stdio.h>
int main(int argc, char **argv)
{
const uintptr_t register framep asm("ebp");
fprintf(stderr, "val: %#x\n", framep);
return 0;
}
An alternative is to use inline assembly to load the value, like this:
#include <stdint.h>
#include <stdio.h>
int main(int argc, char **argv)
{
uintptr_t framep;
asm("movl %%ebp, %0" : "=r" (framep));
fprintf(stderr, "val: %#x\n", framep);
return 0;
}
This requests a 32bit register for a write-operation (= modifier), and loads it onto framep. The compiler takes care of extracting the values you declare.
In gdb, you can print the value and verify it matches the output.
(gdb) b main
Breakpoint 1 at 0x40117f: file ebp2.c, line 8.
(gdb) r
Starting program: /home/zero/a.exe
[New Thread 4664.0x1290]
[New Thread 4664.0x13c4]
Breakpoint 1, main (argc=1, argv=0x28ac50) at ebp2.c:8
8 asm("movl %%ebp, %0" : "=r" (framep));
(gdb) n
10 fprintf(stderr, "val: %#x\n", framep);
(gdb) p/x framep
$1 = 0x28ac28
(gdb) p/x $ebp
$2 = 0x28ac28
(gdb) c
Continuing.
val: 0x28ac28
[Inferior 1 (process 4664) exited normally]
(gdb) q
Remember that you cannot rely on this behaviour, even on x86 gcc can be configured to not use the frame pointer and keeps track of stack usage manually. This is generally called FPO by Microsoft, or omit-frame-pointer on other platforms. This trick frees up another register for general purpose use, but makes debugging a little more complicated.
You're correct that eax is generally used for return values where possible in x86 calling conventions, I have no idea why the comments on your post claim the stack is used.

Resources