2010-12-12 53 views
13

Je travaille sur un programme d'assemblage pour un microcontrôleur ARM Cortex-M3 (jeu d'instructions Thumb 2), en utilisant GNU as.Quand GAS ELF est-il nécessaire d'utiliser les directives .type, .thumb, .size et .section?

Dans un exemple de code, je trouve des directives comme .size, .section et .type que je comprends être des directives ELF. A titre d'exemple:

.section .text.Reset_Handler 
    .weak  Reset_Handler 
    .type  Reset_Handler, %function 
Reset_Handler: 
    bl  main 
    b  Infinite_Loop  
    .size Reset_Handler, .-Reset_Handler 



La directive .type dit pour définir le type d'un symbole - en général, soit à objet% (ce qui signifie des données?) Ou la fonction%. Je ne sais pas quelle différence cela fait. Ce n'est pas toujours inclus, donc je ne sais pas quand il doit être utilisé.

La directive .thumb_func est également associée. D'après ce que je l'ai lu semble que ce pourrait être équivalent de:

.thumb 
.type Symbol_Name, %function 

Ou est-ce quelque chose de complètement différent?



.size est censé définir la taille associée à un symbole. Quand cela est nécessaire, je n'en ai aucune idée. Est-ce calculé par défaut, mais remplaçable avec cette directive? Si oui, quand voulez-vous remplacer?



