问题
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