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

前端 未结 2 1515
萌比男神i
萌比男神i 2021-01-26 13:20

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

2条回答
  •  -上瘾入骨i
    2021-01-26 13:59

    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.

提交回复
热议问题