.section est plus facile de trouver de la documentation, et je pense avoir une idée juste de ce qu'il fait , mais je suis encore un peu incertain au sujet de l'utilisation. La façon dont je le comprends, il bascule entre différentes sections ELF ( text pour le code, data pour les données inscriptibles, bss pour les données non initialisées, rodata pour les constantes et d'autres), et en définit de nouvelles si nécessaire. Je suppose que vous basculeriez entre ces deux types de code selon que vous définissiez du code, des données, des données non initialisées, etc. Mais pourquoi créer une sous-section pour une fonction, comme dans l'exemple ci-dessus?


Toute aide avec ceci est appréciée. Si vous pouvez trouver des liens vers des tutoriels ou des documents qui expliquent cela plus en détail - de préférence compréhensible pour un novice - je serais très reconnaissant.

Jusqu'à présent, le manuel Using as a été d'une aide précieuse - peut-être que vous pouvez en tirer le meilleur parti, avec plus de connaissances.

+2

J'ai ajouté une prime à la question dans l'espoir d'obtenir des réponses plus élaborées, en particulier en ce qui concerne les directives .type et .size. – Oystein

+0

Cela devrait être divisé en une question par directive. Conseil: comprendre le format ELF, puis faire des exemples minimaux avec et sans chaque directive, compiler et «lire -a» sur eux. –

Répondre

10

J'ai programmé le bras/pouce pendant beaucoup d'années beaucoup d'assembleur et ai eu besoin de très peu des nombreuses directives là-bas.

.thumb_func est assez important comme l'a souligné un autre répondant.

par exemple

 
.globl _start 
_start: 
    b reset 

reset: 

.arm 

.globl one 
one: 
    add r0,r0,#1 
    bx lr 

.thumb 

.globl two 
two: 
    add r0,r0,#2 
    bx lr 

.thumb_func 
.globl three 
three: 
    add r0,r0,#3 
    bx lr 


.word two 
.word three 

.arm ou utilisé pour quelque chose comme .code32 ou 32 .code dit qu'il est ce code bras pas de code de pouce, qui pour votre cortex m3 vous n'aurez pas besoin d'utiliser. De même, utilisé pour être .code 16 ou peut-être que cela fonctionne encore, la même donne le pouce de code suivant pas armer. Si les étiquettes que vous utilisez ne sont pas des étiquettes globales à partir desquelles vous devez vous connecter à partir d'autres fichiers ou indirectement, alors vous n'avez pas besoin du .thumb_func. Mais pour que l'adresse d'une branche à l'une de ces étiquettes globales soit calculée correctement (lsbit est un 1 pour le pouce et 0 pour le bras) vous voulez le marquer comme un pouce ou bras étiquette et le thumb_func fait cela, sinon vous doivent définir que peu avant de ramification en ajoutant plus de code et le marqueur est pas appelable à partir de C.

 

00000000 <_start>: 
    0: eaffffff b 4 <one> 

00000004 <one>: 
    4: e2800001 add r0, r0, #1 
    8: e12fff1e bx lr 

0000000c <two>: 
    c: 3002  adds r0, #2 
    e: 4770  bx lr 

00000010 <three>: 
    10: 3003  adds r0, #3 
    12: 4770  bx lr 
    14: 0000000c andeq r0, r0, ip 
    18: 00000011 andeq r0, r0, r1, lsl r0 

jusqu'à la .thumb l'assembleur est un code de bras comme on le souhaite.

Les deux et trois étiquettes/fonctions sont du code pouce comme souhaité, mais les deux étiquettes ont une adresse paire et trois ont l'adresse impaire correcte.

Les derniers outils de codesurcery ont été utilisés pour assembler, lier et vider l'échantillon ci-dessus. Maintenant, pour le cortex-m3 où tout est pouce (/ thumb2) thumb_func peut ne pas être aussi important, il peut juste travailler avec des commutateurs de ligne de commande (très facile à faire une expérience pour le savoir). C'est une bonne habitude d'avoir cependant au cas où vous vous éloignez d'un processeur de pouce seulement à un noyau normal de bras/pouce.

Les monteurs aiment généralement ajouter toutes ces directives et d'autres façons de faire ressembler les choses/se sentir plus comme un langage de haut niveau. Je dis juste que vous ne devez pas les utiliser, j'ai changé les assembleurs pour le bras et utiliser beaucoup d'assembleurs différents pour beaucoup de processeurs différents et préférer l'approche moins, ce qui signifie se concentrer sur l'assemblage lui-même et utiliser le moins d'outils possible. Cependant, je suis généralement l'exception, pas la règle, donc vous pouvez probablement comprendre les directives les plus souvent utilisées en regardant quelles directives la sortie du compilateur génère (et vérifier avec la documentation).

 
unsigned int one (unsigned int x) 
{ 
    return(x+1); 
} 


    .arch armv5te 
    .fpu softvfp 
    .eabi_attribute 20, 1 
    .eabi_attribute 21, 1 
    .eabi_attribute 23, 3 
    .eabi_attribute 24, 1 
    .eabi_attribute 25, 1 
    .eabi_attribute 26, 2 
    .eabi_attribute 30, 2 
    .eabi_attribute 18, 4 
    .file "bob.c" 
    .text 
    .align 2 
    .global one 
    .type one, %function 
one: 
    .fnstart 
.LFB0: 
    @ args = 0, pretend = 0, frame = 0 
    @ frame_needed = 0, uses_anonymous_args = 0 
    @ link register save eliminated. 
    add r0, r0, #1 
    bx lr 
    .fnend 
    .size one, .-one 
    .ident "GCC: (Sourcery G++ Lite 2010.09-50) 4.5.1" 
    .section .note.GNU-stack,"",%progbits 

J'utilise le .ALIGN lors du mélange bras et assembleur pouce ou données avec l'assembleur, vous attendez l'assembleur pour une telle plate-forme de savoir quelque chose d'aussi évident que les instructions du pouce sont sur les limites demi-mots et des instructions de bras sont aligné sur les limites de mots. Les outils ne sont pas toujours aussi intelligents. saupoudrer .aligns à propos de ne pas blesser

.text est la valeur par défaut donc c'est un peu redondant, mais pas blessé. .text et .data sont des attributs standard (non spécifiques à arm) si vous compilez pour une combinaison de rom et ram sur votre cible, cela peut vous intéresser (cela dépend de ce que vous faites avec votre script de linker), sinon .text fonctionnera pour tout .

.size apparemment la taille de la fonction commence à cette directive. L'assembleur ne peut pas le comprendre par lui-même, donc si la taille de cette fonction est importante pour votre code, script d'éditeur de liens, débogueur, loader, quoi que ce soit alors cela doit être correct, sinon vous n'avez pas à vous embêter. Une fonction est un concept de haut niveau de toute façon l'assembleur n'a pas vraiment de fonctions bien moins besoin de déclarer leur taille. Et le compilateur C ne s'en soucie certainement pas, il cherche seulement une étiquette à ramifier et dans le cas de la famille des bras, c'est le code pouce ou le code de bras qui est en train d'être ramifié.

vous pouvez trouver la directive .pool (il existe un équivalent plus récent) utile si vous êtes paresseux avec vos immédiates (ldr rx, = 0x12345678) sur de longues périodes de code. Là encore, les outils ne sont pas toujours assez intelligents pour placer ces données après une branche inconditionnelle, vous leur dites parfois. Je dis la moitié paresseux sérieusement, il est douloureux de faire l'étiquette: .word chose tout le temps et je crois que les deux outils de bras et gcc ont permis pour ce raccourci, donc je l'utilise autant que quelqu'un d'autre. Notez également que llvm génère un autre attribut .eabi_attribute ou deux qui est supporté par la version/mods de la sourcery du code vers binutils mais qui n'est pas supporté (peut-être encore) par binutils gnu. Deux solutions qui fonctionnent, modifient la fonction d'impression asm de llvm pour ne pas écrire les attributs eabi_attributes ou au moins les écrire avec un commentaire (@), ou obtenir les sources/mods binutils du code source et construire binutils de cette façon. La source de code tend à diriger gnu (support de la commande thumb2 par exemple) ou peut-être backports de nouvelles fonctionnalités, donc je suppose que ces attrubutes de llvm seront présentes dans la ligne principale binutils avant longtemps. Je n'ai subi aucun effet néfaste en coupant les attributs eabi_attributes du code compilé llvm.

Voici la sortie llvm pour la même fonction ci-dessus, apparemment c'est le llc que j'ai modifié pour commenter les attributs eabi_attributes.

 
    .syntax unified 
@ .eabi_attribute 20, 1 
@ .eabi_attribute 21, 1 
@ .eabi_attribute 23, 3 
@ .eabi_attribute 24, 1 
@ .eabi_attribute 25, 1 
@ .eabi_attribute 44, 1 
    .file "bob.bc" 
    .text 
    .globl one 
    .align 2 
    .type one,%function 
one:         @ @one 
@ BB#0:         @ %entry 
    add r0, r0, #1 
    bx lr 
.Ltmp0: 
    .size one, .Ltmp0-one 

Le format de fichier elfe est bien documenté et très facile à analyser si vous voulez vraiment voir ce que les directives spécifiques elf (le cas échéant) sont en train de faire. Beaucoup de ces directives doivent aider l'éditeur de liens plus que tout. .thumb_func, .text, .data par exemple.

+0

Merci, cela a certainement aidé. Donc, ni .size ni .type ne devrait être nécessaire à moins que nous ne parlions d'un cas particulier? – Oystein

+0

Je n'ai jamais utilisé .size ou .type et la plupart de mon travail est sur un bras et l'assembleur est toujours impliqué. Je suppose que si votre script de linker ou loader/elf parser veut chercher de telles choses alors vous en avez besoin ici, j'utilise des scripts de linker très simples qui ne s'intéressent vraiment qu'à .text vs .data, essentiellement rom vs ram. –

+0

A propos de la directive .Size: Dans l'arbre git android pour bionique, engager fb723c87490b76d1d2fe521886f7cb6c96ed40b7 dit: 'mettre à jour les ARM syscalls avec le BEGIN (x) et END (x) macros pour donner des informations de taille pour le code de la syscall. Utile pour valgrind. –

5

Les sections de votre programme sont étroitement liées au format ELF dans lequel la plupart des systèmes (Linux, BSD, ...) stockent leurs objets et fichiers exécutables. This article devrait vous donner un bon aperçu du fonctionnement d'ELF, qui vous aidera à comprendre le pourquoi des sections. Simplement, les sections vous permettent d'organiser votre programme dans différentes zones de mémoire qui ont des propriétés différentes, y compris l'adresse, l'autorisation d'exécution et d'écriture, etc. Au cours de la dernière étape de liaison, l'éditeur de liens utilise généralement linker script du même nom ensemble (par exemple tout le code de toutes les unités de compilation ensemble, ...) et leur assigne une adresse finale en mémoire.

Pour les systèmes embarqués, leur utilisation est particulièrement évidente: d'abord, le code de démarrage (généralement contenu dans la section .text) doit être chargé à une adresse fixe pour pouvoir être exécuté. Ensuite, les données en lecture seule peuvent être regroupées dans une section dédiée en lecture seule qui sera mappée dans la zone ROM du périphérique.Dernier exemple: les systèmes d'exploitation ont des fonctions d'initialisation qui ne sont appelées qu'une seule fois et ne sont jamais utilisées par la suite, gaspillant de l'espace mémoire précieux. Si toutes ces fonctions d'initialisation sont regroupées dans une section de dédicace appelée, par exemple, .initcode, et si cette section est définie comme étant la dernière section du programme, le système d'exploitation peut facilement récupérer cette mémoire une fois l'initialisation terminée en abaissant la partie supérieure. limite de sa propre mémoire. Linux, par exemple, est connu pour utiliser cette astuce, et GCC vous permet de placer une variable ou une méthode dans une section spécifique avec postfixant par __attribute__ ((section ("MYSECTION")))

.type et .size sont en fait encore assez clair pour moi aussi. Je les vois comme des aides pour l'éditeur de liens et je ne les ai jamais vus en dehors du code généré par l'assembleur.

.thumb_func semble être seulement nécessaire pour l'ancienne interface OABI afin de permettre l'interfonctionnement avec le code Arm. Sauf si vous utilisez une ancienne chaîne d'outils, vous n'avez probablement pas à vous en préoccuper.

+0

Cela éclaircit la partie section au moins, je suppose. Y a-t-il une raison particulière de définir vos propres sections? – Oystein

+5

.thumb_func est nécessaire si vous prenez une adresse de la fonction. Pour une fonction Thumb, son adresse doit avoir un bit 0 défini (contrairement à une adresse d'élément de données). Cette directive permet à l'assembleur de marquer le symbole en tant que tel, et donc l'éditeur de liens sait quand mettre le bit 0. –

+0

@Igor: Awesome! Merci – Oystein

5

Je suis tombé sur ceci en essayant de comprendre pourquoi l'interfonctionnement d'ARM et de pouce a cassé avec les binutils récents (vérifiés avec 2.21.53 (MacPorts), également 2.22 (Yagarto 4.7.1)). D'après mon expérience, .thumb_func a fonctionné correctement avec les binutils antérieurs pour générer les facettes d'interfonctionnement correctes. Cependant, avec les versions plus récentes, la directive .type *name*, %function est nécessaire pour assurer une génération de placage appropriée.

binutils mailing list post

Je suis trop paresseux pour déterrer une ancienne version de binutils pour vérifier si la directive .type est suffisante à la place de .thumb_func pour binutils plus tôt.Je suppose qu'il n'y a aucun mal à inclure les deux directives dans votre code.

Modifié: commentaires mis à jour sur l'utilisation .thumb_func dans le code, apparemment cela fonctionne pour ARM-> Pouce interfonctionnement pour marquer la routine de pouce pour générer des placages, mais Thumb-> ARM interfonctionnement échoue à moins que la directive .type est utilisé pour marquer l'ARM fonction.