gpt4 book ai didi

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

转载 作者:太空宇宙 更新时间:2023-11-04 01:05:09 25 4
gpt4 key购买 nike

如何在内联汇编 gcc 中读取标准输入并写入标准输出,就像我们在 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。不考虑这一点可能会导致各种问题,因此我已将它们添加到 clobber 列表中。

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

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com