Vous êtes sur la page 1sur 24

1

De nos jours, le dveloppement d'un programme complet en assembleur est assez


rare et concerne des applications trs particulires (ressources matrielles limites,
code critique ultrarapide, etc.). Nanmoins, il ne faut jamais oublier qu'un code
source dvelopp dans un langage de haut niveau sera toujours compil et pourra
tourner sur un processeur qu'avec des instructions machines dpendant
directement de celuici (cd des instructions appartenant au jeu d'instructions du
processeur). Typiquement, si la traduction d'un code source vers un code machine
peut s'effectuer sans problme, il en va tout autrement pour une traduction inverse
(par exemple d'un code binaire vers le code C original). En revanche, la traduction de
code binaire vers un code assembleur est beaucoup plus aise. C'est pourquoi, le
dsassemblage d'un code binaire permettra au dveloppeur de rcuprer un code
en langage clair et fournira des informations prcieuses pour autant que l'on
comprenne le langage assembleur.
Ce cours de programmation assembleur donne les fondements de ce type de
programmation afin que le dveloppeur puisse profiter au maximum de ce langage
pour exploiter au mieux les ressources du processeur et faciliter la mise au point des
logiciels lorsque cela s'avre ncessaire. De plus, il est vident que les hackers vont
exploiter les failles des programmes (voire du matriel) en utilisant des techniques
de trs bas niveau qui reposent directement sur une comprhension avance du
systme (structures de bas niveau lies au binaire, manipulations de la pile,
corruption au niveau du processeur, etc.).

Parmi les utilisations les plus frquentes de l'assembleur, on peut citer les contextes
d'utilisation suivants:
L'amorage d'un systme: lorsque l'on met un systme sous tension, le
processeur commence gnralement excuter des instructions particulires de
configuration/initialisation qui ne correspondent pas une instruction d'un
langage de haut niveau. C'est pourquoi, les premires instructions d'un code de
dmarrage (d'amorage ou bootstrap) sont crites en assembleur.
La mise au point d'un programme (debugging) peut ncessiter l'utilisation d'un
debugger connect l'application pendant que celleci s'excute. Il est possible
alors de rcuprer le code dsassembl en tempsrel et d'obtenir de prcieuses
informations sur le comportement du programme (il se peut que le debugger ne
puisse pas toujours associer le code source original crit dans un langage de haut
niveau avec l'instruction en cours d'excution ( mditer!).
Dans certain cas, l'acclration d'un traitement peut ncessiter l'utilisation de
l'assembleur, en particulier si le processeur dispose d'instructions performantes
qui ne sont pas utilises par le compilateur (code DSP, coprocesseur arithmtique,
etc.).
D'une manire gnrale, ds que l'on cherche utiliser des instructions
particulires du processeur, il faut utiliser du code assembleur. Comme nous le
verrons dans ce cours, il est aussi possible de mixer du code C avec du code
assembleur par exemple.
Finalement, la comprhension de l'assembleur permet d'analyser du code binaire.

L'apprentissage de l'assembleur ncessite une bonne comprhension de


l'architecture matrielle. Nous nous intressons ici aux caractristiques matrielles
pouvant influencer le modle de programmation.
Il existe deux familles principales de processeurs: les processeurs bases sur une
architecture RISC (Reduced InstructionSet Computer) qui possdent un jeu
d'instruction rduit, et ceux bass sur une architecture CISC (Complex InstructionSet
Computer). La plupart des microcontrleurs possdent une architecture RISC.
Les processeurs RISC disposent de beaucoup de registres gnraux (entre 16 et 32),
tous quivalents, pour faciliter leur allocation par le compilateur. Les instructions
sont de taille fixe, souvent 32 bits. Les instructions arithmtiques ont gnralement
2 registres servant d'oprandes et un registre de sortie. Enfin, les accs la mmoire
font l'objet d'instructions spcifiques, et une valeur correspondant une adresse
doit d'abord tre charge dans un registre pour tre utilise: on parle d'architecture
loadstore ou d'instructions registerregister.
Les processeurs CISC ont quant eux de trs nombreuses instructions combines
avec des modes d'adressage relativement complexes. Les instructions peuvent tre
de taille diffrente et sont parfois difficiles utiliser par un compilateur. Elles
peuvent ncessiter plusieurs coups d'horloge; le processeur est globalement plus
complexe. Le jeu d'instruction peut tre modifier par microprogrammation.
L'architecture CISC est prsente dans la plupart des processeurs de type Intel/AMD.

10

La diffrence entre un microprocesseur et un microcontrleur rside au niveau du


contenu du composant lectronique (chipset).
Dans le cas du microprocesseur, le chipset contient les curs de calcul (core), cd
l'unit de traitement et de calcul bas sur un certain jeu d'instructions, les
mmoires de type cache, les circuits permettant la gestion du pipeline, etc. Le
chipset peut galement contenir divers coprocesseurs utiliss pour le traitement
arithmtique (oprations en virgule flottante), l'acclration de calcul, les oprations
multimdia (acclration audio/vido), le chiffrement, etc.
On y trouve bien entendu un ensemble de registres programmables, dont
notamment un registre d'tat.
Un microprocesseur se destine en principe un usage gnrale et constitue le
composant central de tout ordinateur de type PC/laptop/serveur. Il est galement
souvent rfr en tant que CPU (Control Process Unit) bien que ce terme devrait
plutt tre li au cur de calcul intgr l'intrieur du chipset. Bien entendu, le
microprocesseur comporte des bus d'adresses et de donnes exports vers
l'extrieur afin de pouvoir y connecter des priphriques (typiquement un
contrleur de bus PCI). De plus, certaines lignes particulires (ports d'entressorties
I/O, lignes d'interruption) permettent de relier des composants plus critiques
(horloges, contrleur d'interruption, contrleur mmoire, etc.) offrant ainsi une
meilleure scurisation des accs.

11

Un exemple d'architecture de systme bas sur un processeur de type x86 est


prsent cidessus. On retrouve le chipset reprsentant le microprocesseur reli aux
autres priphriques via les bus d'adresses/donnes. Aujourd'hui, la plupart des
contrleurs de priphriques peuvent facilement s'enficher dans des slots PCI, eux
mmes disponibles sur la cartemre du systme sur laquelle se trouve le
processeur.
Les priphriques (clavier, souris, cran, dispositifs USB, etc.) ncessitent un
contrleur de priphrique afin d'interagir correctement avec le processeur. Le
contrleur est constitu d'un (ou plusieurs) circuit lectronique, gnralement
rgul d'une manire indpendante au processeur et disposant de leur propre cycle
de vie. C'est pourquoi, un ensemble processeurpriphriques fait intervenir
beaucoup d'interactions de type asynchrones qui sont pris en charge par le
processeur au travers de lignes d'interruptions (IRQs). Le mcanisme d'interruption
luimme ncessite un contrleur d'interruption permettant une gestion des
priorits et des activations/dsactivations (masquage/dmasquage) de cellesci. Le
contrleur d'interruption (ou APIC pour Advanced Programmable Interrupt
Controller) est reli au processeur via des lignes ddies.

12

A la diffrence d'un microprocesseur, un microcontrleur contient le processeur


(curs de calcul ainsi que coprocesseurs, mmoires cache, etc.), plus des
contrleurs de priphriques directement intgrs dans le chipset, ainsi que de la
mmoire ROM/RAM et des entressorties permettant de relier les composants
externes vers les contrleurs internes.
L'architecture du processeur et donc beaucoup plus "simple" que celui d'un
microprocesseur et la consommation de courant est rduite. Ce type de circuit se
retrouve majoritairement dans les systmes embarqus, mais tend aujourd'hui se
rpandre galement dans les systmes qui jusqu'ici ne comprenant que des
microprocesseurs. C'est le cas par exemple des dernires gnration de
microcontrleur de type ARMCortex A15, multicoeur et supportant les instructions
de virtualisation.
Les microcontrleurs sont ds lors plus spcifiques que les microprocesseurs. De
part leur architecture interne et les contrleurs de priphriques qu'ils intgrent,
certains se destineront aux applications multimdia (smartphones, tablettes PC,
etc.), alors que d'autres conviendront mieux pour des systmes tempsrels critiques
(navigations, machinesoutils, etc.) , ou encore aux systmes de tlcommunication
(routeurs, gateways, etc.) ou aux systmes de traitement rapide de donnes
(encryptage, monitoring, etc.).
C'est dire que la gamme de microcontrleurs est infiniment plus riche que celle des
microprocesseurs.

13

Un exemple de systme embarqu quip d'un microcontrleur est la plateforme


REPTAR (Reconfigurable Embedded Platform for Training And Research) dveloppe
au sein de l'institut REDS et employ dans de nombreux laboratoires de Bachelor et
Master.
La plateforme est btie autour d'un microcontrleur de type ARM CortexA8 de
Texas Instruments (TI); il s'agit du microcontrleur DM3730. Le microcontrleur est
mont sur une carte de dveloppement disponible sur le march (carte Variscite); ce
petit module contient de la RAM ainsi qu'une connexion Ethernet et un module Wifi.
Comme la plupart des microcontrleurs, le DM3730 offre la possibilit de relier des
priphriques externes au travers de GPIO (General Purpose Input/Output)
permettant l'envoi/rception de donnes ou d'utiliser une ligne comme canal
d'interruption. Ces lignes sont relies en interne dans le microcontrleur grce
un contrleur spcifique appel contrleur GPIO.
L'accs aux diffrents composants, et notamment aux contrleurs de priphriques,
s'effectue gnralement au travers d'accs mmoire (lecture/criture) des
adresses I/O bien dfinies (contrairement au microprocesseur qui peut lui piloter
des composants externes via des ports I/O ddis qui ncessitent des instructions
d'accs rserves).
Les adresses I/Os sont dtermins grce au plan d'adressage dict donc fig par
le microcontrleur/microprocesseur.

14

Le plan d'adressage d'un microprocesseur/microcontrleur permet de dterminer


les adresses physiques qui permettent d'accder tout type de mmoire (mmoire
ROM/RAM, mmoire I/O, registres, mmoires externes, etc.).
Il faut noter que le plan d'adressage est gnralement linaire et peut tre contigu
ou discontigu (il existe des plages d'adresses invalides qui ne sont pas utilises). De
plus, certaines plages d'adresses peuvent tre dynamiquement reconfigures par le
processeur et correspondre des priphriques diffrents (notamment au
dmarrage o certaines adresses peuvent rfrencer une ROM, puis changer de
mappage pour rfrencer une RAM ou une mmoire de stockage secondaire comme
de la mmoire flash).
En revanche, une adresse ne peut rfrencer plusieurs octets la fois. Chaque
adresse rfrence un et un seul octet (byte) de la mmoire; on dit que la mmoire
est byteadressable.
La capacit d'adressage d'un systme est directement li la taille des registres du
processeur; en effet, lors d'un transfert mmoire (mme que d'un seul byte), le
processeur utilise les registres pour stocker l'adresse intervenant dans l'accs
mmoire. C'est pourquoi la taille d'une adresse ne peut dpasser la taille d'un
registre: une architecture 32 bits comporte des registres 32 bits, cd qu'une
adresse peut tre code sur 32 bits au maximum, et peut donc aller de 0x0
0xffff'ffff (2321), ce qui correspond une zone de 4 Go.

15

Un aspect important des processeurs qui aura un impact sur le dveloppement d'un
programme en assembleur concerne la paralllisation en tage de plusieurs cycles
de traitement; chaque cycle est associ un tage. En effet, pour chaque instruction
d'un programme, le processeur doit:
1) rcuprer l'instruction en mmoire (fetch). Pour cela, il doit accder
l'instruction stocke une adresse place dans un registre de type PC
(Program Counter)
2) dcoder l'instruction partir de la valeur stocke en mmoire.
3) excuter l'instruction
4) effectuer divers traitements internes (rcriture dans un registre, mise
jour de la mmoire, etc.)
A chaque coup d'horloge, chaque tage excute une tape du cycle en parallle; il
s'agit de la technique de pipeline.
Les mcanismes de pipeline ont un impact non ngligeable sur le dveloppement
d'un code assembleur. En particulier, l'utilisation du registre PC ncessite une
attention particulire. Il faut comprendre que sa valeur durant l'excution d'une
instruction a t incrmente plusieurs fois (2 fois selon l'exemple cidessus) depuis
l'opration fetch, autrement dit le PC est en avance de 8 bytes lors de l'excution de
l'instruction, si les instructions sont codes sur 32 bits.
L'utilisation d'une instruction de type nop (no operation) peut s'avrer utile pour
vider le pipeline et anticiper une mauvaise lecture d'instruction (fin de code par
exemple).

