需要弄清楚以下内联汇编代码的含义

use*_*964 1 c x86 assembly inline-assembly pintos

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

    return result;
}
Run Code Online (Sandbox Code Playgroud)

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

Fra*_*kH. 5

该代码在功能上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

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

$ readelf -a xqf.o
ELF 标头:
[...]
节标题:
  [编号] 名称 类型 地址 偏移量
       尺寸 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 个条目:
  偏移信息类型符号。价值符号。名称 + 加数
000000000001 00020000000a R_X86_64_32 0000000000000000 .text + 8
[...]

这个 ELF 部分条目说:

  • 查看1.text部分的偏移量
  • 有一个 32 位值将被零扩展为 64 位 ( R_X86_64_32)。这可能旨在用于 32 位代码,但在 64 位非 PIE 可执行文件中,这仍然是将地址放入寄存器的最有效方法;小于RIPlea 1f(%rip), %0相对R_X86_64_PC32重定位。是的,将 RIP 相关的 LEA 放入 32 位寄存器是合法的,如果您不关心截断地址,则可以节省一个字节的机器代码。
  • 您(作为链接器)需要放在那里的值是.text + 8(必须在链接/加载时计算)

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

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

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