c - 使用 gcc 中的内联汇编从 stdin 扫描并打印到 stdout

标签 c gcc nasm inline-assembly

如何在内联汇编 gcc 中读取 stdin 并写入 stdout,就像我们在 NASM 中所做的那样:

_start:
mov ecx, buffer ;buffer is a data word initialised 0h in section .data
mov edx, 03
mov eax, 03 ;read
mov ebx, 00 ;stdin
int 0x80
;Output the number entered
mov eax, 04 ;write
mov ebx, 01 ;stdout
int 0x80

我尝试在内联汇编中从标准输入读取,然后将输入分配给 x:

#include<stdio.h>
int x;
int main()
{
    asm(" movl $5,  %%edx \n\t" " 
    movl $0,  %%ebx \n\t" " 
    movl $3,  %%eax \n\t" " 
    int $0x80 \n\t "
    mov %%ecx,x" 
    ::: "%eax", "%ebx", "%ecx", "%edx");

    printf("%d",x);  
    return 0;
}

但是它没有这样做。

syscall from within GCC inline assembly

此链接包含的代码只能将单个字符打印到标准输出。

最佳答案

此代码完全基于我对 Linux 引用资料的阅读。我不在linux上,所以我无法测试它,但它应该非常接近。我将使用重定向来测试它:a.out < foo.txt

#include <stdio.h>

#define SYS_READ 3

int main()
{
   char buff[10]; /* Declare a buff to hold the returned string. */
   ssize_t charsread; /* The number of characters returned. */

   /* Use constraints to move buffer size into edx, stdin handle number
      into ebx, address of buff into ecx.  Also, "0" means this value
      goes into the same place as parameter 0 (charsread).  So eax will
      hold SYS_READ (3) on input, and charsread on output.  Lastly, you
      MUST use the "memory" clobber since you are changing the contents
      of buff without any of the constraints saying that you are.

      This is a much better approach than doing the "mov" statements
      inside the asm.  For one thing, since gcc will be moving the 
      values into the registers, it can RE-USE them if you make a 
      second call to read more chars. */

   asm volatile("int $0x80" /* Call the syscall interrupt. */
      : "=a" (charsread) 
      : "0" (SYS_READ), "b" (STDIN_FILENO), "c" (buff), "d" (sizeof(buff))
      : "memory", "cc");

    printf("%d: %s", (int)charsread, buff);

    return 0;
}

回应 Aanchal Dalmia 的以下评论:

1) 正如 Timothy 下面所说,即使您不使用返回值,您也必须让 gcc 知道 ax 寄存器正在被修改。换句话说,删除“=a”(字符读取)并不安全,即使它看起来有效。

2)我对你的观察感到非常困惑,除非 buff 是全局的,否则这段代码将无法工作。现在我已经安装了 Linux,我能够重现该错误,并且我怀疑我知道问题所在。我敢打赌您正在 x64 系统上使用 int 0x80。这不是 64 位内核的调用方式。

下面是一些替代代码,展示了如何在 x64 中执行此调用。请注意,函数编号和寄存器与上面的示例相比已发生变化(请参阅 http://blog.rchapman.org/post/36801038863/linux-system-call-table-for-x86-64 ):

#include <stdio.h>

#define SYS_READ 0
#define STDIN_FILENO 0

int main()
{
   char buff[10]; /* Declare a buff to hold the returned string. */
   ssize_t charsread; /* The number of characters returned. */

   /* Use constraints to move buffer size into rdx, stdin handle number
      into rdi, address of buff into rsi.  Also, "0" means this value
      goes into the same place as parameter 0 (charsread).  So eax will
      hold SYS_READ on input, and charsread on output.  Lastly, I
      use the "memory" clobber since I am changing the contents
      of buff without any of the constraints saying that I am.

      This is a much better approach than doing the "mov" statements
      inside the asm.  For one thing, since gcc will be moving the 
      values into the registers, it can RE-USE them if you make a 
      second call to read more chars. */

   asm volatile("syscall" /* Make the syscall. */
      : "=a" (charsread) 
      : "0" (SYS_READ), "D" (STDIN_FILENO), "S" (buff), "d" (sizeof(buff))
      : "rcx", "r11", "memory", "cc");

    printf("%d: %s", (int)charsread, buff);

    return 0;
}

需要比我更好的 Linux 专家来解释为什么 x64 上的 int 0x80 无法使用堆栈变量。但使用 syscall 确实有效,并且 syscall 在 x64 上比 int 更快。

编辑:有人向我指出内核 clobbers系统调用期间的 rcx 和 r11。未能考虑到这一点可能会导致各种问题,因此我已将它们添加到破坏列表中。

关于c - 使用 gcc 中的内联汇编从 stdin 扫描并打印到 stdout,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25345033/

相关文章:

c - sscanf 可选列

linux - 尝试执行二进制文件时出现 "No such file or directory"错误

c - 在汇编中使用 fgets 时出现段错误

python - 如何将变量实现为字符串?

c - Lua错误没有传递 bool 值

c - clang 和 gcc 之间的行为差​​异?

c++ - 本地( block )函数 decln 与 defn 返回类型不匹配;在 C++ 中诊断?

linux - 描述 xmm 寄存器中未传递给 rax 的浮点参数数量的整数

c - 在 C 中运行以下代码时出错

c - 有一些家庭作业的问题,C 编程的东西