【问题标题】:C asm linux syscall in loop continue for ever循环中的C asm linux系统调用永远继续
【发布时间】:2019-07-31 17:35:48
【问题描述】:

我想使用 C 语言中的扩展 asm 来处理系统调用(写入)。 一切都很好,我正在使用

#define write(RESULT, FD, BUFF, SIZE)   \
    asm volatile ("syscall" :           \
        "=a" (RESULT) :                 \
        "D" (FD), "S" (BUFF), "d" (SIZE), "a" (1))

这没有任何问题......例如

#define write(RESULT, FD, BUFF, SIZE)   \
    asm volatile ("syscall" :           \
        "=a" (RESULT) :                 \
        "D" (FD), "S" (BUFF), "d" (SIZE), "a" (1))

int
main() {

    int res;
    write(res, 1, "Hello\n", 6);

    return 0;
}

我对这段代码没有任何问题,但我的问题是当我拨打这个电话时

write(res, 1, "Hello\n", 6);

循环使用100次

#define write(RESULT, FD, BUFF, SIZE)   \
    asm volatile ("syscall" :           \
        "=a" (RESULT) :                 \
        "D" (FD), "S" (BUFF), "d" (SIZE), "a" (1))

int
main() {

    int res;
    for (int i = 0; i < 100; i++) {
        write(res, 1, "Hello\n", 6);
    }    

    return 0;
}

但是有一点……我对这段代码也没有问题,但只是在调试模式下……如果我将程序设置为(发布)或优化模式,这个循环将永远执行! !!!好像没有 100 次的限制……这仅在“发布”模式下发生,该模式优化了我的代码并使其出错!!

是的......打击源代码在“debug”中找到,但它在“Release”模式下永远写“Hello”(没有限制)

这是我使用的汇编代码online check 带 -O3 选项 (GCC x86_64)

.LC0:
  .string "Hello\n"
main:
  movl $100, %ecx
  movl $.LC0, %esi
  movl $1, %edi
  movl $6, %edx
.L2:
  movl %edi, %eax
  syscall
  subl $1, %ecx
  jne .L2
  xorl %eax, %eax
  ret

【问题讨论】:

  • 您知道您不需要内联汇编程序来调用系统调用,对吧?
  • 为什么我不需要??如果我使用“写”功能,它将使用这个系统调用将在其中发生的 std wirte 函数......而且如果我使用(系统调用)函数,仍然有一个额外的调用“系统调用”函数
  • 只需包含unistd.h 并从那里使用write。无需非便携式内联汇编。
  • 因为它更简单、更便携、更快。
  • 我认为您大大高估了函数调用需要多长时间。

标签: c assembly inline-assembly


【解决方案1】:

syscall 指令破坏者注册 rcxr11。编译器假定它们的值被保留,从而导致无限循环。将这两个添加到clobber列表以解决问题:

#define write(RESULT, FD, BUFF, SIZE)               \
    asm volatile ("syscall" :                       \
        "=a" (RESULT) :                             \
        "D" (FD), "S" (BUFF), "d" (SIZE), "a" (1) : \
        "rcx", "r11", "memory")

【讨论】:

  • 太棒了!!这些寄存器适用于所有系统调用吗?除了 rax 和 rcx 和 r11 还有其他寄存器吗?
  • @Jason :在 Linux 中,系统调用不会恢复的唯一寄存器是 RAX,因为它用作返回值。因此,唯一可能会被破坏的寄存器是 RAX 和两个寄存器 syscall 指令本身会破坏(RCX 和 R11)。
  • @fuz :这是一个例子:pastebin.com/D4fy0uS3 。当优化关闭时,它将按预期执行,因为编译器正在执行所有不必要的加载和存储到内存和从内存中存储。然后用-O3 试试。请注意,字符串已放在堆栈上。如果您查看生成的代码,您会发现优化器从未将字符串写入堆栈,因为它不知道字符串中的数据正在被使用。它只知道指向字符串的指针被用作输入,而不知道指针指向的内容。
  • @jason:没问题。这种潜在的行为实际上记录在memory clobber 的 GCC 内联汇编文档中。它显示了使用 memory clobber 的一些替代方法,其中包括可能添加虚拟内存约束。可以在此处找到相关文档:gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html。只需搜索 The "memory" clobber 即可找到该部分。
  • @fuz:LLVM 并不完全“更优雅”,它只是不擅长围绕内联汇编进行优化。它还总是为像"rm" 这样允许内存源的约束选择内存,即使这意味着首先从寄存器中溢出一个var!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-07-09
  • 2017-06-14
  • 1970-01-01
  • 2010-11-15
  • 2020-11-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多