web-dev-qa-db-fra.com

Assemblage - .data, .code et registres ...?

Alors ce matin, j'ai posté une question confuse sur l'Assemblée et j'ai reçu une aide très sincère, ce que j'apprécie vraiment.

Et maintenant, je commence à entrer dans l'Assemblée et je commence à comprendre comment cela fonctionne.

Les choses que je pense bien comprendre incluent la pile, les interruptions, le binaire/hexadécimal et en général ce que font la plupart des opérations de base (jmp, Push, mov, etc.).

Les concepts que j'ai du mal à comprendre et pour lesquels j'aimerais obtenir de l'aide sont ci-dessous - ce serait d'une grande aide si vous pouviez aborder l'un des éléments suivants:

  1. Que se passe-t-il exactement dans la section .data? Sont ces variables que nous déclarons?
  2. Si tel est le cas, pouvons-nous déclarer des variables plus tard dans la section code? Sinon, pourquoi pas? Si tel est le cas, comment et pourquoi utilisons-nous alors la section des données?
  3. Qu'est-ce qu'un registre? Comment se compare-t-il à une variable? Je veux dire, je sais que c'est un emplacement qui stocke une petite information ... mais cela me semble exactement une variable.
  4. Comment créer un tableau? Je sais que cela semble un peu aléatoire, mais je suis curieux de savoir comment je procéderais pour faire quelque chose comme ça.
  5. Existe-t-il une liste quelque part des pratiques courantes pour lesquelles chaque registre doit être utilisé? Je ne les comprends toujours pas complètement, mais j'ai remarqué que certaines personnes disaient, par exemple, qu'un certain registre devrait être utilisé pour stocker les `` valeurs de retour '' des procédures - existe-t-il une liste complète ou au moins informative de ces pratiques?
  6. L'une des raisons pour lesquelles j'apprends Assembly est de mieux comprendre ce qui se passe derrière mon code de haut niveau. Dans cet esprit - lorsque je programme en C++, je pense souvent à la pile et au tas. Dans l'assemblage, je sais ce qu'est la pile - où est le "tas"?

Quelques informations: j'utilise masm32 avec WinAsm en tant qu'IDE, et je travaille sur Windows 7. J'ai beaucoup d'expérience en programmation dans des langages de plus haut niveau tels que c ++/Java.


edit: Merci pour l'aide à tous, extrêmement informative comme d'habitude! Super truc! Une dernière chose cependant - je me demande quelle est la différence entre le pointeur de pile et le pointeur de base, ou ESP et EBP. Quelqu'un peut-il m'aider?

edit: Je pense que je comprends maintenant ... ESP pointe toujours vers le haut de la pile. Cependant, vous pouvez pointer EBP vers ce que vous voulez. ESP = est géré automatiquement mais vous pouvez faire ce que vous voulez avec EBP. Par exemple:

Push 6
Push 5
Push 4
mov EBP, ESP
Push 3
Push 2

Dans ce scénario, EBP pointe maintenant vers l'adresse contenant 4, mais ESP pointe maintenant vers l'adresse contenant 2.

Dans une application réelle, 6, 5 et 4 auraient pu être des arguments de fonction, tandis que 3 et 2 pourraient être des variables locales au sein de cette fonction.

28
Cam

Essayons de répondre dans l'ordre!

  1. La section de données contient tout ce que vous voulez que le système initialise automatiquement pour vous avant d'appeler le point d'entrée de votre programme. Vous avez raison, normalement les variables globales se retrouvent ici. Les données initialisées à zéro ne sont généralement pas incluses dans le fichier exécutable, car il n'y a aucune raison de le faire - quelques directives vers le chargeur de programme suffisent pour générer cet espace. Une fois que votre programme démarre, les régions ZI et de données sont généralement interchangeables. Wikipedia a beaucoup plus d'informations.

  2. Les variables n'existent pas vraiment lors de la programmation Assembly, du moins pas dans le sens où elles le font lorsque vous écrivez du code C. Tout ce que vous avez, ce sont les décisions que vous avez prises pour organiser votre mémoire. Les variables peuvent être sur la pile, quelque part en mémoire, ou simplement ne vivre que dans des registres.

  3. Les registres sont le stockage de données interne du processeur. Vous ne pouvez, en général, effectuer des opérations que sur des valeurs dans les registres du processeur. Vous pouvez charger et stocker leur contenu vers et à partir de la mémoire, qui est l'opération de base du fonctionnement de votre ordinateur. Voici un exemple rapide. Ce code C:

    int a = 5;
    int b = 6;
    int *d = (int *)0x12345678; // assume 0x12345678 is a valid memory pointer
    *d = a + b;
    

    Peut être traduit en un assemblage (simplifié) du type:

    load  r1, 5
    load  r2, 6
    load  r4, 0x1234568
    add   r3, r1, r2
    store r4, r3
    

    Dans ce cas, vous pouvez considérer les registres comme des variables, mais en général, il n'est pas nécessaire qu'une seule variable reste toujours dans le même registre; selon la complexité de votre routine, cela peut même ne pas être possible. Vous devrez pousser certaines données sur la pile, supprimer d'autres données, etc. Une `` variable '' est cette donnée logique, pas là où elle vit dans la mémoire ou les registres, etc.

  4. Un tableau est juste un bloc de mémoire contigu - pour un tableau local, vous pouvez simplement décrémenter le pointeur de pile de manière appropriée. Pour un tableau global, vous pouvez déclarer ce bloc dans la section de données.

  5. Il existe un tas de conventions sur les registres - vérifiez l'ABI de votre plate-forme ou le document de convention d'appel pour plus de détails sur la façon de les utiliser correctement. La documentation de votre assembleur peut également contenir des informations. Vérifiez le article ABI sur wikipedia .

  6. Votre programme d'assemblage peut effectuer les mêmes appels système que n'importe quel programme C, vous pouvez donc simplement appeler malloc() pour obtenir de la mémoire à partir du tas.

