1
static int func_name (const uint8_t * address)
{
    int result;
    asm ("movl $1f, %0; movzbl %1, %0; 1:"
   : "=&a" (result) : "m" (*address));

    return result;
}

我已经通过互联网浏览了内联汇编参考。但我无法弄清楚这段代码在做什么,例如。什么是 $1f ?“m”是什么意思?使用 "=r" 和 "r" 的正常内联约定不是吗?

4

3 回答 3

4

$1f1标签的地址。f指定查找以1正向命名的第一个标签。"m"是内存中的输入操作数。"=&a"是使用eax寄存器的输出操作数。a指定要使用的寄存器,=使其成为输出操作数,并&保证其他操作数不会共享同一个寄存器。

在这里,%0将被第一个操作数(eax寄存器)和%1第二个操作数(指向的地址)替换address

所有这些以及更多内容都在关于Inline assemblyasm 约束的 GCC 文档中进行了解释。

于 2013-02-17T14:40:54.203 回答
2

该代码在功能上与return *address此 wrt 相同,但并不绝对等效。到生成的二进制/目标文件。

在 ELF 中,使用前向引用(即mov $1f, ...检索程序集本地标签的地址)会导致创建所谓的重定位重定位是对链接器的指令(在可执行文件创建时或稍后在可执行文件/库加载时向动态链接器)插入仅在链接/加载时才知道的值。在目标代码中,这看起来像:

部分.text的反汇编:

0000000000000000 :
   0: b8 00 00 00 00 移动 $0x0,%eax
   5: 0f b6 07 movzbl (%rdi),%eax
   8:c3 retq

请注意,此处的值(在该部分的偏移量 1 处.text)为零,即使这实际上不正确 - 这取决于函数在运行代码中的最终位置。只有(动态)链接器才能最终知道这一点,而这块内存在加载时需要更新的信息实际上是放在目标文件中的:

$ readelf -a xqf.o
精灵头:
[ ... ]
部分标题:
  [Nr] 名称类型地址偏移量
       大小 EntSize 标志链接信息对齐
  [0]空0000000000000000 00000000
       0000000000000000 0000000000000000 0 0 0
  [1] .text PROGBITS 0000000000000000 00000040
       0000000000000009 0000000000000000 AX 0 0 16
  [2] .rela.text RELA 0000000000000000 000004e0
       0000000000000018 0000000000000018 10 1 8
[ ... ]
偏移 0x4e0 处的重定位节“.rela.text”包含 1 个条目:
  偏移信息类型 Sym。价值符号。姓名+加号
000000000001 00020000000a R_X86_64_32 0000000000000000 .text + 8
[ ... ]

这个 ELF 部分条目说:

  • 查看1.text部分的偏移量
  • 有一个 32 位的值将被符号扩展为 64 位 ( R_X86_64_32)
  • 您(作为链接器)需要放置的值是.text + 8(必须在链接/加载时计算)

此条目是根据mov $1f, %0说明创建的。如果你把它排除在外(或只是写return *address),它就不会在那里。

我通过删除static限定符来强制生成上述代码;如果不这样做,一个简单的编译实际上根本不会创建任何代码static如果不使用,代码会被删除,并且很多时候,如果使用,则内联)。

如上所述,由于函数是static,因此编译器通常会在调用站点内联它。因此,使用它的信息通常会丢失,调试器检测它的能力也会丢失。但是这里展示的技巧可以(间接地)恢复这一点,因为每次使用该函数都会创建一个重定位条目。除此之外,像这样的方法可用于在二进制文件中建立检测点;在可通过目标文件格式恢复的位置插入众所周知/严格定义但在功能上无意义的小汇编语句,然后让调试器/跟踪实用程序在需要时用“更有用”的东西替换它们。

于 2013-02-19T10:29:01.083 回答
2

这段代码(除了由于两个拼写错误而无法编译)几乎没有用处。

这就是它变成的(使用-S开关):

_func_name:
        movl 4(%esp), %edx ; edx = the "address" parameter
        movl $1f, %eax ; eax = the address of the "1" label
        movzbl (%edx), %eax; eax = byte from address in edx, IOW, "*address"
     1:
        ret

所以整个函数体可以替换为

return *address;
于 2013-02-17T14:51:09.340 回答