16

Le langage C comprend de nombreux types de nombres entiers, occupant plus ou


moins de bits. La taille des types n'est que partiellement standardise: le standard
fixe uniquement une taille minimale et une magnitude minimale. Les magnitudes
minimales sont compatibles avec d'autres reprsentations binaires que le
complment deux, bien que cette reprsentation soit presque toujours utilise en
pratique. Cette souplesse permet au langage d'tre efficacement adapt des
processeurs trs varis, mais elle complique la portabilit des programmes crits en
C.
Chaque type entier a une forme "signe" (signed) pouvant reprsenter des nombres
ngatifs et positifs, et une forme "non signe" (unsigned) ne pouvant reprsenter
que des nombres naturels. Le type char, gnralement utilis pour reprsenter un
caractre, est un type entier comme les autres, si ce n'est que, selon
l'implmentation, il quivaut signed char ou unsigned char.
Le standard impose les relations suivantes:
sizeof(char) sizeof(short) sizeof(int) sizeof(long) sizeof(long long)
8
16
16
32
32

(source: Wikipedia, 2012)

17

Lareprsentationd'unevariableenmmoireestillustresurleschmacidessous.
Onsupposequelavariablevar estplantel'adresse0x00000010 etlavariable
pvar estplantel'adresse0x00001000.

