在内联汇编中使用函数范围的标签

Posted

技术标签:

【中文标题】在内联汇编中使用函数范围的标签【英文标题】:Using function-scoped labels in inline assembly 【发布时间】:2012-10-04 15:16:55 【问题描述】:

我在 64 位 little endian Ubuntu 12.04 LTS 和 Eclipse CDT 和 gdb 上使用 gcc-4.7 在 C++ 中使用内联汇编。我正在尝试做的总体方向是为一些深奥的基于堆栈的编程语言制作某种字节码解释器。

在这个例子中,我一次处理 4 位指令(实际上这将取决于指令),当没有更多非零指令时(因为 0 将是 nop)我读取下一个 64 -位词。

不过我想问一下,如何在内联汇编中使用函数范围标签?

似乎汇编中的标签是全局的,这是不利的,我找不到从汇编语句跳转到 C++ 函数范围标签的方法。

以下代码是我正在尝试做的一个示例(注意注释):

  ...
  register long ip  asm("r8");
  register long buf asm("r9");
  register long op  asm("r10");
  ...
fetch:
  asm("mov (%r8), %r9");
  asm("add $8, %r8");
control:
  asm("test %r9, %r9");
  asm("jz   fetch"); // undefined reference to `fetch'
  asm("shr  $4, %r9");
  asm("mov  %r9, %r10");
  asm("and  $0xf, %r10");
  switch (op) 
  ...
  
  goto control;

【问题讨论】:

【参考方案1】:

请注意 gcc 内联 asm 文档中的以下注释:

说到标签,不支持从一个“asm”跳转到另一个。 编译器的优化器不知道这些跳转,因此 在决定如何优化时,他们无法考虑这些因素。

您也不能依赖在一个 asm 中设置的标志在下一个可用,因为编译器可能会在它们之间插入一些东西

使用 gcc 4.5 及更高版本,您可以使用asm goto 做您想做的事:

fetch:
  asm("mov (%r8), %r9");
  asm("add $8, %r8");
control:
  asm goto("test %r9, %r9\n\t"
           "jz  %l[fetch]" : : : : fetch);

请注意,asm 的所有其余部分都是完全不安全的,因为它直接使用寄存器而不在其读/写/破坏列表中声明它们,因此编译器可能决定在其中放入其他内容(尽管带有 @987654324 的变量@ 对它们的声明——它可能会决定它们已经死了,因为它们从未被使用过)。因此,如果您希望这实际上可以与 -O1 或更高版本一起使用,则需要将其编写为:

  ...
  long ip;
  long buf;
  long op;
  ...
fetch:
  asm("mov (%1), %0" : "=r"(buf) : "r"(ip));
  asm("add $8, %0" : "=r"(ip) : "0"(ip));
control:
  asm goto("test %0, %0\n\t"
           "jz   %l[fetch]" : : "r"(buf) : : fetch);
  asm("shr  $4, %0" : "=r"(buf) : "0"(buf));
  asm("mov  %1, %0" : "=r"(op) : "r"(buf));
  asm("and  $0xf, %0" : "=r"(op) : "r"(op));

此时,将其编写为 C 代码要容易得多:

long *ip, buf, op;

fetch:
  do 
    buf = *op++;
control:
   while (!buf);
  op = (buf >>= 4) & 0xf;
  switch(op) 
     :
    
  goto control;

【讨论】:

【参考方案2】:

你应该可以这样做:

fetch:
  asm("afetch: mov(%r8), %r9");
  ...
  asm("jz afetch");

或者,将标签放在单独的asm("afetch:"); 中也可以。请注意不同的名称以避免冲突 - 我不完全确定这是必要的,但我怀疑它是。

【讨论】:

但是如果我有另一个具有相同标签名称的函数呢? void foo() asm("label:") void bar() asm("label:") 无效。 使用本地标签 - 即 <integer>: 形式的标签,并使用 forward 或 backward 引用来引用它们 - asm("0: nop"); asm("jmp 0b");

以上是关于在内联汇编中使用函数范围的标签的主要内容,如果未能解决你的问题,请参考以下文章

如何在内联汇编中实现这一点?

如何在 GCC (x86_64) 中使用内联汇编进行相对跳转/调用

在 C++ 内联 asm 中使用基指针寄存器

C指针原理-C内嵌汇编

x86汇编编程中如何表示FFFFFFBB等十六进制值?

x86汇编编程中如何表示FFFFFFBB等十六进制值?