You could只需不返回即可安全且便携地做到这一点f1
(or main
)。简单地做他们想做的事would完成后返回,然后退出:
void f2() {
printf("A\n");
exit(0);
}
不幸的是,这违反了“A
必须仅由输出main
function”的要求,所以你必须有一些严肃的内部知识,并执行堆栈的操作(无论它是有风险和不可移植的)。
基本思想是分析状态would就在退出之前f1
,并将代码放入f2
将其设置为该状态。状态可能只是寄存器内容,但是may在某些情况下涉及更复杂的事情(例如,考虑在 C++ 中展开异常处理程序,或者可能由其余部分引起的任何全局影响)f1
).
这样,当你退出时f2
,返回点将位于main
而不是f1
. This could就像这样简单:
void f2() {
asm "load sp <value>"
}
where <value>
是你需要计算的值。注意asm
上面的代码需要根据您的环境进行定制。由于您没有提供CPU架构(或其他具体细节),我使用了mythicalCPU上面带有一个load
指令和sp
登记。
为了进一步说明,假设您编译的代码为f2
看起来像这样(x86-64,带有我添加的注释):
f2: push rbp # prolog stack frame set up.
mov rbp, rsp
## Point 1
mov rsp, rbp # epilog.
pop rbp
ret
and f1
好像:
b_str: .string "B\n"
f1: push rbp # prolog stack frame set up.
mov rbp, rsp
sub rsp, 16 # allocate space for locals.
mov DWORD PTR [rbp-4], 3 # store values for b and c.
mov DWORD PTR [rbp-8], 7
mov eax, DWORD PTR [rbp-4] # add b to c.
add DWORD PTR [rbp-8], eax
mov eax, 0 # make call to f2().
call f2
mov edi, OFFSET FLAT:b_str # output B.
call printf
## Point 2
mov rsp, rbp # epilog.
pop rbp
ret
你需要做的就是在点 1 处放置一些东西,这样它就会返回就像在第 2 点一样。在此代码中,可以通过添加一个值来实现rbp
,正好足以退出:
- 推送的返回值
call f2
; and
- the
push rbp
开始时f2
.
For example, if the call
pushed a 32-bit return address and the push
was for a 64-bit register, you would need to add twelve to rbp
(64 bits = 8 bytes(a) and 32 bits = 4 bytes, giving 12).
我还没有进行深入的分析来确认这是添加的正确值,但它为您提供了基本的想法。
无论如何,它可能需要根据您的情况进行定制,因为它取决于编译器、调用约定、CPU 选择、优化级别以及可能的许多其他因素等因素。
(a) Using the "byte is eight bits" definition, though I normally prefer "octet" for that.