帮助理解C堆栈
我正在尝试了解 C 中的低级内存管理器,尤其是堆栈。正如我所知,当调用函数时,返回地址被压入堆栈。然后局部变量位于之后。
所以我写了一个小程序来调查这个问题。这是我的程序:
#include <stdio.h>
void TestStack();
void DoTestStack() {
char x1 = 1;
char x2 = 2;
char x3 = 3;
char x4 = 4;
char *x = &x4;
printf("TestStack: %08X\n", (&TestStack));
printf("\n");
int i;
x = &x4;
for(i = 0; i < 32; i++)
printf("%02d: %08X : %08X\n", i, *(x + i), *(x - i));
printf("\n");
printf("x1: %02X\n", x1);
printf("x2: %02X\n", x2);
printf("x3: %02X\n", x3);
printf("DONE!!!\n");
}
void TestStack() {
DoTestStack();
}
void main() {
TestStack() ;
}
基本上,它调查位置 x4 所在之前和之后的所有内存。它应该很好地覆盖返回地址的位置。
但我似乎根本找不到任何类似于返回地址的字节。
这是我的结果:
TestStack: 08048B49
00: 00000004 : 00000004
01: 00000003 : FFFFFFBF
02: 00000002 : FFFFFFAC
03: 00000001 : FFFFFFED
04: 00000004 : 0000001C
05: FFFFFFC3 : 00000000
06: FFFFFFB9 : 00000000
07: 00000000 : 00000000
08: FFFFFFF4 : 00000008
09: FFFFFFBF : 00000000
10: FFFFFFB9 : FFFFFF90
11: 00000000 : FFFFFFBD
12: 00000038 : 00000020
13: FFFFFFED : 00000000
14: FFFFFFAC : 00000000
15: FFFFFFBF : 00000000
16: 00000054 : 00000000
17: FFFFFF8B : 00000000
18: 00000004 : FFFFFFFF
19: 00000008 : 00000000
20: 00000045 : 00000008
21: 00000073 : 00000000
22: FFFFFFA7 : 00000000
23: 00000000 : 00000000
24: 00000020 : 00000017
25: FFFFFFBD : 00000008
26: FFFFFF90 : 00000004
27: 00000000 : FFFFFF8C
28: 00000048 : FFFFFFCF
29: FFFFFFED : 00000008
30: FFFFFFAC : 00000004
31: FFFFFFBF : FFFFFF8A
x1: 01
x2: 02
x3: 03
DONE!!!
我在这里缺少什么?有人可以解释一下吗?
无论如何,我使用的是 Ubuntu 9.10。
提前致谢。 :-D
I am trying to understand low-level memory manager in C especially Stack. As I was told, when a function is called, a return address was push on a stack. Then local variables is located after that.
So I write a small program to investigate this. Here is my program:
#include <stdio.h>
void TestStack();
void DoTestStack() {
char x1 = 1;
char x2 = 2;
char x3 = 3;
char x4 = 4;
char *x = &x4;
printf("TestStack: %08X\n", (&TestStack));
printf("\n");
int i;
x = &x4;
for(i = 0; i < 32; i++)
printf("%02d: %08X : %08X\n", i, *(x + i), *(x - i));
printf("\n");
printf("x1: %02X\n", x1);
printf("x2: %02X\n", x2);
printf("x3: %02X\n", x3);
printf("DONE!!!\n");
}
void TestStack() {
DoTestStack();
}
void main() {
TestStack() ;
}
Basically, it investigate all memory before and after the position x4 is located. which should well cover the position of the return address.
But I can't seems to find any byte that resembles the return address at all.
Here is my result:
TestStack: 08048B49
00: 00000004 : 00000004
01: 00000003 : FFFFFFBF
02: 00000002 : FFFFFFAC
03: 00000001 : FFFFFFED
04: 00000004 : 0000001C
05: FFFFFFC3 : 00000000
06: FFFFFFB9 : 00000000
07: 00000000 : 00000000
08: FFFFFFF4 : 00000008
09: FFFFFFBF : 00000000
10: FFFFFFB9 : FFFFFF90
11: 00000000 : FFFFFFBD
12: 00000038 : 00000020
13: FFFFFFED : 00000000
14: FFFFFFAC : 00000000
15: FFFFFFBF : 00000000
16: 00000054 : 00000000
17: FFFFFF8B : 00000000
18: 00000004 : FFFFFFFF
19: 00000008 : 00000000
20: 00000045 : 00000008
21: 00000073 : 00000000
22: FFFFFFA7 : 00000000
23: 00000000 : 00000000
24: 00000020 : 00000017
25: FFFFFFBD : 00000008
26: FFFFFF90 : 00000004
27: 00000000 : FFFFFF8C
28: 00000048 : FFFFFFCF
29: FFFFFFED : 00000008
30: FFFFFFAC : 00000004
31: FFFFFFBF : FFFFFF8A
x1: 01
x2: 02
x3: 03
DONE!!!
What am I missing here? Can someone please explain.
I am on Ubuntu 9.10 anyway.
Thanks in advance. :-D
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您正在查看单个字符,然后将它们转换为 32 位整数,这让您感到困惑。返回地址位于这四行的最低有效字节中:
即您的返回地址是 0x08048b54。
试试这个:
You're looking at individual chars, then casting those to 32-bit integers, which is confusing you. The return address is in the least significant bytes of these four lines:
I.e. your return address is 0x08048b54.
Try this instead:
在此行中:
x
指针是一个字符指针,在取消引用后,该指针将提升为带符号扩展的整数,这会导致所有输出值要么是 000000xx 要么是 FFFFFFxx,具体取决于位 7 的值相反,您可能想要使用
int
指针来扫描堆栈值,而不是char
指针。In this line:
the
x
pointer is a character pointer, which after dereferencing gets promoted to an integer with sign extension, which causes all your output values to be either 000000xx or FFFFFFxx depending on the value of bit 7.Instead, what you probably want to do is use an
int
pointer to scan through the stack values, instead of achar
pointer.如果您想了解这一切是如何工作的,一个好主意是在调试器中运行您的应用程序。弄清楚发生了什么事真是太糟糕了。
任何调试器都可以,但是我最了解 Windbg,因此这里有一些关于从哪里开始稍微修改的代码版本的指示,就像 Greg 建议的那样,您需要一个 int*。
更改:
TO:
删除:
移动(第一个变量,在 x1 之前):
新更改(更容易阅读,并且按照之前的操作(xi)是随机的/当前不在范围内的值):
此更改将有效地显示堆栈帧地址和值。
在windbg中,获取调用、内存、线程和命令窗口,以便您可以看到它们全部。
用编译器编译你的C代码,我用的是MSVC(你可以免费试用),用“Visual Studio 20##命令提示符”和“cl /Zi your.c”进行编译。加载 Windbg(Windows 调试工具),按 Ctrl+E 或使用“打开可执行文件”。
加载以下窗口,以便您可以一次看到所有窗口:“调用”、“本地”、“内存”、“线程”和“命令”。
在命令窗口中,使用“bu DoTestStack”在 DoTestStack 上放置一个断点。
使用“g”命令开始调试。
到达断点后,使用“p”单步执行,您应该也会弹出源代码,或者您可以观察应用程序的输出,在 for 循环中运行后,返回到 Windbg。打开内存窗口并将其设置为“i”中地址类型的“指针和符号”,它应该具有来自编译(/Zi)的调试信息,并将为您提供从以下位置开始的“指针和符号”列表指向 i 地址的指针。
输出应该与测试代码相同(在完成我建议的更改之后),如果继续点击 p,您还会看到在内存窗口中您可以观察到每次执行时 i 的值发生变化,但是作为 printf现在正在打印其他值,您将在输出上的原始“0”处看到它;)。
您也可以使用windbg命令dds(注意i的值为0xb,因为它位于for循环的中间)
这与以下相同(除了没有符号支持)修改后的测试代码;
A good idea if you want to know how this all works is to run your application in a debugger. It's really cut's the crap to figure out what's going on.
Any debugger is OK, however I know windbg best so here's some pointers on where to get started at a slightly modified version of your code, like Greg suggested, you need an int*.
CHANGE:
TO:
DELETE:
MOVE(first variable, before x1):
NEW CHANGE(much easeir to read, and doing as previously (x-i) is random/not currently in-scope values):
This change will show effectivly the stack-frame address and the value.
In windbg, get the calls, memory, threads and command windows up so you can see them all.
Compile your C code with a compiler, I used MSVC (you can get it for trial for free), compile with the "Visual Studio 20## Command Prompt" with "cl /Zi your.c". Load up Windbg (debugging tools for Windows), press Ctrl+E or use "open executable".
Load the following windows so you can see them all at one time, Calls, Locals, Memory, Threads and Command.
At the command window, put a breakpoint on your DoTestStack with "bu DoTestStack".
start debugging with "g" command.
After you get to the breakpoint, use "p" to single step, you should get the source popped up also, or you can watch the output of your application, after it's running in the for loop, go back to Windbg. Get the memory window up and set it to "Pointers and Symbols" for an address type in "i", it should have the debug information from the compile (/Zi) and will give you a listing of "Pointers and symbols" starting from a pointer to the address of i.
The output should be identical to the test code (after you do the changes I suggested), if you continue to hit p, you will see also that in the memory window you can observe the value for i changing per execution, however as the printf is now printing other values, you will simply see it at the original "0" on the output ;).
You can alternativly use the windbg command dds (note the value for i is 0xb as it's in the middle of the for loop)
This is identical (exept no symbol support) as the modified test code;