Suivez-nous sur X

|
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
ALL
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
Programmation : Assembleur - les fichiers exécutables
(Article écrit par Frédéric Delacroix et extrait d'Amiga News - avril 1996)
|
|
Aujourd'hui nous nous plongeons dans certains méandres internes à AmigaDOS : le format des fichiers exécutables. Cet article
va tenter d'expliquer, pour les programmeurs d'éditeurs de liens et les autres, comment AmigaDOS stocke les programmes sur disque.
Introduction
C'est une question qui m'est fréquemment posée et qui déroute souvent le débutant en programmation sur Amiga : comment un
programme utilisant des adresses absolues peut-il être chargé et exécuté à une adresse quelconque ? Par exemple, dans
l'instruction «"MOVE d0,_DOSBase», _DOSBase ici est une adresse absolue au moment de l'exécution du programme. Si on voulait
que le programme puisse être chargé à une adresse quelconque sans traitement particulier, il faudrait une séquence d'instructions
du type :
lea _DOSBase(pc),a0
move.l d0,(a0)
|
Ce qui, vous en conviendrez, est beaucoup plus lourd. Pour contourner ce problème, AmigaDOS stocke sur disque, dans le fichier
programme, plus que les données du programme lui-même. On y trouve également des informations de relogement, ainsi que d'autres
choses comme des sections de données.
Toutes ces informations sont décodées lors du chargement par la fonction LoadSeg() de la dos.library : de la mémoire est allouée
pour toutes les différentes sections du programme (définies par la directive SECTION de Devpac) et se charge de tous les
travaux nécessaires comme le relogement par le biais des tables stockées dans le fichier.
C'est pourquoi, pour charger un programme, il ne faut pas utiliser Open(), Read() et consorts mais obligatoirement LoadSeg()
ou NewLoadSeg(). Dans le cas contraire, on accède aux données "brutes" du fichier programme. C'est aussi pourquoi le code
d'un secteur de démarrage (bootblock) ne doit jamais contenir d'adresses absolues : le DOS n'étant pas initialisé au moment
de sa lecture, aucun relogement ne peut être effectué. Il faut donc utiliser la forme "lourde" ci-dessus étant donné qu'Exec
ne charge pas (ne peut pas charger !) ce code à une adresse absolue.
C'est également là le noeud de la distinction que Devpac (par exemple) fait entre les expressions absolues et les expressions
relatives : une expression relative fera l'objet d'un relogement en fonction de l'adresse de chargement du programme et donc
de l'entrée d'une donnée correspondante dans les tables de relogement, tandis qu'une expression absolue est la même quelle
que soit l'adresse de chargement du programme.
Les "hunks" (segments)
Écartons dans cet article la structure des fichiers objets et bibliothèques liables, que nous étudierons éventuellement dans
un prochain numéro, si l'abondance du courrier me convainc de me plonger dans cet épineux problème. Nous ne nous intéresserons
ici qu'aux fichiers exécutables, mot d'ailleurs impropre puisque LoadSeg() charge aussi des fichiers qui ne sont pas des
exécutables au sens du Shell : polices, keymaps... Excluons aussi - pour l'instant - le format des overlays, c'est
suffisamment compliqué comme ça.
La division du source (assembleur) en différentes SECTIONS correspond, dans le fichier programme produit, à différents segments.
Un segment est ainsi la division primaire du programme. Chaque segment, lors du chargement, est placé à une adresse quelconque en
mémoire et le relogement est effectué si nécessaire pour assurer la cohérence des données. Une fois le fichier chargé en mémoire,
on parle plutôt de "segments".
Mais voyons d'abord l'en-tête indispensable d'un fichier exécutable. Il s'agit du bloc HUNK_HEADER, qui contient notamment
le nombre et la taille des différents segments. Précisons qu'ici les décalages sont toujours donnés en mots longs, étant donné que
tout ce format est orienté mot long, sans doute en raison de son origine BCPLienne.
décalage contenu
0 constante HUNK_HEADER ($000003F3)
1 constante 0 ($00000000 :-)
2 nombre de segments (n)
3 numéro du premier segment (normalement 0)
4 numéro du dernier segment
5 n mots longs représentant la taille (en mots longs) des
différents segments à allouer, ainsi que le type de la
mémoire dans les bits 30 et 31.
|
Ainsi, à la lecture de ce bloc, la fonction LoadSeg() est en mesure d'allouer n blocs de mémoire dont les tailles sont indiquées.
L'avantage de mesurer la taille de ces blocs en mots longs est que cela permet de stocker dans les bits 30 et 31 le type de
mémoire à allouer. 00 indique de la mémoire publique quelconque, 01 de la mémoire Chip et 10 de la mémoire Fast. On retrouve
là un autre paramètre de la directive SECTION de Devpac.
La combinaison 11 est reconnue depuis le Kickstart 2.0 et indique que le type réel (le paramètre passé à AllocVec()) de la
mémoire se trouve dans le mot long suivant (qui n'est bien sûr pas comptabilisé dans "n"). Cette possibilité, qui permet
de réclamer de la mémoire MEMF_KICK, par exemple, n'est pas (encore) utilisée par nos assembleurs, compilateurs ou éditeurs de
liens.
Sous Kickstart 1.3 et antérieurs, on peut également trouver des spécifications de bibliothèques résidentes, mais celles-ci
sont obsolètes depuis (LoadSeg() échoue s'il en trouve !), c'est pourquoi je n'en parlerai pas ici.
Précisons également que les tailles de segments données dans le bloc HUNK_HEADER peuvent ne pas correspondre à celles des segments :
elles ne spécifient que la taille du bloc à allouer, pas des données à y mettre. On peut utiliser une taille dans HUNK_HEADER
plus grande que celle du code ou des données correspondantes pour "simuler" des zones de type BSS dans la même section que du
code ou des données (ce que fait le SAS/C d'ailleurs). Ensuite, on trouve, juxtaposées, les données des différents segments. Un
segment est lui-même formaté en différents blocs, chacun d'un type et d'une fonction particuliers.
Les types de blocs
Un bloc commence toujours par son identificateur sous forme de mot long. Voyons les types permis dans les segments des exécutables
non overlays.
Les blocs HUNK_NAME, HUNK_SYMBOL et HUNK_DEBUG sont purement et simplement ignorés par LoadSeg(). Les débogueurs comme MonAm
tirent cependant parti des deux derniers pour afficher respectivement des noms de symboles à la place de noms et des numéros
de ligne de source. HUNK_NAME et HUNK_DEBUG ont le même format :
décalage contenu
0 HUNK_NAME ($3E8) ou HUNK_DEBUG ($3F1)
1 n
2 n mots longs indiquant le nom du segment (autre paramètre
de la directive SECTION) pour HUNK_NAME, ou des
données spécifiques pour un débogueur pour HUNK_DEBUG.
|
Le format de HUNK_SYMBOL est calqué sur celui de HUNK_EXT qui n'apparaît que dans les fichiers objet et liste les XDEF et
XREF :
décalage contenu
0 HUNK_SYMBOL ($3F0)
{1 nombre 24 bits "n" (nombre non nul)
(0 dans les bits 24 à 31)
2 n mots longs contenant le nom du symbole
2+n} valeur du symbole
? 0
|
La partie entre accolades peut être répétée un nombre quelconque de fois, listant ainsi tous les symboles de la section. La valeur
mentionnée dans le dernier champ représente en réalité un décalage par rapport à l'adresse de chargement des données. La véritable
valeur du symbole est donc obtenue en ajoutant au contenu de ce champ l'adresse de chargement du segment.
Les blocs HUNK_CODE et HUNK_DATA contiennent les données effectives du programme, et ont le format très simple suivant :
décalage contenu
0 HUNK_CODE ($3E9) ou HUNK_DATA ($3EA)
1 n
2 n mots longs de code (pour HUNK_CODE)
ou de données (HUNK_DATA)
|
On y trouve les données et le programme générés par l'assembleur ou le compilateur, mais les adresses absolues (comme le
_DOSBase du début de l'article dans l'opérande de l'instruction MOVE elle-même) sont remplacées par un simple décalage par
rapport au début du segment auquel cette adresse fait référence. Concrètement, si votre code est du type...
section programme,CODE
...
move.l d0,_DOSBase
...
section données,DATA
_ExecBase dc.l 0
_DOSBase dc.l 0
|
...alors, au moment de coder l'opérande destination de l'instruction MOVE, c'est-à-dire l'adresse absolue _DOSBase, l'assembleur
y met 4, c'est-à-dire le décalage de _DOSBase par rapport au début de sa section. Il génère de plus une entrée dans la table de
relogement du premier segment indiquant qu'il faut ajouter au contenu de cette adresse, l'adresse de chargement du segment numéro 1.
Ainsi, après LoadSeg(), l'adresse effective du MOVE est correcte, elle a été calculée au moment du chargement. Magique... Le
bloc HUNK_BSS indique, lui, un bloc de données non initialisées :
décalage contenu
0 HUNK_BSS ($3EB) avec attributs de mémoire
1 nombre de mots longs à allouer
|
Ici, aucune donnée ne suit (on passe directement au bloc suivant). À la rencontre d'un tel bloc, LoadSeg() remplit la mémoire
(qui avait été allouée lors de la lecture du HUNK_HEADER) par des zéros. C'est donc un moyen commode de réserver des tampons
et autres variables non initialisés dans un programme. En assembleur, on réclame une section BSS, puis on la remplit par des
directives ds.b, ds.w ou ds.l, comme on en a l'habitude. BSS signifie "Block Storage Segment", ds signifie "Define Storage".
Contrairement aux HUNK_BSS toutefois, les BSS simulés par des HUNK_DATA comme mentionné ci-dessus, ne sont pas remplis de zéros,
sauf utilisation (non prévue par Devpac) d'un type de mémoire MEMF_CLEAR dans le HUNK_HEADER, bien entendu.
Le format des tables
Pour ce qui concerne la division d'un programme en segments et la division de ceux-ci en blocs, je vous renvoie à l'article
du mois dernier. Dans chaque segment qui contient des références absolues, on trouve
une table de relogement, indiquant les adresses à corriger. Elle se situe dans un bloc de type HUNK_RELOC32. Il existe aussi
les identificateurs HUNK_RELOC16 et HUNK_RELOC8 mais ceux-ci ne sont utilisés que dans les modules objets, les relogements
effectifs en mémoire ne pouvant se faire que sur 32 bits.
décalage contenu
0 HUNK_RELOC32 ($3EC)
{1 n (non nul)
2 numéro de segment concerné
3 n mots longs donnat les décalages à corriger
3+n}
? 0
|
Ici, tous les décalages sont en mots longs, sauf depuis le Kickstart 2.0 qui a introduit l'identificateur HUNK_RELOC32SHORT
($3FC), qui utilise des mots courts, d'où un gain de place. Le but du programme est de proposer de convertir ces tables au format
court.
Le programme
Nommé, ShortenReloc, il se propose de convertir les tables de relogements de fichiers exécutables au format court exposé ci-dessus.
Attention toutefois, après la conversion, le programme en question ne pourra plus être chargé sous Kickstart 1.3 ou antérieur.
De plus, ce procédé ne fonctionne que pour les fichiers suffisamment petits pour que les relogements n'excèdent pas 65535 octets !
De plus, les versions du Kickstart antérieures à la 3.0 comportent un bogue qui utilise $3F7 à la place de $3FC pour
HUNK_RELOC32SHORT. L'utilisation de cette dernière valeur peut être forcée dans ShortenReloc par l'option V39, mais le programme
ainsi créé ne pourra être chargé que sous Kickstart 3.0 ou supérieur !
include exec/exec.i
include exec/exec_lib.i
include dos/dos.i
include dos/doshunks.i
include dos/dos_lib.i
move.l 4.w,a6
move.l a6,Exec.Base
lea DOS.Name(pc),a1
moveq #37,d0 ; Kickstart 2.0 mini
jsr _LVOOpenLibrary(a6)
move.l d0,DOS.Base
beq Exit
move.l #Args.Template,d1
move.l #Args.Array,d2
moveq #0,d3
move.l DOS.Base(pc),a6
jsr _LVOReadArgs(a6)
move.l d0,Args.RDArgs
beq.s CloseDOS
move.l From.Arg(pc),d1
move.l #MODE_OLDFILE,d2
jsr _LVOOpen(a6) ; ouverture fichier source
move.l d0,Source.Handle
beq.s FreeArgs
move.l To.Arg(pc),d1
move.l #MODE_NEWFILE,d2
jsr _LVOOpen(a6) ; ouverture fichier destination
move.l d0,Dest.Handle
beq.s CloseSource
bsr ReadHeader ; transfère (et teste sa présence) le bloc
beq.s CloseDest ; HunkHeader.
moveq #0,d2 ; numéro du segment courant (commence à 0!)
move.l d0,d3 ; nombre de segments
HunkLoop
bsr ReadHunk
beq.s CloseDest
addq.l #1,d2 ; segment suivant
cmp.l d2,d3
bne.s HunkLoop
CloseDest
move.l Dest.Handle(pc),d1
jsr _LVOClose(a6)
CloseSource
move.l Source.Handle(pc),d1
jsr _LVOClose(a6)
FreeArgs
move.l Args.RDArgs(pc),d1
jsr _LVOFreeArgs(a6)
CloseDOS
move.l a6,a1
move.l Exec.Base(pc),a6
jsr _LVOCloseLibrary(a6)
Exit moveq #0,d0
rts
PrintError ; PrintError()
movem.l d0-d2/a0-a1/a6,-(sp)
move.l DOS.Base(pc),a6
jsr _LVOIoErr(a6)
bsr.s PrintFault
movem.l (sp)+,d0-d2/a0-a1/a6
rts
PrintFault ; PrintFault(Code)(D0)
movem.l d0-d2/a0-a1/a6,-(sp)
move.l DOS.Base(pc),a6
move.l d0,d1
move.l #Fault.Header,d2
jsr _LVOPrintFault(a6)
movem.l (sp)+,d0-d2/a0-a1/a6
rts
ReadVal ; (Z=1)Error,(D0)Value=ReadVal(Size-1)(D0)
movem.l d1-d3/a0-a1/a6,-(sp)
move.l DOS.Base(pc),a6
moveq #0,d3
move.l d0,d2
.CLoop move.l Source.Handle(pc),d1
jsr _LVOFGetC(a6)
cmp.l #-1,d0
beq.s .Fail
lsl.l #8,d3
add.b d0,d3
dbra d2,.CLoop
move.l d3,d0
moveq #-1,d1
.Ret movem.l (sp)+,d1-d3/a0-a1/a6
rts
.Fail bsr PrintError
moveq #0,d0
bra.s .Ret
; lit un mot long
ReadLong ; (Z=1)Error,(D0)Value=ReadLong()
moveq #3,d0 ; 4 octets
bra ReadVal
; lit un mot
ReadWord ; (Z=1)Error,(D0)Value=ReadWord()
moveq #1,d0 ; 2 octets
bra ReadVal
; les <Size> octets les plus à GAUCHE de <Value> sont écrits dans le fichier
WriteVal ; (Z=1)Error=WriteVal(Value,Size-1)(D0,D1)
movem.l d0-d4/a0-a1/a6,-(sp)
move.l DOS.Base(pc),a6
move.l d0,d3 ; valeur (mot long)
move.l d1,d4 ; compteur
moveq #0,d2
.CLoop rol.l #8,d3 ; octet suivant
move.b d3,d2
move.l Dest.Handle(pc),d1
jsr _LVOFPutC(a6)
cmp.l #-1,d0 ; erreur?
beq.s .Fail
dbra d4,.CLoop
moveq #1,d0 ; met Z à 0
.Ret movem.l (sp)+,d0-d4/a0-a1/a6
rts
.Fail bsr PrintError
moveq #0,d0 ; met Z à 1
bra.s .Ret
; écrit un mot long
WriteLong ; (Z=1)Error=WriteLong(Value)(D0)
move.l d1,-(sp)
moveq #3,d1
bsr WriteVal
movem.l (sp)+,d1 ; movem pour préserver le bit Z
rts
; écrit un mot
WriteWord ; (Z=1)Error=WriteWord(Value)(D0)
movem.l d0-d1,-(sp)
cmp.l #$10000,d0 ; ça tient dans un mot?
bhs.s .TooBig
swap d0 ; WriteVal() demande une justification à gauche
moveq #1,d1
bsr WriteVal
.Ret movem.l (sp)+,d0-d1
rts
.TooBig move.l #ERROR_OBJECT_TOO_LARGE,d0
bsr PrintFault
moveq #0,d0
bra.s .Ret
ReadRecords ; (Z=0)Success,(D0)Buffer=ReadRecords(Number,Size)(D0,D1)
movem.l d1-d4/a0-a1/a6,-(sp)
move.l d0,d4
move.l d1,d3
mulu d3,d0 ; nombre d'octets
move.l #MEMF_PUBLIC,d1
move.l Exec.Base(pc),a6
jsr _LVOAllocVec(a6)
move.l d0,d2
bne.s .MemOK
bsr PrintError ; en cas d'échec, Exec met IoErr() à 103
bra.s .Fail ; pour les processus...
.MemOK move.l DOS.Base(pc),a6
moveq #0,d0 ; corrige un léger bug de FRead()
jsr _LVOSetIoErr(a6)
move.l Source.Handle(pc),d1
jsr _LVOFRead(a6)
cmp.l d0,d4 ; lecture réussie?
beq.s .OK
bsr PrintError
move.l d2,a0
bsr.s FreeBuf
.Fail moveq #0,d2
.OK move.l d2,d0
movem.l (sp)+,d1-d4/a0-a1/a6
rts
; libère de la mémoire allouée par AllocVec(), en préservant les
; codes de conditions
FreeBuf ; FreeBuf(Buffer)(A0) ; DOIT préserver le bit Z
movem.l d0-d2/a0-a1/a6,-(sp)
movea.l Exec.Base(pc),a6
jsr _LVOGetCC(a6)
move.l d0,d2
move.l a0,a1
jsr _LVOFreeVec(a6)
move.w d2,CCR
movem.l (sp)+,d0-d2/a0-a1/a6
rts
ReadLongs ; (Z=0)Success,(D0)Buffer=ReadLongs(Number)(D0)
move.l d1,-(sp)
moveq #4,d1 ; 4 octets par enregistrement
bsr ReadRecords
movem.l (sp)+,d1 ; préserve Z
rts
ReadWords ; (Z=0)Success,(D0)Buffer=ReadWords(Number)(D0)
move.l d1,-(sp)
moveq #2,d1
bsr ReadRecords
movem.l (sp)+,d1
rts
WriteRecords ; (Z=0)Success=WriteRecords(Buffer,Number,Size)(A0,D0,D1)
movem.l d0-d4/a0-a1/a6,-(sp)
move.l d0,d4 ; nombre
move.l a0,d2 ; adresse
move.l d1,d3 ; taille des blocs
moveq #0,d0 ; corrige un petit bug de FWrite()
move.l DOS.Base(pc),a6
jsr _LVOSetIoErr(a6)
move.l Dest.Handle(pc),d1
jsr _LVOFWrite(a6)
cmp.l d0,d4
eori.b #1<<2,ccr ; inverse l'état du bit Z
movem.l (sp)+,d0-d4/a0-a1/a6
rts
WriteLongs ; (Z=0)Success=WriteLongs(Buffer,Number)(A0,D0)
move.l d1,-(sp)
moveq #4,d1
bsr WriteRecords
movem.l (sp)+,d1
rts
WriteWords ; (Z=0)Success=ReadWords(Buffer,Number)(A0,D0)
move.l d1,-(sp)
moveq #2,d1
bsr WriteRecords
movem.l (sp)+,d1
rts
; la présence d'un HUNK_HEADER en début de fichier indique soit un fichier
; exécutable, soit un fichier objet. Un fichier objet sera automatiquement
; rejeté par notre programme car il contient aussi des HUNK_UNIT.
ReadHeader ; (Z=0)Success,(D0)Hunks=ReadHeader()
movem.l d1-d2/a0-a1/a6,-(sp)
bsr ReadLong
beq.s .Fail
cmp.l #HUNK_HEADER,d0
beq.s .HeaderFound
move.l #ERROR_FILE_NOT_OBJECT,d0
bsr PrintFault
bra.s .Fail
.HeaderFound
bsr ReadLong
beq.s .Fail
tst.l d0
beq.s .NoResLib
move.l #ERROR_BAD_HUNK,d0 ; les bibliothèques
bsr PrintFault ; résidentes ne sont plus autorisées
bra.s .Fail
.NoResLib
bsr ReadLong
beq.s .Fail
move.l d0,d2 ; nombre de segments
addq.l #2,d0 ; il y a deux mots longs supplémentaires
bsr ReadLongs
beq.s .Fail
move.l d0,a0 ; A0= partie du bloc HunkHeader
move.l Exec.Base(pc),a6
move.l #HUNK_HEADER,d0
bsr WriteLong ; identificateur de bloc
beq.s .FreeBuf
moveq #0,d0
bsr WriteLong ; pas de bibliothèque résidente
beq.s .FreeBuf
move.l d2,d0
bsr WriteLong ; nombre de segments
beq.s .FreeBuf
addq.l #2,d0
bsr WriteLongs ; le reste du bloc
bne.s .HeaderWritten
.FreeBuf
bsr FreeBuf
.Fail moveq #0,d2
bra.s .Ret
.HeaderWritten
bsr FreeBuf
.Ret move.l d2,d0
movem.l (sp)+,d1-d2/a0-a1/a6
rts
; cette fonction est appelée pour chaque segment. Elle transfère tous
; les blocs qu'il contient en convertissant les RELOC32.
ReadHunk ; (Z=0)Success=ReadHunk()
movem.l d0-d2/a0-a1/a6,-(sp)
.BlockLoop
bsr ReadLong
beq.s .Fail
move.l d0,d2
bsr Tell
cmp.l #HUNK_END,d0
beq.s .End
lea Blocks.Table(pc),a0
.Table cmp.w (a0)+,d0
beq.s .Found
tst.w (a0)+
bne.s .Table
btst #HUNKB_ADVISORY,d0
beq.s .Unknown
lea Hunk_DEBUG(pc),a0
bra.s .Ignore
.Unknown
move.l #ERROR_BAD_HUNK,d0
bsr PrintError
.Fail moveq #0,d0
.Ret movem.l (sp)+,d0-d2/a0-a1/a6
rts
.End bsr WriteLong
beq.s .Ret
moveq #1,d0
bra.s .Ret
.Found movea.w (a0),a0
add.l #ReadHunk,a0 ; évite trop de Reloc32 dans le prog :-)
.Ignore jsr (a0)
beq.s .Fail
bra.s .BlockLoop
; les routines suivantes sont appelées pour traiter les blocs dont
; l'identificateur se trouve en D2. En retour, Z=0 indique l'absence
; d'erreur
READLONG MACRO
bsr ReadLong
beq.s .Ret
ENDM
READWORD MACRO
bsr ReadWord
beq.s .Ret
ENDM
WRITELONG MACRO
bsr WriteLong
beq.s .Ret
ENDM
WRITEWORD MACRO
bsr WriteWord
beq.s .Ret
ENDM
; les suivants se contentent de recopier le bloc intégralement.
Hunk_NAME
Hunk_CODE
Hunk_DATA
Hunk_DEBUG
Hunk_Generic
move.l d2,d0
WRITELONG ; identificateur
READLONG ; nombre de mots longs
WRITELONG
move.l d0,d1
bsr ReadLongs
beq.s .Ret
move.l d0,a0
move.l d1,d0
jsr WriteLongs
bsr FreeBuf
.Ret rts
; ici, il n'y a que deux mots longs
Hunk_BSS
move.l d2,d0
WRITELONG
READLONG ; longueur (mots longs)
bsr WriteLong
.Ret rts
; un peu plus compliqué pour connaître la longueur de celui-ci (il y
; a d'autres données)
Hunk_SYMBOL
bsr Hunk_Generic ; longueur et nom
beq.s .Ret
READLONG ; valeur du symbole
WRITELONG
READLONG ; zéro final
WRITELONG
.Ret rts
; à convertir en BOGUSSHORT ou RELOC32SHORT (pour option V39)
Hunk_RELOC32
move.l #HUNK_RELOC32SHORT,d0
tst.l V39.Arg
bne.s .NewShort
move.l #HUNK_BOGUSSHORT,d0
.NewShort
WRITELONG ; change l'identificateur de bloc
moveq #0,d2 ; compteur pour l'alignement
.Loop READLONG ; nombre de décalages
WRITEWORD
move.l d0,d1
beq.s .End
READLONG ; numéro de segment
WRITEWORD
move.l d1,d0
bsr ReadLongs
beq.s .Ret
move.l d0,a0
bsr ConvertOffsets
beq.s .Ret
move.l d1,d0
add.l d1,d2
bsr WriteWords
bsr FreeBuf
bne.s .Loop
.Ret rts
.End and.l #1,d2 ; déjà aligné sur un mot long (nombre de mots impair)?
bne.s .LW ; (il faut ajouter le 0 final!) sinon rajouter un
moveq #0,d0 ; mot nul
WRITEWORD
.LW moveq #1,d0 ; met Z à 0
bra.s .Ret
; à recopier
Hunk_RELOC32SHORT
Hunk_BOGUSSHORT
move.l d2,d0
WRITELONG
.Loop READWORD ; longueur du nom
WRITEWORD
moveq #0,d1
move.w d0,d1
beq.s .End
READWORD ; numéro de segment
WRITEWORD
move.l d1,d0
bsr ReadWords
beq.s .Ret
move.l d0,a0
move.l d1,d0
add.l d1,d2
bsr WriteWords
bsr FreeBuf
bne.s .Loop
.Ret rts
.End and.l #1,d2 ; y a-t-il encore un mot nul?
beq.s .LW
READWORD
WRITEWORD
.LW moveq #1,d0
bra.s .Ret
Tell ; Tell(Block)(D0)
movem.l d0-d2/a0-a1/a6,-(sp)
tst.l Verbose.Arg
beq.s .None
move.l DOS.Base(pc),a6
move.l #Verbose.Fmt,d1
move.l d0,-(sp)
move.l sp,d2
jsr _LVOVPrintf(a6)
addq.l #4,sp
.None movem.l (sp)+,d0-d2/a0-a1/a6
rts
; cette routine convertit (dans le même tampon) les décalages mots longs
; en mots courts, tout en testant si tous les décalage sont suffisamment
; courts.
ConvertOffsets ; (Z=0)Success=ConvertOffsets(RelocOffsets,Number)(A0,D1)
movem.l d0-d1/a0-a1,-(sp)
subq.l #1,d1
move.l a0,a1
.Loop move.l (a0)+,d0
cmp.l #$10000,d0
bhs.s .TooBig
move.w d0,(a1)+
dbra d1,.Loop
moveq #1,d0 ; met Z à 0
.Ret movem.l (sp)+,d0-d1/a0-a1
rts
.TooBig move.l #ERROR_OBJECT_TOO_LARGE,d0
bsr PrintFault
moveq #0,d0
bra.s .Ret
Exec.Base dc.l 0
DOS.Base dc.l 0
Args.RDArgs dc.l 0
Source.Handle dc.l 0
Dest.Handle dc.l 0
Args.Array
From.Arg dc.l 0
To.Arg dc.l 0
Verbose.Arg dc.l 0
V39.Arg dc.l 0
BLOCK MACRO ; nom du type de bloc
dc.w HUNK_1,Hunk_1-ReadHunk
ENDM
Blocks.Table
BLOCK NAME ; types de blocs reconnus par notre
BLOCK CODE ; programme. Les autres types génèrent
BLOCK DATA ; une erreur. Les autres types n'arrivent
BLOCK BSS ; que dans les modules objets, à l'exception
BLOCK RELOC32 ; des overlays.
BLOCK RELOC32SHORT
HUNK_BOGUSSHORT EQU HUNK_DREL32 ; les mecs de Commodore se sont
BLOCK BOGUSSHORT ; trompés !
BLOCK SYMBOL
BLOCK DEBUG
dc.l 0,0 ; fin de la table
DOS.Name dc.b "dos.library",0
Args.Template dc.b "FROM/A,TO/A,VERBOSE/S,V39/S",0
Fault.Header dc.b "ShortenReloc",0
Verbose.Fmt dc.b "Bloc: $%08.lx",10,0
|
|