本文介绍了混合C和汇编. 64位Linux上的"Hello World"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

限时删除!!

基于此教程,我试图将Hello World写入控制台在64位Linux上.编译不会引起任何错误,但控制台上也不会显示任何文本.我不知道怎么了.

Based on this tutorial, I am trying to write Hello World to the console on 64 bit Linux. Compilation raises no errors, but I get no text on console either. I don't know what is wrong.

写入.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:

extern void write(void);
int main (int argc, char **argv)
{
    write();
    return 0;
}

编译:

as write.s -o write.o
gcc main.c -c -o main.o
gcc main.o write.o -o program
./program

推荐答案

您正在阅读的教程不太正确. ELF(可执行和可链接格式)可执行文件中的全局符号有两种不同的约定.一种约定表示所有全局C符号应以_为前缀,另一种约定则不以C符号为前缀. 在GNU/Linux中,尤其是在x86-64 ABI中,全局符号不带_ 前缀.但是,您链接的教程可能适用于某些不使用GNU libc的 other Linux/ELF编译器.

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. One convention says that all global C symbols should be prefixed with _, the other convention does not prefix the C symbols. In GNU/Linux, especially in x86-64 ABI, the global symbols are not prefixed with _. However the tutorial that you linked might be right for some other compiler for Linux/ELF that didn't use the GNU libc.

现在,在原始代码中发生的事情是,汇编函数在C代码中将显示为_write,而不是write.而是在libc(用于write(2)系统调用的包装器)中找到write符号:

Now, what happens in your original code is that your assembler function would be visible as _write in C code, not write. Instead, the write symbol is found in the libc (the wrapper for write(2) system call):

ssize_t write(int fd, const void *buf, size_t count);

现在,您将 this write声明为函数void write(void);,当您调用它时,会导致未定义的行为.您可以使用strace ./program找出它产生的系统调用:

Now you declared this write as a function 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
...
write(1, "\246^P\313\374\177\0\0\0\0\0\0\0\0"..., 140723719521144) = -1 EFAULT (Bad address)
...

因此,它调用的write系统调用不是使用您想要的参数,而是使用提供给glibc write包装器的寄存器中的任何垃圾. (实际上,这里已知垃圾"-第一个参数是argc,第二个参数是argv的值,而第三个参数是char **environ的值).而且,由于内核注意到以(void*)argv开始且长度为140723719521144字节的缓冲区没有完全包含在映射的地址空间中,因此它从该系统调用中返回了EFAULT.结果:没有崩溃,没有消息.

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. (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). 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. Result: no crash, no message.

write不是C中的保留字.它是一个函数,在POSIX中可能是宏.您可以 覆盖它,链接顺序很重要-如果您编程定义write,则将根据该定义链接其他代码,而不是glibc中的代码.但是,这意味着其他调用write的代码最终将改为调用您的不兼容函数.

write is not a reserved word as such in C. It is a function and possibly a macro in POSIX. 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. However this would mean that other code calling write would end up calling your incompatible function instead.

因此,解决方案是不要使用GNU libc或链接到的任何其他库中的函数名称.因此,在汇编程序中,您可以使用:

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. Thus in assembler you can use:

.global writehello
writehello:

然后

extern void writehello(void);

当您自己发现时.

这篇关于混合C和汇编. 64位Linux上的"Hello World"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

1403页,肝出来的..

09-07 03:09