-
Notifications
You must be signed in to change notification settings - Fork 16
/
x86_C_compilation.language.txt
238 lines (208 loc) · 12.3 KB
/
x86_C_compilation.language.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
┏━━━━━━━━━━━━━━━━━━━━━━━┓
┃ ASM C COMPILATION ┃
┗━━━━━━━━━━━━━━━━━━━━━━━┛
fonc() # call ADR <fonc>
PUSH ARGUMENT #TOUINT de 4 octets ou moins (eax) ou float :
# mov [esp], VAL
#Ou :
# push VAL
#TOUINT de 8 octets (edx:eax) ou double :
# suite de 2 push, ou mov [esp] puis mov [esp + 4]
#long double :
# suite de 3 pushs, etc.
ENTRER FONCTION # push ebp
# mov ebp, esp
# [sub esp, TAILLE_VAR] (si TAILLE_VAR != 0)
#Ou :
# enter TAILLE_VAR, 0
#TAILLE_VAR est la somme de :
# - taille pour variables locales (divisable par 0x10)
# - taille max nécessaire pour variables temporaires
# (comprend aussi les arguments des fonctions
# appelées) (divisable par 0x04)
RETURN VAL #TOUINT de 4 octets ou moins (eax) :
# mov eax, VAL
#TOUINT de 8 octets (edx:eax):
# mov eax, VAL1
# mov edx, VAL2
#TOUFLOAT (st0) :
# fld VAL ou fild VAL
SORTIR FONCTION #Pas de variables locales :
# pop ebp
# ret
#Variables locales :
# leave
# ret
ACCEDER ARGUMENTS #TOUINT de 4 octets ou moins (eax) ou float :
# [ebp + 8] (1er argument)
# [ebp + 12] (2ème argument)
# etc.
#TOUINT de 8 octets (edx:eax) ou double :
# [ebp + 8] puis [ebp + 12] (1er argument)
# etc.
#long double :
# [ebp + 8], [ebp + 12] puis [ebp + 16] (1er argument)
# etc.
argc # [ebp + 8] dans le main()
argv # [ebp + 12] dans le main()
arge # [ebp + 16] dans le main()
VAL #Les variables temporaires sont pushed sur la pile,
#comme les variables locales, ou mises directement
#dans des registres si c'est possible.
DECLARATIONS ==> #Le compiler ignore celle sans initialisation future,
#et rassemble sinon la déclaration et l'initialisation.
VAR = VAL; # mov VAR, VAL
#Pour double :
# fld qword VAL (M souvent dans .data si VAL connue
# compile-time)
# fstp qword VAR
#Ou deux mov.
#Pour long double :
# mov R1, VAL_DEBUT
# mov R2, VAL_MILIEU
# mov R3, VAL_FIN
# mov dword M, R1
# mov dword M+4, R2
# mov dword M+8, R3
[auto|registr] TYPE VAR;#Erreur compile-time si définie hors d'un bloc.
[auto] TYPE VAR = VAL;
register TYPE VAR = VAL;# mov R, VAL
[static|extern] TYPE #Définie et initialise une VAR dans .data
VAR = VAL; #Si static dans un bloc : accès hors du bloc provoque
#erreur compile-time, mais malgré tout définie dans
#.data : sa portée réduite est juste une construction
#du compiler. Erreur compile-time aussi si extern
#définie dans un bloc.
TYPE ARR[TAILLE]; #Pas initialisé =>
TYPE ADR; # - auto : mis en variable locale via enter.
# - static/extern : dans .bss
TYPE ARR[[TAILLE]] = #Est dans .data
{ ... } # - auto : variable locale, et initialisée avec une
char STR[[TAILLE]] = # série de mov
"..."
TYPE ARR[] vs TYPE ADR #Les deux sont des pointeurs. Mais le premier pointe
ARRAY STATIQUE VS DYNAM.#vers des data allouées sur le stack (en général
==> #[esp + N]), et le second vers des data dans .bss.
#Le premier a donc une taille fixe (compile-time), mais
#peut être modifié ; et le second ne peut pas être
#modifié, sauf avec allocation sur le heap, ce qui lui
#permet aussi d'avoir une taille variable.
MATRICES ==> # - toujours simulés via des arrays unidimensionnels :
# - si ARRAY[x][y][z] (où Nx est taille de dimension
# x, etc.) :
# - accéder à ARRAY[x][y][z] : ARRAY + Ny * Nz * x +
# Nz * y + z
# - "rowwise" car finit par z et non par x
# - il n'est donc pas nécessaire de connaître x, qui
# peut être omis en C
const [static|extern] #Pareil, mais définit dans .rodata et non .data
TYPE VAR = VAL;
const [auto] TYPE VAR = #La constness des auto est seulement checkée
VAL #compile-time : si tentative d'écriture compile-time,
TYPE FONC(const VAR) #erreur.
volatile #Empêche de "compile out" une variable considérée
#inutile ?
inline FONC #Si accepté par le compiler, pas de call, mais
#intégration direct de la fonction à chaque endroit où
#elle est called.
fonc(CHAR_VAL) #L'argument mis sur le stack sera un INT_VAL : CHAR_VAL
fonc(SHORT_VAL) #et SHORT_VAL sont "widen" en faisant un transtypage
#vers int.
char FONC_VAR #De même un return type char ou short sera mis dans eax,
short FONC_VAR #donc widen par défaut.
(signed short/int) # movsx R, SCHAR_VAL
SCHAR_VAL/SSHORT_VAL
(unsigned short/int) # movzx R, SCHAR_VAL
UCHAR_VAL/USHORT_VAL
([un]signed long long #Fait transtypage vers int, puis vers long long int.
int) UCHAR_VAL/USHRT_VAL
(signed long long int) # mov R1, INT_VAL
INT_VAL # mov R2, R1
# sar R2, 0x1f
#(utilisation ensuite de R2:R1)
(unsigned long long int)# mov R1, INT_VAL
INT_VAL # mov R2, 0
#(utilisation ensuite de R2:R1)
(touint plus faible) # mov R1, R2 (où R2 est le registre faible d'un R3:R2)
TOUINT_VAL #ou :
# mov TYPE M, R2 (où TYPE est le type plus faible)
UNSIGNED <--> SIGNED #Rien de spécial, garde la même valeur, même si
#interprétation différente dans l'autre signedness
#(responsabilité du développeur).
(toufloat plus fort) # fld TYPE_FAIBLE TOUFLOAT_VAL
TOUFLOAT_VAL # fstp TYPE_FORT TOUFLOAT_VAL2
(toufloat plus faible) # fld TYPE_FORT TOUFLOAT_VAL
TOUFLOAT_VAL # fstp TYPE_FAIBLE TOUFLOAT_VAL2
(TOUFLOAT_VAL) # fild TYPE1 TOUINT_VAL
TOUINT_VAL # fstp TYPE2 TOUFLOAT_VAL
#TYPE1 est dword pour un int et qword pour un long long
#int.
#TYPE2 est dword, qword ou tword pour un float, double
#ou long double.
#Si TOUINT_VAL est short ou char, il est transtyper en
#int d'abord.
(TOUINT_VAL)
TOUFLOAT_VAL # A FAIRE !
TYPE_ADR #Son accès non-déréfencé est comme celui d'une INT_VAL
#normal, quel que soit TYPE.
VOID_ADR #Traitée comme un INT_ADR, sauf que déférencement
#impossible.
void #Comme un int, mais avec des restrictions compile-time :
# - si VOID_ADR, impossible à déréfencer
# - seul VOID_ADR possible, sauf pour le return type
# d'une fonction et le type de ses arguments.
#Et des nécessités pour :
# - les fonctions ne retournant rien.
# - les fonctions ne prenant pas d'argument.
# - en C++, obligation d'expliciter un transtypage
# depuis void* vers TYPE*
typedef #Simple preprocessing compile-time.
enum ENUM { ... } #De même que reste, simple déclarations (dont
#déclaration du type de l'énum, sans instantiation)
#sont utilisées par le compiler, mais pas compilées.
enum ENUM ENUM_VAR = VAL#La VAL est remplacée par la vraie valeur compile-time,
#et ENUM_VAR est en fait un UINT_VAL normal auquel il
#est affecté cette vraie valeur.
struct et classe ==> #Même remarque qu'enum pour les déclarations.
struct STRUCT STRUCT_VAR#Revient à une instantiation d'array statique, mais avec
#des éléments de taille différente.
PADDING ==> #Les char font bien un seul octet dans un struct, mais
#un vide avec du garbage est laissé entre lui et
#l'élémént prochain, pour chaque élément soit aligné sur
#4 bits.
#Tout padding est dû à une question d'alignement, et
#suit le membre à aligner (même s'il est à la fin).
union ==> #Comme un ensemble de structs/CLASSDT normales, mais
#dont l'adresse est la même pour tous (donc ils
#s'écrasent les uns les autres).
CLASS CLASS_VAR #Une instantiation d'une classe n'instantiatie en fait
#que ses CLASSDT : cela revient donc à instantier une
#struct de CLASSDT sur le stack (auto) ou dans .data
#(non-pointeurs), .bss (pointeurs) et .rodata (const).
CLASSFK #Les CLASSFK sont en fait des fonctions prenant comme
#premier argument (implicite dans le code C) une
#CLASS_ADR. Le pointeur CLASS_ADR est const (pas la
#CLASS_VAR pointée, sauf si CLASSFK() const { })
#Ainsi CLASS_VAR.CLASSFK() revient à faire
#CLASSFK(&CLASS_VAR). Elles sont de lifetime globale,
#et pas instantiée par une CLASS_VAR, mais ne peuvent
#être called que via une CLASS_VAR (cette dernière étant
#le premier argument implicite).
public: private:
protected: #Simple check compile-time, pas d'assemblage.
TYPE& #Références sont implémentées comme les pointeurs,
#mais :
# - empêchent le déréfencement, et donc aussi
# l'arithmétique des pointeurs
# - n'utilise pas .bss, mais l'adresse sur le stack,
# donc pas de problème d'allocation mémoire.
# - le TYPE doit donc être connu compile-time, ce qui
# empêche des choses comme les tableaux dynamiques.
TEMPLATES FONCTION ==> #L'ensemble des instantiations du template de la
#fonction sont recherchées compile-time, et une fonction
#spéciale (mangle name différent) pour chacune est créé.
#Il n'est donc pas possible de d'abord compiler la
#déclaration et définition à part, puis de lier avec un
#fichier utilisant une instantiation particulière du
#template : les instantiations doivent connues lors de
#la compilation du template.