从stdin扫描并使用gcc中的内联汇编打印到标准输出 [英] Scan from stdin and print to stdout using inline assembly in gcc

查看:139
本文介绍了从stdin扫描并使用gcc中的内联汇编打印到标准输出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何从标准输入读取并写入内联汇编gcc的标准输出,就像我们在NASM中做的那样:

  _start :
mov ecx,buffer; buffer是一个数据字,在0小节中初始化.data
mov edx,03
mov eax,03; read
mov ebx,00; stdin
int 0x80
;输入输入的数字
mov eax,04;写入
mov ebx,01; stdout
int 0x80

我尝试从内联程序集的stdin读取,然后将输入分配给x:

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

printf(%d,x);
return 0;

$ / code>

然而它没有这样做。



GCC内嵌程序集中的系统调用



这个链接包含一个代码,它只能将一个字符打印到标准输出。

解决方案

<这个代码完全基于我对linux引用的阅读。我不在linux上,所以我不能测试它,但它应该非常接近,我会使用重定向来测试它:a.out< foo.txt

  #include< stdio.h> 

#define SYS_READ 3

int main()
{
char buff [10]; / *声明一个buff来保存返回的字符串。 * /
ssize_t charsread; / *返回的字符数。 * /

/ *使用约束将缓冲区大小转换为edx,stdin句柄号
转换为ebx,将地址加入ecx。此外,0表示该值
与参数0(charsread)的位置相同。所以eax将
保存在输入上的SYS_READ(3),并对输出进行charsread。最后,你
必须使用内存clobber,因为你正在改变buff的内容
,没有任何约束说你是。

这比在asm中执行mov语句
好​​得多。首先,因为gcc会将
的值转移到寄存器中,所以如果你花费
秒读取更多的字符,它可以重新使用它们。 * /

asm volatile(int $ 0x80/ *调用系统调用中断* /
:= a(charsread)
:0(SYS_READ) ,b(STDIN_FILENO),c(buff),d(sizeof(buff))
:memory,cc);

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

返回0;
}

回应Aanchal Dalmia的评论如下:

<1>正如Timothy所说,即使你没有使用返回值,你也必须让gcc知道ax寄存器正在被修改。换句话说,删除= a(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]; / *声明一个buff来保存返回的字符串。 * /
ssize_t charsread; / *返回的字符数。 * /

/ *使用约束将缓冲区大小转换为rdx,stdin处理数字
转换为rdi,将buff地址转换为rsi。此外,0表示该值
与参数0(charsread)的位置相同。因此,eax将
保存在输入上的SYS_READ,并对输出进行charsread。最后,I
使用内存clobber,因为我正在更改buff的内容
,没有任何约束说我是。

这比在asm中执行mov语句
好​​得多。首先,因为gcc会将
的值转移到寄存器中,所以如果你花费
秒读取更多的字符,它可以重新使用它们。 * /
$ b $ asm volatile(syscall/ * make syscall。* /
:= a(charsread)
:0(SYS_READ),D (STDIN_FILENO),S(buff),d(sizeof(buff))
:rcx,r11,memory,cc);

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

返回0;
}

这需要比我更好的linux专家来解释为什么<$ x64上的c $ c> int 0x80 不适用于堆栈变量。但是使用系统调用确实有效,并且syscall在x64上比int更快。



编辑:已经指出内核 clobbers 中出现。未能解释这可能会导致各种问题,所以我已将它们添加到clobber列表中。


How to read from stdin and write to stdout in inline assembly gcc, just like we do it in 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

I tried reading from stdin in inline assembly and then assign the input to 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;
}

However it fails to do so.

syscall from within GCC inline assembly

This link contains a code that is able to print only a single character to the stdout.

解决方案

This code is based solely on my reading of linux references. I'm not on linux, so I cannot test it, but it should be pretty close. I would test it using redirection: 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;
}

Responding to Aanchal Dalmia's comments below:

1) As Timothy says below, even if you aren't using the return value, you must let gcc know that the ax register is being modified. In other words, it isn't safe to remove the "=a" (charsread), even if it appears to work.

2) I was really confused by your observation that this code wouldn't work unless buff was global. Now that I have a linux install to play with, I was able to reproduce the error and I suspect I know the problem. I'll bet you are using the int 0x80 on an x64 system. That's not how you are supposed to call the kernel in 64bit.

Here is some alternate code that shows how to do this call in x64. Note that the function number and the registers have changed from the example above (see 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;
}

It's going to take a better linux expert than me to explain why the int 0x80 on x64 wouldn't work with stack variables. But using syscall does work, and syscall is faster on x64 than int.

Edit: It has been pointed out to me that the kernel clobbers rcx and r11 during syscalls. Failing to account for this can cause all sorts of problems, so I have added them to the clobber list.

这篇关于从stdin扫描并使用gcc中的内联汇编打印到标准输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