【发布时间】:2016-12-23 09:31:00
【问题描述】:
在尝试编写操作系统时,我需要获取当前函数的结束地址(就在结尾之前)以进行任务切换。
具体来说,我的问题是让一个 EIP 分配给我在复制堆栈中新创建的任务(进程)。我已经设法保存/恢复进程的寄存器,但我需要找到子进程在其 EIP 中的值。
我使用了 GCC 对 C 标准的扩展:Labels as Values 和 Local Labels
从文档中:您可以使用一元运算符“&&”获取当前函数(或包含函数)中定义的标签的地址。该值的类型为 void *。
和 : GCC 允许您在任何嵌套块范围内声明本地标签。本地标签与普通标签一样,但您只能在声明它的块内引用它(使用 goto 语句或获取其地址)。
pid_t fork(void)
{
__label__ fork_end;
...
task->regs.eip = (uintptr_t)&&fork_end;
...
return task->pid;
fork_end:;
}
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
我希望task->regs.eip = (uintptr_t)&&fork_endl 保存0x00105096 而不是0x00105008。CFLAGS 是-O0 -std=gnu99 -fgnu89-inline -DDEBUG -ggdb3 -ffreestanding -fbuiltin(此处未显示与警告相关的选项)。
评论 __label__ fork_end; 不会改变任何事情。
【问题讨论】:
-
请提供有关
&&的gcc 文档的参考,并说明您理解它的作用。使用扩展时它不会发出警告。请参阅How to Ask 并提供所有必需的信息。 -
已编辑,@Olaf,gcc 使用
-Wpedantic选项警告非标准代码。感谢您的建议。 -
啊哈,我明白了。使用带有扩展名的
-Wpedantic听起来是个坏主意。无论如何,您的演员表是实现定义的,并且不需要uint32_t来保存指针,也没有 1:1 分配。在 64 位系统上,您已经被淘汰了。至少使用正确的类型。有什么理由不使用void *作为指针,而是使用整数? -
个人意见:我认为该代码没有任何意义。你可能有一个 XY 问题。
-
只使用汇编。