Je joue actuellement avec Bras Assembly sur Linux en tant qu'exercice d'apprentissage. J'utilise l'assemblée "nue", c'est-à-dire non libcrt ou libgcc. Quelqu'un peut-il me signaler à l'information sur l'état de la pile-pointeur et d'autres registres au début du programme avant la première instruction? Évidemment, PC / R15 points à _start, et le reste semble être initialisé à 0, à deux exceptions près; SP / R13 pointe vers une adresse bien à l'extérieur de mon programme et R1 pointe vers une adresse légèrement supérieure. P>
Donc, à quelques questions solides: p>
Tous les pointeurs seraient appréciés. P>
4 Réponses :
Je n'ai jamais utilisé ARM Linux, mais je vous suggère de regarder la source de la libcrt et de voir ce qu'elles font ou utilisent le GDB pour entrer dans une exécutable existante. Vous ne devriez pas avoir besoin du code source simplement dans le code de montage. P>
Tout ce dont vous avez besoin pour savoir devrait arriver dans le tout premier code exécuté par n'importe quel exécutable binaire. P>
J'espère que cela aide. P>
Tony p>
Voici le UCLIBC CRT . Il semble suggérer que tous les registres ne sont indéfinis sauf Donc, la valeur que vous voyez dans Certaines données sont placées sur la pile pour vous. P> R0 code> (qui contient un pointeur de fonction à enregistrer avec
atexit () code>) et
sp code> qui contient une adresse de pile valide. p>
r1 code> n'est probablement pas quelque chose que vous pouvez compter sur. p>
Voici ce que j'utilise pour obtenir un programme Linux / ARM a commencé avec mon compilateur:
/** 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" );
Merci. Cette configuration est-elle documentée partout où vous connaissez?
Je suis sûr que cela doit être mais je dois admettre que je l'ai compris avec GDB.
Si le programme est appelé sans argument, il y a un moyen d'obtenir ENVP dans une seule instruction comme LDR RN, SP, #XXX?
Comme il s'agit de Linux, vous pouvez regarder comment il est mis en œuvre par le noyau.
Les registres semblent être définis par l'appel vers clairement, vous avez une pile valide. Je pense que les valeurs de une chose intéressante à remarquer ici est que cette fonction est indépendante de l'architecture, de sorte que les mêmes choses (principalement) seront placées sur la pile sur chaque Linux basé sur Elfe architecture. Ce qui suit est sur la pile, dans l'ordre que vous le liriez: p> Étant donné que cette structure est la même pour chaque architecture, vous pouvez rechercher une instance sur le dessin de la page 54 de la SYSV 386 ABI Pour obtenir une meilleure idée de la manière dont les choses correspondent ensemble (noter, cependant, que les constantes de type vecteur auxiliaires sur ce document sont différentes de ce que Linux utilise, de sorte que vous devriez donc Regardez les en-têtes Linux pour eux). P> Vous pouvez maintenant voir pourquoi le contenu de start_thread code> à la fin de
load_elf_binary code>
(si vous utilisez un système Linux moderne, il va presque toujours utiliser le format elfe). Pour le bras, les registres semblent être définis comme suit: p> R0 code> -
r2 code> sont indésirables, et vous devriez plutôt tout lire de la pile (vous verrez pourquoi je pense que cela plus tard). Maintenant, regardons ce qui est sur la pile. Ce que vous allez lire à partir de la pile est rempli par
Create_elf_Tables code>
. p>
argc code> dans
principal () code>). li>
argv code> dans
principal () code>;
argv code > voudrait indiquer le premier de ces pointeurs). Li>
principal () code>;
ENVP code> signalerait le premier de ces pointeurs). Li>
at_null code>) dans le premier élément. Ce vecteur auxiliaire a des informations intéressantes et utiles, que vous pouvez voir (si vous utilisez GLIBC) en exécutant tout programme lié à dynamisme avec la variable d'environnement
ld_show_auxv code> définie sur
1 code> (Par exemple
ld_show_auxv = 1 / bin / true code>). C'est aussi là où les choses peuvent varier un peu en fonction de l'architecture. LI>
ul>
R0 code> -
r2 code> sont des ordures. Le premier mot de la pile est
argc code>, le second est un pointeur sur le nom du programme (
argv [0] code>), et le troisième était probablement zéro pour vous parce que vous avez appelé Le programme sans arguments (ce serait
argv [1] code>). Je suppose qu'ils sont configurés de cette façon pour le format binaire plus ancien
A.out code>, comme vous pouvez le voir sur
CREATE_AOUT_TABLE CODE>
METS ArgC code>,
argv code> et
Envple code> dans la pile (donc ils se retrouveraient dans
R0 code> -
r2 code> dans l'ordre prévu pour un appel à
Main () Code >) Je suppose que quelque chose de profond dans la machinerie SysCall l'a écrasé avec la valeur de retour de l'appel système (qui serait zéro depuis que l'exécutif a réussi). Vous pouvez voir dans
kernel_execve Code>
(qui n'utilise pas la machinerie SysCall, car c'est ce que le noyau appelle le noyau lorsqu'il souhaite exécuter en mode noyau) qu'il écrase délibérément R0 code> avec la valeur de retour du
do_execve code>. p> p>