将 C 数组设置为内联 asm 的新调用堆栈 (ESP)?
Setting a C array as a new call stack (ESP) from inline asm?
正如我的标题所暗示的,我很感兴趣是否可以分配一个 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( ) 调用,将其 return 地址和局部变量存储在这个新堆栈(我们的 C 数组)中。
我想知道这种方法是否可行?如果可以,如何实现?
在尝试实现上面的代码时,我 运行 GDB 只是为了查看有关我的堆栈的一些信息(例如:GDB 中的信息堆栈命令),我一直得到 "No Stack",这可能意味着堆栈指针是被送入深渊。
PS:我将其实现为 kernel-level 代码
也许这可以作为一个仅适用于玩具实验的巨大不安全黑客。如果要设置新堆栈,请在调用 C 函数之前在手写 asm 中执行此操作。
这个 hack 可以用于调用 foo()
,但是 return 0;
呢?编译器生成的代码将尝试从当前的 %esp.
弹出一个 return 地址
(或者如果优化被禁用,将使用 leave
在弹出保存的 EBP 之前设置 ESP = EBP
。这将切换回初始堆栈。所以行为取决于在优化级别上! 你不想要那个。)
使用 GDB 单步执行您的代码并实际观察 reg 值的变化,例如layout reg
.
但是是的,&myStack + 1
是数组末尾后一位的地址,并且确实导致 movl $myStack+1024, %eax
作为 asm 语句的设置(其中 %0
在模板中扩展为 %eax
,因为编译器为 "rm"
操作数选择了那个寄存器。你没有给它一个立即常量的选项,否则它会用 movl $myStack+1024, %esp
).
https://godbolt.org/z/Nz6DgA 显示它 "works" 并且在启用优化的情况下到达 ret 时会立即崩溃,因为它试图 pop
并且 ESP 指向 one-past-the -myStack
.
结束
I am currently toying around implementation of threads in kernel level, hence idea is to assign separate stack for each thread, and shift between them
特别是如果 main
实际上必须 return
,那么是的,您需要在 使用它之前设置一个堆栈 call
任何事物。否则最后的 return 地址将在错误的堆栈上!
例如,在 Linux 下,pthread 库通过使用 mmap()
分配新堆栈来创建一个新线程,然后将该堆栈地址作为操作数传递给 clone()
。所以新线程永远不会使用父线程的堆栈,只会使用它自己的堆栈。我假设新任务的内核端线程堆栈创建是类似的。您分配一个新堆栈,然后将其用于新线程上下文。
您可以在顶部放置一个 "return address",这样在新线程中调用的第一个函数实际上会 return 到线程退出/清理函数。可能为此需要一些汇编。或者使实际的线程入口点成为一个不 return 的函数,而是清理线程上下文并切换到另一个线程或调用您的调度程序或其他东西。
That is kind of what I am going for, having thread using its own stack as opposed to main one. However, I do not have paging enabled and would like to implement stack creation as simple as possible for now (thus C array seemed like easy solution)
不幸的是,这太简单了,实际上不起作用。
是的,你可以使用 C 数组作为线程堆栈(如果你正好有一个额外的线程......),问题是你如何切换到它。
您将需要在某个时候编写一个上下文切换函数来保存一个寄存器上下文并加载另一个。 (例如,Google,您可以在 Stack Overflow 上找到一些,并且可能在 https://www.osdev.org/ 上找到一些东西。)
在内存中创建一个新的线程上下文结构,其堆栈指针指向线程堆栈的顶部,其 EIP 指向线程入口点。调用您的上下文切换函数以切换到该新上下文。
从 C 编译器的 POV 来看,上下文切换函数看起来就像任何其他函数调用。它return最终,并且可能修改了任何全局可访问的 C 对象。它暂时将 ESP 指向其他地方并不重要。 "Like any other function call" 包括破坏调用破坏的寄存器,顺便说一句,所以你不需要 save/restore EAX/ECX/EDX .上下文切换函数的调用者已经假定它们已被销毁。
你通常应该在 asm 中手写,而不是内联 asm。从内联 asm 更改 ESP 充满了危险,并且 is officially documented as not supported by GCC。
This is because the compiler requires the value of the stack pointer to be the same after an asm statement as it was on entry to the statement
正如我的标题所暗示的,我很感兴趣是否可以分配一个 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( ) 调用,将其 return 地址和局部变量存储在这个新堆栈(我们的 C 数组)中。
我想知道这种方法是否可行?如果可以,如何实现?
在尝试实现上面的代码时,我 运行 GDB 只是为了查看有关我的堆栈的一些信息(例如:GDB 中的信息堆栈命令),我一直得到 "No Stack",这可能意味着堆栈指针是被送入深渊。
PS:我将其实现为 kernel-level 代码
也许这可以作为一个仅适用于玩具实验的巨大不安全黑客。如果要设置新堆栈,请在调用 C 函数之前在手写 asm 中执行此操作。
这个 hack 可以用于调用 foo()
,但是 return 0;
呢?编译器生成的代码将尝试从当前的 %esp.
(或者如果优化被禁用,将使用 leave
在弹出保存的 EBP 之前设置 ESP = EBP
。这将切换回初始堆栈。所以行为取决于在优化级别上! 你不想要那个。)
使用 GDB 单步执行您的代码并实际观察 reg 值的变化,例如layout reg
.
但是是的,&myStack + 1
是数组末尾后一位的地址,并且确实导致 movl $myStack+1024, %eax
作为 asm 语句的设置(其中 %0
在模板中扩展为 %eax
,因为编译器为 "rm"
操作数选择了那个寄存器。你没有给它一个立即常量的选项,否则它会用 movl $myStack+1024, %esp
).
https://godbolt.org/z/Nz6DgA 显示它 "works" 并且在启用优化的情况下到达 ret 时会立即崩溃,因为它试图 pop
并且 ESP 指向 one-past-the -myStack
.
I am currently toying around implementation of threads in kernel level, hence idea is to assign separate stack for each thread, and shift between them
特别是如果 main
实际上必须 return
,那么是的,您需要在 使用它之前设置一个堆栈 call
任何事物。否则最后的 return 地址将在错误的堆栈上!
例如,在 Linux 下,pthread 库通过使用 mmap()
分配新堆栈来创建一个新线程,然后将该堆栈地址作为操作数传递给 clone()
。所以新线程永远不会使用父线程的堆栈,只会使用它自己的堆栈。我假设新任务的内核端线程堆栈创建是类似的。您分配一个新堆栈,然后将其用于新线程上下文。
您可以在顶部放置一个 "return address",这样在新线程中调用的第一个函数实际上会 return 到线程退出/清理函数。可能为此需要一些汇编。或者使实际的线程入口点成为一个不 return 的函数,而是清理线程上下文并切换到另一个线程或调用您的调度程序或其他东西。
That is kind of what I am going for, having thread using its own stack as opposed to main one. However, I do not have paging enabled and would like to implement stack creation as simple as possible for now (thus C array seemed like easy solution)
不幸的是,这太简单了,实际上不起作用。
是的,你可以使用 C 数组作为线程堆栈(如果你正好有一个额外的线程......),问题是你如何切换到它。
您将需要在某个时候编写一个上下文切换函数来保存一个寄存器上下文并加载另一个。 (例如,Google,您可以在 Stack Overflow 上找到一些,并且可能在 https://www.osdev.org/ 上找到一些东西。)
在内存中创建一个新的线程上下文结构,其堆栈指针指向线程堆栈的顶部,其 EIP 指向线程入口点。调用您的上下文切换函数以切换到该新上下文。
从 C 编译器的 POV 来看,上下文切换函数看起来就像任何其他函数调用。它return最终,并且可能修改了任何全局可访问的 C 对象。它暂时将 ESP 指向其他地方并不重要。 "Like any other function call" 包括破坏调用破坏的寄存器,顺便说一句,所以你不需要 save/restore EAX/ECX/EDX .上下文切换函数的调用者已经假定它们已被销毁。
你通常应该在 asm 中手写,而不是内联 asm。从内联 asm 更改 ESP 充满了危险,并且 is officially documented as not supported by GCC。
This is because the compiler requires the value of the stack pointer to be the same after an asm statement as it was on entry to the statement