RAM
.

0x00001000 10

00

00

00

0x00000010 05

03

04

02

0x00000004
0x00000000

18

Le code cidessus montre l'utilisation des oprateurs "&" et "*" pour accder
l'adresse d'une variable et modifier son contenu partir de son adresse en
mmoire.
L'utilisation du symbole "*" dans la partie dclarative sert dclarer un pointeur; il
s'agit d'une variable qui pourra contenir une adresse mmoire. A noter que le mot
cl null est utilis en C pour une adresse gale 0 (#define null (void *) 0).
Il ne faut pas oublier que la dclaration d'un pointeur (comme toute autre variable)
ne suffit pas initialiser la variable et que celleci peut contenir priori n'importe
quelle valeur.
Le symbole "*" utilis dans le corps d'une fonction permet d'accder au contenu se
trouvant l'adresse stocke dans la variable (pointeur). On parle aussi de
drfrencement. C'est ce moment que le type du pointeur intervient afin que le
compilateur sache quel quantit de donnes doit tre transfre (1, 2, ou 4 bytes
typiquement).
Le symbole "&" permettra de rcuprer l'adresse d'une variable. On notera qu'un
pointeur est trait en C comme tout autre variable, savoir qu'il est bien sr
possible de rcuprer l'adresse d'une variable contenant dj une adresse! (adresse
d'un pointeur).

19

Le compilateur C cherchera en principe optimiser le code et fera tout pour


minimiser les accs mmoire; cela se traduit par l'utilisation de variable en mmoire
cache ou encore en modifiant l'ordre d'excution des instructions.
Si une variable ou un emplacement mmoire est sujette tre modifi entre deux
accs, l'utilisation du motcl volatile dans la dclaration de la variable ou du
pointeur indiquera au compilateur d'effectuer inconditionnellement l'accs
mmoire, et de ne pas avoir recours une mmoire cache des fins d'optimisation.
Typiquement, les pointeurs associs des adresses I/O doivent tre dclars avec
volatile afin d'viter des mauvaises surprises lors de l'excution.

20

L'utilisation de termes prdfinis avec la clause #define est vivement encourage


lorsque l'on doit accder la mmoire des adresses fixes. C'est le cas typiquement
pour les registres de priphriques.
Une bonne pratique consiste dfinir l'adresse l'aide des #define mais de procder
au drfrencement l'aide d'une variable de type pointeur. Si l'on souhaite
toutefois effectuer directement le drfrencement dans le #define, il est important
de bien nommer les constantes, savoir de faire la diffrence entre adresse et
valeur.
Par ailleurs, il ne faut pas oublier non plus de caster les valeurs numriques en
adresse, cd en (unsigned int *) par exemple. Par dfaut, le compilateur considre
toute valeur entire comme des valeurs de type int.

21

Dans le langage C, il est possible de manipuler les valeurs au bit prs grce aux
oprateurs arithmtiques et logiques. Ces oprateurs possdent gnralement une
quivalence directe avec des instructions en assembleur ralisant les mmes
oprations.
Il s'agit notamment du ET logique (&), OU logique (|), XOR logique (^), et NOT (~).
On rajoutera galement les oprateurs de dcalages binaires vers la droite (>>) et
vers la gauche (<<).
Tous ces oprateurs agissent sur des valeurs correspondant aux types de base
(scalaires) du langage C.

22

23

24