在尝试编写OS时,我需要获取当前函数结束的地址(在结尾之前)以进行任务切换.
具体来说,我的问题是让EIP分配给复制堆栈内新创建的任务(进程).我已经设法为进程保存/恢复寄存器,但我需要找到子进程在其EIP中具有的值.
我使用了GCC对C标准的扩展:标签作为值和本地标签
从文档中:您可以使用一元运算符'&&'获取当前函数(或包含函数)中定义的标签的地址.该值的类型为void*.
和:GCC允许您在任何嵌套块范围内声明本地标签.本地标签就像普通标签一样,但您只能在声明它的块中引用它(使用goto语句或通过获取其地址).
pid_t fork(void)
{
__label__ fork_end;
...
task->regs.eip = (uintptr_t)&&fork_end;
...
return task->pid;
fork_end:;
}
Run Code Online (Sandbox Code Playgroud)
GCC确实编译它,只是警告非标准代码.
但是在反汇编时,gdb显示:
task->regs.eip = (uintptr_t)&&fork_end;
0x00105008 <+87>: mov $0x105008,%edx
0x0010500d <+92>: mov -0xc(%ebp),%eax
0x00105010 <+95>: mov %edx,0x40(%eax)
...
fork_end:;
}
0x00105096 <+229>: leave
0x00105097 <+230>: ret
Run Code Online (Sandbox Code Playgroud)
我希望task->regs.eip = (uintptr_t)&&fork_endl能节省0x00105096而不是0x00105008.
CFLAGS是-O0 -std=gnu99 -fgnu89-inline -DDEBUG -ggdb3 -ffreestanding -fbuiltin(此处未显示警告相关选项).
评论__label__ fork_end;没有任何改变.
编译器似乎正在完全优化标签,因为没有代码路径通向它。我已确认将标签移至return语句之前并确保分配和标签之间存在实际代码会产生我认为是您期望的行为。这是我放在一起测试的代码:
void *fork(void) {
__label__ fork_end;
void *test = &&fork_end;
test++;
fork_end:
return test;
}
Run Code Online (Sandbox Code Playgroud)
基于此,我希望您确实可以通过稍微重新设计代码路径来获得您想要的东西,以确保任何代码路径都可以到达标签点。