Mixing C and Assembly. `Hello World` on 64-bit Linux

﹥>﹥吖頭↗ 提交于 2020-07-31 04:16:54

问题


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.

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:

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 

回答1:


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.

2) in function name, i shouldn't put underscores.

Proper code:

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

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

Compilation stays as is :) Thanks to everyone that helped!




回答2:


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.


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);

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)
...

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 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.

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:

and then

extern void writehello(void);

as you yourself have found out.



来源:https://stackoverflow.com/questions/36606646/mixing-c-and-assembly-hello-world-on-64-bit-linux

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!