2011-03-04 4 views
10

Je suis plutôt nouveau dans la programmation d'assemblage. J'utilise la plate-forme x86 avec GCC (Linux).Passage d'un pointeur sur une fonction d'assemblage

J'ai une fonction que je veux appeler à partir de C comme:

myfunc (unsigned char * s1, unsigned char * s2, int someint); 

La fonction prendra les emplacements mémoire s1 et s2 et de les comparer, puis incrémenter et comparer, etc., effectuer des traitements comme ça va. C'est un peu comme memcmp mais je fais plus.

Ma question: si je passe un pointeur dans une fonction d'assemblage? Et puis comment je dis "donnez-moi la valeur stockée à cette adresse mémoire"?

Voici ce que j'ai jusqu'à présent:

Pour obtenir la première fonction arg (« s1 ») de la pile, je le fais (someaddress est un entier de 32 bits, et je travaille sur un 32 processeur -bit):

movl 8(%esp), %ecx 
movl %ecx, someaddress 

Si je mets somevar dans %eax (ou %ebx, etc.), puis printf avec %p, je vois que son adresse et l'adresse du pointeur unsigned char « s1 » je suis passé ce sont les mêmes. Mais je suspecte que ce que j'ai réellement fait est pris l'adresse de mémoire, l'a convertie en nombre entier, et puis mets cet entier dans une certaine partie.

Par exemple, si je puis le faire:

movl pos1, %eax 
movl pos2, %ebx 
cmp (%eax),(%ebx) 

je reçois "Erreur: trop de références de mémoire pour` cmp '". Je ne suis pas tout à fait certain de ce que cela signifie, sauf « vous foiré » ;-)

Alors ...

  • Comment passer un pointeur et le garder comme un pointeur? Comment utiliser la valeur dudit pointeur dans l'ensemble?
  • (Par exemple, comme *ptr en C)

Est-ce que je veux regarder l'opérande LEA? J'utilise la "Professional Assembly Programming" de Richard Blum comme guide, mais Blum ne semble pas couvrir ce cas.

Mise à jour

Merci beaucoup pour votre réponse apprise!

Malheureusement, je ne suis toujours pas capable de déréférencer.

Voici un exemple simplifié. La fonction d'assemblage prend un pointeur et devrait lui faire écho.Au lieu de cela, je reçois:

first_ptr points to 81 (should be 81) <-- from C program 
the value is -1543299247 <-- printf called from within assembler 
the value is -6028513 <-- printf called from within assembler 
my function returned -6028513 <-- return value printed from C program 

Programme C:

#include <stdio.h> 
#include <string.h> 

int main (void) { 
     unsigned char first; 
     unsigned char * first_ptr; 

     first = 'Q'; 
     first_ptr = &first; 

     printf ("first_ptr points to %i (should be 81)\n",*first_ptr); 

     printf ("my function returned %i\n", myfunc(first_ptr)); 
     return 0; 
} 

programme de montage:

.section .data 

msg: 
    .asciz "the value is %i\n" 

.section .bss 
.lcomm str, 8 

.section .text 
.type myfunc, @function 
.globl myfunc 
myfunc: 

    # save stack 
    pushl %ebp 
    movl %esp, %ebp 

    # save string arg from stack to "str" 
    movl 8(%esp), %ecx 
    movl %ecx, str 

    # let's try printing the ecx dereference 

    pushl (%ecx) 
    pushl $msg 
    call printf 

    # put the value of str on the stack 
    # and call printf 

    pushl (str) 
    pushl $msg 
    call printf 

    # now return the character at pos1 
    movl (str), %eax 

    # restore the stack 
    movl %ebp, %esp 
    popl %ebp 

    ret 
+0

Problèmes avec votre code mis à jour: Pour imprimer la valeur, vous poussez 32 bits alors que la variable est juste 8 bits. Vous pouvez l'étendre à 32 bits (ce que fait C) ou changer la chaîne de format. Notez que les 8 bits bas de -1543299247 évaluent en fait 81, comme prévu. Pour la deuxième impression et le retour: Vous essayez d'utiliser le double déréférencement en écrivant (str) et il n'y en a pas en x86. L'assembleur devrait lancer une erreur pour cela, si vous me le demandez, mais à la place il laisse tomber les parenthèses. – Jester

Répondre

8

Au moins un des opérandes à cmp doit être un registre. Si vous essayez de comparer le contenu de deux emplacements de mémoire, vous devrez en mettre un dans un registre. Comment l'obtenir dans un registre que vous demandez? Eh bien, vous avez déjà fait cela avec votre exemple de code. Cette ligne:

movl 8(%esp), %ecx 

Prend les 4 octets à% esp + 8 et les place dans% ecx. Dans un pseudo-code C-like:

ecx = *(esp + 8); 

Espérons que cela a du sens. Vous pouvez effectuer des opérations similaires pour retirer vos pointeurs de la pile et les enregistrer, puis les déréférencer, comparer les valeurs déréférencées, etc. Dites moi si vous avez d'autres questions!

Edit - vos cassés sur des questions:

  1. Vous faites déjà, et votre instruction movl 8(%esp), %ecx, ou quelque chose comme ça va faire tout ce que vous avez besoin.

  2. how to use the value of said pointer in assembly? (e.g., like *ptr in C)

    Vous devez utiliser le () à nouveau - pour charger le premier octet hors du pointeur dans %ecx de votre instruction ci-dessus, par exemple:

    movb (%ecx), %edx 
    

    Dans le pseudo-code C comme similaire à comment je l'ai utilisé ci-dessus, cette instruction est:

    edx = *(unsigned char *)ecx; 
    
  3. Do I want to look at the LEA operand?

    Probablement pas, basé sur la description de votre problème que vous avez fourni. C'est toujours possible, cependant. lea fonctionne quelque chose comme l'opérateur & en C.À titre d'exemple, cette instruction:

    lea 12(%ecx), %edx 
    

    peut être traduit dans notre pseudocode comme:

    edx = &(*(ecx + 12)) 
    

    ou plus simplement:

    edx = ecx + 12 
    

    Cet exemple est un peu bête, puisque nous » re en utilisant un mode d'adressage relativement simple, mais que diriez-vous de quelque chose comme ceci:

    lea 1(%edx,%ecx,4), %eax 
    

    qui signifie:

    eax = &(edx[ecx * 4] + 1) 
    

Souvent, la meilleure solution à ce genre de problèmes est d'écrire votre routine en C, puis le compiler et démonter les résultats.

Edit 2:

Votre exemple programme semble presque droite, mais vous essayez de pointeurs de déréférencer en mémoire - obtenir ces pointeurs dans les registres d'abord et vous devriez être ok.

Questions connexes