gpt4 book ai didi

c - 在 C 中 move 当前堆栈帧

转载 作者:行者123 更新时间:2023-11-30 16:08:21 25 4
gpt4 key购买 nike

我想知道是否有一种方便的方法来复制当前堆栈帧,将其 move 到其他位置,然后从新位置的函数“返回”?

我一直在使用setjmplongjmp,同时在堆栈上分配大数组以强制堆栈指针离开。我熟悉调用约定以及函数参数的结尾等,但我对指针算术不是很有经验。

笼统地描述最终目标;我们的目标是当我调用一个函数时能够分配堆栈帧并跳转到另一个堆栈帧(我们可以调用这个函数switch)。然而,在跳转到新的堆栈帧之前,我希望能够从 switch 获取返回地址,因此当我(大概)longjmpd 到在新框架中,我能够返回到启动上下文切换的位置。

我已经从 this post 中获得了一些关于如何使用 longjmpsetjmp 来模仿协程的灵感。 .

如果这是可能的,它将成为我当前研究的一部分,我正在尝试在编译器中实现(非常粗略的)概念验证扩展。我非常感谢仅解决我第一段中提出的问题的答案和评论。

更新

为了让我的意图更清楚,我用 C 语言编写了这个示例。它需要使用 -fno-stack-protector 进行编译。我想要是将main中的局部变量ab改为not在堆栈上彼此相邻(1),而是按 call 中缓冲区指定的距离分隔开。此外,目前此代码将返回main两次,而我只希望它返回一次(2)。我建议您按以下顺序阅读过程:maincallchange

如果有人能回答上段中提出的两个问题中的任何一个,我将不胜感激。它不一定要漂亮或便携。

再说一遍,我更喜欢问题的答案,而不是更好的处理方法的建议。

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

jmp_buf* buf;
long* retaddr;

int change(void) {
// local variable to use when computing offsets
long a[0];

for(int i = 0; i < 5; i++) a[i]; // same as below, not sure why I need to read this

// save this context
if(setjmp(*buf) == 0) {
return 1;
}
// the following code runs when longjmp was called with *buf

// overwrite this contexts return address with the one used by call
a[2] = *retaddr;

// return, hopefully now to main
return 1;
}

static void* retain;

int call() {
buf = (jmp_buf*)malloc(sizeof(jmp_buf));
retaddr = (long*) malloc(sizeof(long));
long a[0];
for(int i = 0; i < 5; i++) a[i]; // not sure why I need to do this. a[2] reads (nil) otherwise

// store return address
*retaddr = a[2];

// allocate local variables to move the stackpointer
char n[1024];
retain = n; // maybe cheat the optimiser?

// get a jmp_buf from another context
change();

// jump there
longjmp(*buf, 1);
}


// It returns to main twice, I am not sure why
int main(void) {
char a;
call(); // this function should move stackpointer (in this case, 1024 bytes)
char b;
printf("address of a: %p\n", &a);
printf("address of b: %p\n", &b);
return 1;
}

最佳答案

这是可能的,这就是多任务调度程序所做的,例如在嵌入式环境中。
然而,它是极其特定于环境的,并且必须深入研究它所运行的处理器的细节。

基本上,可能的步骤是:

  • 确定包含所需信息的寄存器。根据需要选择它们,它们可能与编译器在堆栈上用于实现函数调用的不同。
  • 了解如何存储其内容(很可能是每个寄存器的特定汇编指令)。
  • 使用它们连续存储所有内容。
  • 执行此操作的位置可能已经在描述和管理当前任务的对象内分配。
  • 考虑不使用返回地址。相反,当完成“插入”任务时,在描述要返回的潜在任务的多个任务数据集中做出决定。这就是调度的核心。如果提前知道返回地址,那么它与正常的函数调用非常相似。 IE。这个想法是有可能返回到与上一个任务不同的任务。这也是任务在很多情况下需要自己的堆栈的原因。

顺便说一句,我不认为指针算术是这里最相关的工具。
构成堆栈帧的寄存器的内容位于寄存器中,而不是指针可以指向的内存中的任何位置。 (至少在大多数当前系统中,C64 不参与其中......)。

关于c - 在 C 中 move 当前堆栈帧,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59352560/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com