简体   繁体   English

混合C和汇编。 在64位Linux上的“ Hello World”

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM