【问题标题】:Reading variable with inline assembly使用内联汇编读取变量
【发布时间】:2021-04-06 22:09:56
【问题描述】:

我想知道,根据实际指令读取输入变量是否有限制?

我正在尝试为 .rept 指令读取内联汇编中的输入变量,以重复指令 x 的次数。

但是,汇编程序抱怨: Error: negative count for REPT - ignored

这是我的方法:

const int x = 42;
__asm__ volatile (".rept %[in]" :: [in] "m" (x));
__asm__ volatile (".endr");

尝试将变量值加载到寄存器中,正如预期的那样:

int function(void) {
    const int x = 42;
    __asm__ volatile ("mov %[in], %%eax" :: [in] "m" (x));
    __asm__ volatile ("ret");
}

返回42,反汇编看起来和预期一样。

我尝试在汇编中编写此代码,以查看是否可以将常量与 .rept 指令一起使用,并且确实可以使用

global _start

section .data
    nvalue  equ     39

section .text

_start:
    push    rbp
    mov     rbp,    rsp

    %rep nvalue
        nop
    %endrep

    mov     rax,    60
    mov     rdi,    nvalue
    syscall

反汇编看起来像预期的那样:

Disassembly of section .text:

0000000000401000 <_start>:
  401000:       55                      push   rbp
  401001:       48 89 e5                mov    rbp,rsp
  401004:       90                      nop
  ...
  40102d:       90                      nop
  40102e:       b8 3c 00 00 00          mov    eax,0x3c
  401033:       bf 2a 00 00 00          mov    edi,0x2a
  401038:       0f 05                   syscall

我是否将.rept%rep 混淆了,它们不代表相同的操作吗?

任何帮助将不胜感激。

【问题讨论】:

  • .rept 是对汇编器重复代码的指令。它不会使用您使用"m" 指定的内存引用。编译器可能会将内存引用写成-4(%rbp) 之类的东西,因此会出现有关负值的错误消息。我不认为 GCC 或 Clang 对此有限制。您可以将"i" 用于立即操作数,但它带有表示指令中立即操作数的标记,在英特尔程序集中使用$,因此它将$42 放入程序集中,而您需要一个普通的@987654338 @。可能没有办法做到这一点。
  • 您不能在asm() 语句中安全地使用ret,除非作为__attribute__((naked)) 函数主体的一部分。 (在这种情况下,整个主体必须​​是基本 Asm 语句;没有输入/输出约束。即,这是一种让编译器在您用 asm 编写整个函数主体时进行名称修改的方法。)除此之外,您不能ret;当编译器将您的函数内联到某个调用者时,这将中断。
  • 这很有意义。查看int function()的反汇编,可以看出[rbp-0x4]确实是内存引用的产物。非常感谢 Eric 的解释。
  • 哦,哇,谢谢你指出彼得!确实,我只是尝试从main() 返回function() 的返回值,然后一切都崩溃了。
  • 类似地,如果您在扩展的asm 语句中写入寄存器,则需要将其作为clobber 或输出操作数提及。即在编译器的脚趾上编写 EAX 步骤的 asm,你应该预料到会损坏。

标签: c gcc inline-assembly


【解决方案1】:

如 cmets 中所述,i 操作数通常会以 $ 符号开头插入,这在 AT&T 语法中作为立即操作数是正确的,但不适用于像 .rept 这样的汇编指令。您可以使用c 修饰符来抑制它,请参阅Section 6.47.2.8 of the GCC manual

void many_nops(void) {
    const int num = 42;
    asm(".rept %c0 ; nop ; .endr" : : "i" (num));
}

这将内联 42 个 nop 指令。当然,这只有在 num 可以折叠为编译时常量时才有效;特别是您必须进行优化才能使其工作。

Try on godbolt

您还可以将 Intel asm 语法与 -masm=intel 一起使用,其中常量一开始就没有前导 $

请注意,您的内联 asm 还存在许多其他问题(您不能从内联 asm 中 ret,不应该在多个 asm 语句之间拆分代码等),因此您可能希望从在https://stackoverflow.com/tags/inline-assembly/info阅读一些常见问题解答。

.rept%rep 的问题:第一个是 GNU 汇编器 gas 的正确语法,它通常用于汇编 GCC 输出,第二个是 nasm 汇编器。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-31
    • 2012-02-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多