2016-04-13 13 views
0

Basierend auf dieser tutorial, versuche ich Hello World auf die Konsole auf 64-Bit-Linux zu schreiben. Compilation löst keine Fehler aus, aber ich bekomme auch keinen Text auf der Konsole. Ich weiß nicht, was falsch ist.Mischen von C und Assembly. Hallo Welt auf 64-Bit-Linux

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

compilieren:

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

OK, gut, wenn Sie einen Haltepunkt auf dem 'pushq% rbp' setzen, wird es geschlagen? –

+1

Wie ich bereits in Ihrer früheren Eingabe erwähnt habe, ist 'write' ein reserviertes Wort in 'C'. Benennen Sie Ihre Funktion um und versuchen Sie es erneut. – KevinDTimm

+3

Korrektur: Es ist kein reserviertes Wort, aber es ist eine Funktion, die häufig von 'C'-Programmen verwendet wird und möglicherweise mit Ihrem Programm verknüpft ist, bevor Ihre lokale Funktion aufgerufen wird. Mein Rat steht. – KevinDTimm

Antwort

2

Okay, so meinen Code hatte zwei Fehler:

1) Ich nannte meine als Funktion 'write', das ist Common c Name und ich musste es umbenennen.

2) in Funktionsname, ich sollte keine Unterstriche setzen.

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 bleibt wie :) Vielen Dank an alle, die geholfen haben!

2

Das Tutorial, das Sie gerade lesen, stimmt nicht ganz. Es gab zwei unterschiedliche Konventionen für globale Symbole in den ausführbaren ELF-Dateien (ausführbare und verknüpfbare Formate). Eine Konvention besagt, dass allen globalen C-Symbolen das Präfix _ vorangestellt sein sollte, während die andere Konvention den C-Symbolen nicht vorangestellt ist. In GNU/Linux, insbesondere in x86-64 ABI, haben die globalen Symbole kein Präfix _. Allerdings könnte das Tutorial, das Sie verknüpft haben, für einige andere Compiler für Linux/ELF, die nicht die GNU-libc verwendet haben.


Nun, was in Ihrem ursprünglichen Code geschieht, ist, dass Ihre Assembler-Funktion als _write in C-Code sichtbar wäre, nicht write. Stattdessen wird das write Symbol in der libc (der Wrapper für write(2) Systemaufruf) gefunden:

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

Jetzt erklärt man diesewrite als Funktion void write(void);, die als solche undefinierten Verhalten führt, wenn Sie anrufen es. Sie können strace ./program verwenden, um herauszufinden, was System nennt es macht:

% strace ./program 
... 
write(1, "\246^P\313\374\177\0\0\0\0\0\0\0\0"..., 140723719521144) = -1 EFAULT (Bad address) 
... 

So nannte es den write Systemaufruf nicht mit Ihren beabsichtigten Argumenten, sondern mit dem, was Müll gibt es in den dafür vorgesehenen Registern write Wrapper glibc.(Eigentlich ist der "Müll" hier bekannt - erstes Argument ist das argc, und das zweite Argument ist der Wert argv und das dritte Argument ist der Wert char **environ). Und als der Kernel bemerkte, dass ein Puffer, der bei (void*)argv und 140723719521144 Bytes lang begann, nicht vollständig innerhalb des abgebildeten Adressraums enthalten war, gab er EFAULT von diesem Systemaufruf zurück. Ergebnis: kein Absturz, keine Nachricht.


write ist kein reserviertes Wort als solches in C. Es ist eine Funktion, und möglicherweise ein Makro in POSIX. Sie könnte überschreiben, die Verknüpfung Reihenfolge von Bedeutung - wenn Sie definiert write Programm, würde andere Code gegen diese Definition anstelle der in Glibc gefunden werden. Dies würde jedoch bedeuten, dass ein anderer Code, der write aufruft, stattdessen Ihre inkompatible Funktion aufruft.

Die Lösung besteht also darin, keinen Namen zu verwenden, der eine Funktion in der GNU libc oder in anderen Bibliotheken ist, mit denen Sie verknüpft sind. So ist in Assembler können Sie:

.global writehello 
writehello: 

und dann

extern void writehello(void); 

wie Sie selbst herausgefunden haben.