Frage

Ich spiele derzeit mit ARM Montage auf Linux als Lernübung. Ich verwende ‚nackten‘ Montage, das heißt keine libcrt oder libgcc. Kann mich jemand auf Informationen hinweist, über das, was die Stapel-Zeiger und andere Register angeben, werden zu Beginn des Programms vor dem ersten Befehl aufgerufen werden? Offensichtlich pc / r15 Punkte bei _start, und der Rest erscheinen auf 0 initialisiert werden, mit zwei Ausnahmen; sp / r13 auf eine Adresse weit außerhalb meines Programms, und r1 verweist auf eine etwas höhere Adresse.

So zu einigen soliden Fragen:

  • Was ist der Wert in r1?
  • Ist der Wert in sp ein legitimen Stapel vom Kernel zugewiesen?
  • Wenn nicht, was die bevorzugte Methode ist, einen Stapel zuzuordnen; mit brk oder einem statischen .bss Abschnitt zuweisen?

würde Alle Zeiger geschätzt werden.

War es hilfreich?

Lösung

Hier ist, was ich ein Linux / ARM-Programm zu erhalten verwenden begann mit meinem Compiler:

/** The initial entry point.
 */
asm(
"       .text\n"
"       .globl  _start\n"
"       .align  2\n"
"_start:\n"
"       sub     lr, lr, lr\n"           // Clear the link register.
"       ldr     r0, [sp]\n"             // Get argc...
"       add     r1, sp, #4\n"           // ... and argv ...
"       add     r2, r1, r0, LSL #2\n"   // ... and compute environ.
"       bl      _estart\n"              // Let's go!
"       b       .\n"                    // Never gets here.
"       .size   _start, .-_start\n"
);

Wie Sie sehen können, habe ich nur die argc, argv und environ Sachen aus dem Stapel an [sp].

Eine kleine Klarstellung: Der Stapelzeiger zeigt auf einen gültigen Bereich in dem Prozess Speichern. r0, r1, r2 und r3 sind die ersten drei Parameter für die Funktion aufgerufen wird. Ich bevölkern sie mit argc, argv und environ sind.

Andere Tipps

Da diese Linux, kann man sich anschaut, wie sie vom Kernel implementiert ist.

Die Register scheinen durch den Aufruf von start_thread am Ende der load_elf_binary (wenn Sie ein modernes Linux-System verwenden, wird es fast immer das ELF-Format verwenden). Für ARM, scheinen die Register gesetzt werden, wie folgt:

r0 = first word in the stack
r1 = second word in the stack
r2 = third word in the stack
sp = address of the stack
pc = binary entry point
cpsr = endianess, thumb mode, and address limit set as needed

Offensichtlich haben Sie einen gültigen Stapel. Ich denke, dass die Werte von r0-r2 Spam eingestuft werden, und Sie sollten stattdessen alles vom Stapel lesen (Sie werden sehen, warum ich diese später denken). Nun wollen wir sehen, was auf dem Stapel ist. Was Sie aus dem Stapel gelesen werden durch create_elf_tables gefüllt .

Eine interessante Sache, hier zu bemerken ist, dass diese Funktion Architektur-unabhängig ist, so dass die gleichen Dinge (meistens) auf jeder ELF-basierte Linux-Architektur auf den Stapel gelegt werden. Im Folgenden ist auf dem Stapel in der Reihenfolge Sie sie lesen würden:

  • Die Anzahl der Parameter (dies ist argc in main()).
  • Ein Zeiger auf einen C-String für jeden Parameter, gefolgt von einer Null (dies ist der Inhalt des in argv main(); argv zum ersten diesen Zeigern zeigen würde)
  • .
  • Ein Zeiger auf ein C-String für jede Umgebungsvariable, gefolgt von einer Null (dies ist der Inhalt der selten gesehene envp dritten Parameter von main(); envp auf den ersten diesen Zeiger zeigen würde).
  • Die „Hilfsvektor“, das eine Folge von Paaren (eine Art gefolgt von einem Wert) ist, die durch ein Paar mit einer Null (AT_NULL) in dem ersten Elemente beendet. Dieser Hilfsvektor hat einige interessante und nützliche Informationen, die Sie sehen können (wenn Sie glibc verwenden) mit einem dynamisch-Linked-Programm mit dem LD_SHOW_AUXV Umgebungsvariable läuft gesetzt (zum Beispiel 1) LD_SHOW_AUXV=1 /bin/true. Das ist auch, wo die Dinge ein wenig abhängig von der Architektur variieren können.

Da diese Struktur die gleiche für jede Architektur ist, können Sie in der Zeichnung auf Seite 54 der SYSV 386 ABI bekommen eine bessere Vorstellung davon, wie die Dinge zusammenpassen (beachten Sie jedoch, dass der Hilfsvektortyp Konstanten auf dem Dokument unterscheidet von dem, was Linux verwendet, so dass Sie auf der Linux aussehen sollten Header für sie).

Jetzt können Sie sehen, warum der Inhalt r0-r2 Müll sind. Das erste Wort in dem Stapel ist argc, die zweite ein Zeiger auf die Programmnamen ist (argv[0]), und die dritte war wahrscheinlich Null für Sie, weil Sie das Programm ohne Argumente aufgerufen (es argv[1] wäre). Ich denke, sie werden auf diese Weise für die ältere a.out Binärformat eingerichtet, die, wie Sie unter create_aout_tables setzt argc, argv und envp im Stapel (so sie würden in r0-r2 in der Reihenfolge für einen Anruf erwartet am Ende main()).

Schließlich, warum Null für Sie statt einer r0 (argc sein sollten, wenn Sie das Programm ohne Argumente genannt)? Ich vermute, etwas tief in der syscall Maschinen es mit dem Rückgabewert des Systemaufruf überschrieben (der Null sein würde, da die exec gelungen). sehen in Sie können kernel_execve ( die es nicht verwendendie syscall Maschinen, da es ist, was der Kernel aufruft, wenn er will aus dem Kernel-Modus exec), dass es r0 mit dem Rückgabewert von do_execve absichtlich überschrieben.

Hier ist der uClibc crt . Es scheint darauf hinzudeuten, dass alle Register außer r0 nicht definiert sind (die einen Funktionszeiger enthält mit atexit() registriert werden) und sp, die eine gültige Stapeladresse enthält.

So ist der Wert, den Sie in r1 sehen, ist wahrscheinlich nicht etwas, das Sie sich verlassen können.

Einige Daten auf dem Stapel für Sie gegeben.

Ich habe noch nie ARM Linux verwendet, aber ich schlage vor, Sie entweder für die libcrt an der Quelle und sehen, was sie tun, oder gdb in eine vorhandene ausführbare Datei zu treten. Sie sollten den Quellcode Schritt nur durch den Assembler-Code nicht benötigen.

Alles, was Sie innerhalb des ersten Code, um herauszufinden, durch jede ausführbare Binärdatei ausgeführt passieren sollte.

Hope, das hilft.

Tony

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top