32
Carl Norum

J'aimerais ajouter quelque chose. Les programmes sur un ordinateur sont généralement divisés en trois sections, bien qu'il y en ait d'autres.

Segment de code - .code, .text: http://en.wikipedia.org/wiki/Code_segment

En informatique, un segment de code, également appelé segment de texte ou simplement texte, est une phrase utilisée pour désigner une partie de la mémoire ou d'un fichier objet contenant des instructions exécutables. Il a une taille fixe et est généralement en lecture seule. Si la section de texte n'est pas en lecture seule, l'architecture particulière autorise le code auto-modifiable. Le code en lecture seule est réentrant s'il peut être exécuté par plusieurs processus en même temps. En tant que région mémoire, un segment de code réside dans les parties inférieures de la mémoire ou tout en son bas, afin d'empêcher les débordements de tas et de pile de l'écraser.

Segment de données - .data: http://en.wikipedia.org/wiki/Data_segment

Un segment de données est l'une des sections d'un programme dans un fichier objet ou en mémoire, qui contient les variables globales et les variables statiques qui sont initialisées par le programmeur. Il a une taille fixe, puisque toutes les données de cette section sont définies par le programmeur avant le chargement du programme. Cependant, il n'est pas en lecture seule, car les valeurs des variables peuvent être modifiées lors de l'exécution. Cela contraste avec la section Rodata (données constantes en lecture seule), ainsi que le segment de code (également appelé segment de texte).

BSS: http://en.wikipedia.org/wiki/.bss

En programmation informatique, .bss ou bss (qui signifiait à l'origine Block Started by Symbol) est utilisé par de nombreux compilateurs et éditeurs de liens comme le nom d'une partie du segment de données contenant des variables statiques et des variables globales qui sont remplies uniquement avec des données de valeur zéro initialement (c'est-à-dire lorsque l'exécution commence). Elle est souvent appelée "section bss" ou "segment bss". Le chargeur de programme initialise la mémoire allouée à la section bss lors du chargement du programme.

Les registres sont, comme décrit par d'autres, des fonctions de la CPU pour stocker des données ou une adresse mémoire. Les opérations sont effectuées sur les registres, tels que add eax, ebx et selon le dialecte de l'Assemblée, cela signifie des choses différentes. Dans ce cas, cela se traduit par ajouter le contenu d'ebx à eax et le stocker dans eax (syntaxe NASM). L'équivalent dans GNU AS (AT&T) est: movl $ebx, $eax. Différents dialectes de l'Assemblée ont des règles et des opérateurs différents. Je ne suis pas fan de MASM pour cette raison - il est très différent de NASM, YASM et GNU AS.

Il n'y a pas vraiment d'interaction générale avec C. ABI indique comment cela se produit; par exemple, sur x86 (unix), vous trouverez les arguments d'une méthode poussés sur la pile, alors que dans x86-64 sous Unix, les premiers arguments seront positionnés dans des registres. Les deux ABI s'attendent à ce que le résultat de la fonction soit stocké dans le registre eax/rax.

Voici une routine d'ajout 32 bits qui s'assemble pour Windows et Linux.

_Add
    Push    ebp             ; create stack frame
    mov     ebp, esp
    mov     eax, [ebp+8]    ; grab the first argument
    mov     ecx, [ebp+12]   ; grab the second argument
    add     eax, ecx        ; sum the arguments
    pop     ebp             ; restore the base pointer
    ret

Ici, vous pouvez voir ce que je veux dire. La valeur "return" se trouve dans eax. En revanche, la version x64 ressemblerait à ceci:

_Add
    Push    rbp             ; create stack frame
    mov     rbp, rsp
    mov     eax, edi        ; grab the first argument
    mov     ecx, esi        ; grab the second argument
    add     eax, ecx        ; sum the arguments
    pop     rbp             ; restore the base pointer
    ret

Il existe des documents qui définissent ce genre de chose. Voici l'ABI UNIX x64: http://www.x86-64.org/documentation/abi-0.99.pdf . Je suis sûr que vous pourriez probablement trouver des ABI pour n'importe quel processeur, plate-forme, etc. dont vous avez besoin.

Comment opérez-vous sur un tableau en assemblage? Arithmétique du pointeur. Étant donné une adresse de base à eax, le prochain entier stocké serait à [eax+4] si l'entier a une taille de 4 octets. Vous pouvez créer cet espace en utilisant des appels à malloc/calloc, ou vous appelez l'appel système d'allocation de mémoire, quel qu'il soit sur votre système.

Quel est le "tas"? Selon wikipedia encore une fois, c'est la zone de mémoire réservée à l'allocation dynamique de mémoire. Vous ne le voyez pas dans votre programme d'assemblage tant que vous n'avez pas appelé calloc, malloc ou l'appel système d'allocation de mémoire, mais il est là.

Désolé pour l'essai.

18
user257111