【问题标题】:GCC - Label address return current EIP rather than real label addressGCC - 标签地址返回当前的 EIP 而不是真实的标签地址
【发布时间】:2016-12-23 09:31:00
【问题描述】:

在尝试编写操作系统时,我需要获取当前函数的结束地址(就在结尾之前)以进行任务切换。
具体来说,我的问题是让一个 EIP 分配给我在复制堆栈中新创建的任务(进程)。我已经设法保存/恢复进程的寄存器,但我需要找到子进程在其 EIP 中的值。

我使用了 GCC 对 C 标准的扩展:Labels as ValuesLocal 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-&gt;regs.eip = (uintptr_t)&amp;&amp;fork_endl 保存0x00105096 而不是0x00105008
CFLAGS-O0 -std=gnu99 -fgnu89-inline -DDEBUG -ggdb3 -ffreestanding -fbuiltin(此处未显示与警告相关的选项)。

评论 __label__ fork_end; 不会改变任何事情。

【问题讨论】:

  • 请提供有关&amp;&amp; 的gcc 文档的参考,并说明您理解它的作用。使用扩展时它不会发出警告。请参阅How to Ask 并提供所有必需的信息。
  • 已编辑,@Olaf,gcc 使用-Wpedantic 选项警告非标准代码。感谢您的建议。
  • 啊哈,我明白了。使用带有扩展名的-Wpedantic 听起来是个坏主意。无论如何,您的演员表是实现定义的,并且不需要 uint32_t 来保存指针,也没有 1:1 分配。在 64 位系统上,您已经被淘汰了。至少使用正确的类型。有什么理由不使用 void * 作为指针,而是使用整数?
  • 个人意见:我认为该代码没有任何意义。你可能有一个 XY 问题。
  • 只使用汇编。

标签: c gcc osdev


【解决方案1】:

编译器似乎正在完全优化标签,因为没有任何代码路径可以通向它。我已经确认将标签移动到 return 语句之前并确保在分配和标签之间有实际代码会导致我认为是您想要的行为。这是我放在一起测试的代码:

void *fork(void) {
  __label__ fork_end;
  void *test = &&fork_end;

  test++;

  fork_end:
  return test;
}

基于此,我希望您确实可以通过稍微重新设计代码路径以确保标签点可以通过任何代码路径到达。

【讨论】:

  • 由于调用者进程预计不会到达这个标签,我不得不使用 never-true if 来欺骗 GCC 并让它被视为可达,即使它不是。无论如何,非常感谢@joel-c
猜你喜欢
  • 2011-10-22
  • 2011-09-19
  • 2017-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多