【发布时间】:2020-07-18 03:56:21
【问题描述】:
正如我的标题所暗示的,我很感兴趣是否可以分配一个 C 数组,并通过相应地将 ESP 寄存器指向它来使其表现得像一个堆栈。
一些代码示例...
void foo(){
int x = 99;
int y = 89;
return;
}
char myStack[1024];
void main(){
int main_num = 66;
__asm volatile("movl %0, %%esp": : "rm" (&myStack+1)); //Move ESP to the end of the array
foo();
return 0;
}
这段代码背后的想法是创建一个单独的堆栈,专门用于 foo(),方法是首先将 ESP 指向 myStack 数组的末尾(因为堆栈会向低地址增长),然后调用 foo() ,现在将其返回地址和局部变量存储在这个新堆栈(我们的 C 数组)中。
我想知道这种方法是否可行?如果可以,如何实现?
在尝试实现上述代码时,我运行 GDB 只是为了查看有关我的堆栈的一些信息(例如:GDB 中的 info stack 命令),我不断收到“No Stack”,这可能意味着堆栈指针被发送到了深渊。
PS:我将其实现为内核级代码
【问题讨论】:
-
也许是一个巨大的不安全黑客,只适用于玩具实验。如果要设置新堆栈,请在调用 C 函数之前在手写 asm 中执行此操作。这个hack可以调用
foo(),但是return 0;呢?编译器生成的代码将尝试从当前的%esp弹出一个返回地址。 (实际上它可能首先使用leave并设置ESP = EBP,然后弹出一个保存的EBP,这将切换回实际堆栈。因此行为取决于优化级别!)使用GDB 单步执行您的代码并实际观看reg 值发生变化,例如layout reg. -
但是,是的,
&myStack + 1是数组末尾的地址,并且确实导致movl $myStack+1024, %eax作为 asm 语句的设置(其中%0扩展为 @ 987654331@)。 godbolt.org/z/Nz6DgA 表明它“工作”,然后当它通过优化到达ret时会立即崩溃。 -
您是否希望通过
myStack[i]从其他C 代码访问堆栈内容?或者这只是静态保留空间的一种方式? GCC 仍然会假设没有任何东西可以给本地人起别名。没有办法告诉它堆栈是一个可观察到的副作用。例如它仍然可以内联函数并将本地变量保存在寄存器中。 你真正希望在这里完成什么? 除了临时使用这个静态数组作为调用堆栈之外还有什么?我不认为您的意思是将调用堆栈用作堆栈数据结构(通常需要手写asm)。 -
嗨,是的,它主要用作静态保留空间。作为一个想法,使用 C 数组似乎更容易,但据我了解,您的 cmets 应该改为在汇编中实现?
-
特别是如果
main必须实际上是return,那么是的,您需要设置一个堆栈之前 使用它来call任何东西。例如,在 Linux 下,pthread 库通过使用mmap()分配它来创建一个具有新堆栈的新线程,然后将该堆栈地址作为操作数传递给clone()。所以新线程从不使用主堆栈,只使用它自己的堆栈。我假设为新任务创建内核端线程堆栈是类似的。您分配一个新堆栈,然后将它用于新的线程上下文。
标签: gcc x86 kernel inline-assembly callstack