Warum wird nur ein Zeichen ausgebenen?
-
Guten Morgen,
ich bin weiter fleißig am ausprobieren (ich glaube ich müsste echt mal schauen ob ich da was passendes finde, aber bisher bin ich für x64 Assembler-Programmierung nicht wirklich fündig geworden) und habe versucht ein Zeichen, bzw. nun einen String auszugeben.
Ich gehe so vor, dass ich erst ein Zeichen auf den Stack schiebe und anschließend die Adresse mittels `lea` an write() übergebe. Das klappt mit einem Zeichen auch wunderbar, jedoch nicht wenn ich zwei Elemente auf den Stack schiebe und den size-Parameter eine 2 übergebe.
.global _start .text _start: pushq $65 pushq $66 movq $1, %rax movq $1, %rdi leaq (%rsp), %rsi movq $2, %rdx syscall movq $60, %rax xor %rdi, %rdi syscall
push dekrementiert laut der Dokumentation von AMD den Stackpointer und schiebt den Wert auf den Stack. Also müsste der Stack nach den beiden push doch so aussehen:
65 66 <-- und hier müsste %sp hinzeigen
Oder sehe ich das falsch? Warum klappt das nicht so, wie ich mir das vorstelle.
Meine Idee war auch schon dem leaq-Befehl einen Offset von 1 anzugeben, weil ich mir über die Reihenfolge nicht ganz sicher war, aber wenn ich es mitleaq 0x1(%rsp), %rsi
versuche, wird gar nichts mehr ausgegeben.
Vielen Dank!
-
Der size-Parameter (EDX) wird in Bytes angegeben und sys_write gibt hintereinanderliegende Bytes aus. PUSHQ pusht 8 Bytes auf den Stack ('q' steht für Quadword = 4 * Word = 8 * Bytes). $65 (dezimal) wird vom Assembler umgewandelt in 0x0000000000000041 (hex) und im Speicher abgelegt als 41,00,00,00,00,00,00,00. Das nächste PUSHQ speichert eine solche Folge davor. Letztendlich sieht der Stack so aus:
00 00 00 00 00 00 00 41 00 00 00 00 00 00 00 42 <-- hier zeigt %rsp hin
Wenn Du dem size-Parameter eine '$9' spendierst, erwischt Du noch die 41. Das ist aber nicht besonders sauber, weil theoretisch auch die Nullen ausgegeben werden, bzw. diese ab und zu eine besondere Bedeutung haben. Wenn Du den Stack bündig füllen willst, musst Du eine große Zahl pushen (einzelne Bytes geht nicht) und zweckmäßigerweise auf hexadezimal umstellen. Ersetze mal Deine PUSH durch
movq $0x323575647544, %rax pushq %rax
und stelle Deinen size_Parameter auf $6 ein. Den Umweg über RAX habe ich genommen, weil mein GAS sich weigert, den PUSH eines 6-Byte-Wertes zu assemblieren.
viele grüße
ralph
-
Achh.... Wie dämlich bin ich denn? Ich hätte die man-page vernünftig lesen sollen. Klar, wenn nur Byteweise geschrieben wird, ist das natürlich nachvollziehbar.
Ich wollte mich ganz auf 64-Bit-Entwicklung konzentrieren und versuche ausschließlich die 64-Bit-Anweisungen und Register zu verwenden, aber wäre es hier nicht sogar sinnvoll push zu verwenden? (Ich suche gerade wie viel Byte das pusht, da pushb scheinbar entfernt wurde.)
Ist es eigentlich sinnvoll, je nach Größe lieber %sp, %esp oder %rsp zu verwenden, oder sollte ich das lieber nicht mischen?
Vielen Dank für deine Hilfe!
-
Dudu52 schrieb:
Ich wollte mich ganz auf 64-Bit-Entwicklung konzentrieren und versuche ausschließlich die 64-Bit-Anweisungen und Register zu verwenden, aber wäre es hier nicht sogar sinnvoll push zu verwenden? (Ich suche gerade wie viel Byte das pusht, da pushb scheinbar entfernt wurde.)
Wenn Du das Suffix weglässt, versucht GAS aus anderen Informationen die Größe zu bestimmen. Ein 'mov $65, %rax' assembliert 'movq', ein 'mov $65, %al' assembliert 'movb'. Wenn GAS die Größe nicht bestimmen kann, meckert es. Bei PUSH geht GAS aber von 64 bit aus und pusht 8 Bytes.
Zum Spielen:
.global main .data fmt: .asciz "%016llX\n" .text main: mov %rsp, %rsi ; Ausgangswert von RSP ausgeben mov $fmt, %edi xor %eax, %eax call printf push $65 ; Immediate pushen mov %rsp, %rsi ; Neuen Wert von RSP ausgeben mov $fmt, %edi xor %eax, %eax call printf mov $12345678, %eax push %ax ; 16-bit-Register pushen mov %rsp, %rsi ; Neuen Wert von RSP ausgeben mov $fmt, %edi xor %eax, %eax call printf xor %rdi, %rdi call exit
Assemblieren mit GCC (Erweiterung der Assembler-Datei: .s) und ausführen mit './a.out'.
Ist es eigentlich sinnvoll, je nach Größe lieber %sp, %esp oder %rsp zu verwenden, oder sollte ich das lieber nicht mischen?
Stack-Pointer nicht mischen! Wenn Du ESP einen Wert zuweist, werden die oberen 32 bit von RSP gelöscht (eine Eigenheit des 64-bit-Modus). Bei Berechnungen mit SP gehen Überträge verloren. Bleibe bei RSP.
viele grüße
ralphEDIT: Ich gehe mal davon aus, dass Du auf Linux programmierst, auf Windows (MinGW, CygWin) funktioniert mein Programm nicht, bzw. müsste ganz anders programmiert werden. Dasselbe vermute ich für MacOSX und BSD.
-
Ah super, vielen Dank für deine Antwort! Ich schaue mir den Code mal an!
Jup, ich entwickle auf meinem 64 Bit Debian-System.