tos*_*a98 3 c assembly gcc x86-64 inline-assembly
首先,如果有人知道标准 C 库的一个函数,该函数无需查找二进制零即可打印字符串,但需要绘制字符数,请告诉我!
否则,我有这个问题:
void printStringWithLength(char *str_ptr, int n_chars){
asm("mov 4, %rax");//Function number (write)
asm("mov 1, %rbx");//File descriptor (stdout)
asm("mov $str_ptr, %rcx");
asm("mov $n_chars, %rdx");
asm("int 0x80");
return;
}
Run Code Online (Sandbox Code Playgroud)
GCC 将以下错误告知“int”指令:
"Error: operand size mismatch for 'int'"
Run Code Online (Sandbox Code Playgroud)
有人可以告诉我这个问题吗?
您的代码存在许多问题。让我一步一步地回顾它们。
首先,int $0x80系统调用接口仅适用于 32 位代码。您不应该在 64 位代码中使用它,因为它只接受 32 位参数。在 64 位代码中,使用syscall接口。系统调用是相似的,但有些数字不同。
其次,在 AT&T 汇编语法中,立即数必须以美元符号为前缀。所以是mov $4, %rax,不是mov 4, %rax。后者会尝试将地址的内容移动4到rax显然不是您想要的。
第三,不能在内联汇编中只引用自动变量的名称。如果需要,您必须使用扩展程序集告诉编译器您想使用哪些变量。例如,在您的代码中,您可以执行以下操作:
asm volatile("mov $4, %%eax; mov $1, %%edi; mov %0, %%esi; mov %2, %%edx; syscall"
:: "r"(str_ptr), "r"(n_chars) : "rdi", "rsi", "rdx", "rax", "memory");
Run Code Online (Sandbox Code Playgroud)
第四,gcc是一个优化编译器。默认情况下,它假定内联汇编语句类似于纯函数,输出是显式输入的纯函数。如果输出未使用,则可以优化 asm 语句,或者在使用相同输入运行时将其提升到循环之外。
但是像这样的系统调用write有一个副作用,你需要编译器来保持,所以它不是纯粹的。您需要 asm 语句以与 C 抽象机相同的次数和相同的顺序运行。 asm volatile将使这发生。(没有输出的 asm 语句是隐式易失性的,但是当副作用是 asm 语句的主要目的时,最好将其明确化。另外,我们确实希望使用输出操作数来告诉编译器 RAX 已修改,以及作为输入,这是我们无法用 clobber 做的。)
您总是需要使用扩展内联汇编语法向编译器准确描述 asm 的输入、输出和破坏。否则你会踩到编译器的脚趾(它假设寄存器不变,除非它们是输出或破坏)。(相关:?我怎样才能表明该内存*可以使用尖*通过内联ASM参数显示,指针输入操作本身并没有暗示指向的内存也是一个输入用的虚拟。"m"输入或"memory"clobber 强制所有可访问的内存同步。)
您应该简化代码,不要编写自己的mov指令将数据放入寄存器,而是让编译器执行此操作。例如,您的程序集变为:
ssize_t retval;
asm volatile ("syscall" // note only 1 instruction in the template
: "=a"(retval) // RAX gets the return value
: "a"(SYS_write), "D"(STDOUT_FILENO), "S"(str_ptr), "d"(n_chars)
: "memory", "rcx", "r11" // syscall destroys RCX and R11
);
Run Code Online (Sandbox Code Playgroud)
whereSYS_WRITE在<sys/syscall.h>和STDOUT_FILENOin 中定义<stdio.h>。我不会向您解释扩展内联汇编的所有细节。通常使用内联汇编通常是一个坏主意。如果您有兴趣,请阅读文档。( https://stackoverflow.com/tags/inline-assembly/info )
第五,尽可能避免使用内联汇编。例如,要进行系统调用,请使用以下syscall函数unistd.h:
syscall(SYS_write, STDOUT_FILENO, str_ptr, (size_t)n_chars);
Run Code Online (Sandbox Code Playgroud)
这是正确的。但它不会内联到您的代码中,因此例如,如果您想真正内联系统调用而不是调用 libc 函数,请使用 MUSL 中的包装宏。
第六,经常检查你要调用的系统调用是否已经在C标准库中可用。在这种情况下,它是,所以你应该只写
write(STDOUT_FILENO, str_ptr, n_chars);
Run Code Online (Sandbox Code Playgroud)
并完全避免这一切。
第七,如果您更喜欢使用stdio,请fwrite改用:
fwrite(str_ptr, 1, n_chars, stdout);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6387 次 |
| 最近记录: |