[英]Mixing C and Assembly. `Hello World` on 64-bit Linux
Based on this tutorial , I am trying to write Hello World
to the console on 64 bit Linux. 基于本教程 ,我试图将
Hello World
写入64位Linux上的控制台。 Compilation raises no errors, but I get no text on console either. 编译不会引起任何错误,但控制台上也不会显示任何文本。 I don't know what is wrong.
我不知道怎么了
write.s : write.s :
.data
SYSREAD = 0
SYSWRITE = 1
SYSEXIT = 60
STDOUT = 1
STDIN = 0
EXIT_SUCCESS = 0
message: .ascii "Hello, world!\n"
message_len = .-message
.text
.globl _write
_write:
pushq %rbp
movq %rsp, %rbp
movq $SYSWRITE, %rax
movq $STDOUT, %rdi
movq $message, %rsi
movq $message_len, %rdx
syscall
popq %rbp
ret
main.c: main.c中:
extern void write(void);
int main (int argc, char **argv)
{
write();
return 0;
}
Compiling: 编译:
as write.s -o write.o
gcc main.c -c -o main.o
gcc main.o write.o -o program
./program
Okay, so my code had two mistakes: 好的,所以我的代码有两个错误:
1) I named my as function 'write' that is common c name and i needed to rename it. 1)我将我的函数命名为“ write”,这是通用的c名称,我需要重命名它。
2) in function name, i shouldn't put underscores. 2)在函数名称中,我不应该使用下划线。
Proper code: 正确的代码:
writehello.s writehello.s
.data
SYSREAD = 0
SYSWRITE = 1
SYSEXIT = 60
STDOUT = 1
STDIN = 0
EXIT_SUCCESS = 0
message: .ascii "Hello, world!\n"
message_len = .-message
.text
#.global main
#main:
#call write
#movq $SYSEXIT, %rax
#movq $EXIT_SUCCESS, %rdi
#syscall
#********
.global writehello
writehello:
pushq %rbp
movq %rsp, %rbp
movq $SYSWRITE, %rax
movq $STDOUT, %rdi
movq $message, %rsi
movq $message_len, %rdx
syscall
popq %rbp
ret
main.c main.c中
extern void writehello(void);
int main (int argc, char **argv)
{
writehello();
return 0;
}
Compilation stays as is :) Thanks to everyone that helped! 编译保持原样:)谢谢大家的帮助!
The tutorial you're reading is not quite right. 您正在阅读的教程不太正确。 There has been two differing conventions for global symbols in the ELF (Executable and Linkable Format) executables.
ELF(可执行和可链接格式)可执行文件中的全局符号有两种不同的约定。 One convention says that all global C symbols should be prefixed with
_
, the other convention does not prefix the C symbols. 一种约定表示所有全局C符号应以
_
,另一种约定则不以C符号为前缀。 In GNU/Linux, especially in x86-64 ABI, the global symbols are not prefixed with _
. 在GNU / Linux中,尤其是在x86-64 ABI中,全局符号不以
_
为前缀 。 However the tutorial that you linked might be right for some other compiler for Linux/ELF that didn't use the GNU libc. 但是,您链接的教程可能适用于其他不使用GNU libc的Linux / ELF编译器。
Now, what happens in your original code is that your assembler function would be visible as _write
in C code, not write
. 现在,在原始代码中发生的是,汇编程序函数在C代码中将显示为
_write
,而不是write
。 Instead, the write
symbol is found in the libc
(the wrapper for write(2)
system call): 而是在
libc
找到write
符号( write(2)
系统调用的包装器):
ssize_t write(int fd, const void *buf, size_t count);
Now you declared this write
as a function void write(void);
现在,您将此
write
声明为函数void write(void);
, which leads to undefined behaviour as such when you call it. ,当您调用它时会导致未定义的行为 。 You can use
strace ./program
to find out what system calls it makes: 您可以使用
strace ./program
来找出它产生的系统调用:
% strace ./program
...
write(1, "\246^P\313\374\177\0\0\0\0\0\0\0\0"..., 140723719521144) = -1 EFAULT (Bad address)
...
So it called the write
system call not with your intended arguments, but with whatever garbage there was in the registers provided to glibc write
wrapper. 因此,它不使用预期的参数而是通过提供给glibc
write
包装器的寄存器中的任何垃圾来调用write
系统调用。 (actually the "garbage" is known here - first argument is the argc
, and the second argument is the value of argv
and the 3rd argument is the value of char **environ
). (实际上,这里已知“垃圾”-第一个参数是
argc
,第二个参数是argv
的值,第三个参数是char **environ
的值)。 And as the kernel noticed that a buffer starting at (void*)argv
and 140723719521144 bytes long wasn't completely contained within the mapped address space, it returned EFAULT
from that system call. 并且由于内核注意到以
(void*)argv
和140723719521144字节长开头的缓冲区没有完全包含在映射的地址空间中,因此它从该系统调用返回了EFAULT
。 Result: no crash, no message. 结果:没有崩溃,没有消息。
write
is not a reserved word as such in C. It is a function and possibly a macro in POSIX. 在C中,
write
不是保留字。在POSIX中, write
是一个函数,并且可能是宏。 You could overwrite it, the linking order matters - if you program defines write
, other code would be linked against this definition instead of the one found in glibc. 您可以覆盖它,链接顺序很重要-如果您编程定义
write
,则将针对此定义链接其他代码,而不是glibc中找到的代码。 However this would mean that other code calling write
would end up calling your incompatible function instead. 但是,这意味着其他调用
write
代码最终将代替您的不兼容函数。
Thus the solution is to not use a name that is a function in the GNU libc or in any other libraries that you've linked against. 因此,解决方案是不要使用GNU libc或链接到的任何其他库中的函数名称。 Thus in assembler you can use:
因此,在汇编程序中,您可以使用:
.global writehello
writehello:
and then 接着
extern void writehello(void);
as you yourself have found out. 正如您自己发现的那样。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.