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

时间:2014-08-16 23:43:12

标签: c gcc nasm inline-assembly

如何从stdin中读取并写入内联汇编gcc中的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

我尝试在内联汇编中从stdin读取,然后将输入分配给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

此链接包含一个只能打印一个字符到stdout的代码。

1 个答案:

答案 0 :(得分:5)

此代码完全基于我对linux引用的阅读。我不是在linux上,所以我无法测试它,但它应该非常接近。我会使用重定向测试它:a.out&lt; 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)正如蒂莫西在下面所说,即使你没有使用返回值,你也必须让gcc知道正在修改ax寄存器。换句话说,删除&#34; = a&#34;是不安全的。 (charsread),即使看起来有效。

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确实有效,并且系统调用在x64上比int更快。

编辑:在系统调用期间,我已经向我指出内核clobbers rcx和r11。如果不考虑这种情况可能会导致各种问题,所以我已将它们添加到clobber列表中。