Intel Core arkitekturen

Intel Core i7
Niels Olof Bouvin
Institut for Datalogi
Aarhus Universitet
1
Oversigt
Historie
Maskinsprogsniveauet
Symbolsk maskinsprog
Funktionskald
Argumentoverførsel & biblioteksfunktioner
2
Udviklingen af Intels processorer
3
Udviklingen af Intels processorer
Moore’s Curve
4
Oversigt
Historie
Maskinsprogsniveauet
Symbolsk maskinsprog
Funktionskald
Argumentoverførsel & biblioteksfunktioner
5
Core i7
Level 5
Problem-oriented language level
Translation (compiler)
Level 4
Assembly language level
Translation (assembler)
Level 3
Operating system machine level
Partial interpretation (operating system)
Level 2
Instruction set architecture level
Interpretation (microprogram) or direct execution
Level 1
Micro-architecture level
Hardware
Level 0
Digital logic level
6
Core i7 / AMD64 / x86-64
Level 5
Problem-oriented language level
Translation (compiler)
Level 4
Assembly language level
Software
Translation (assembler)
Level 3
Operating system machine level
Partial interpretation (operating system)
Level 2
Instruction set architecture level
Interpretation (microprogram) or direct execution
Hardware
Level 1
Micro-architecture level
Hardware
Level 0
Digital logic level
7
x86-64
Vigtige elementer på maskinkodeniveauet
Central Processing Unit (CPU)
Control Unit
Registre
Lagermodellen
Arithmetic Logic Unit
(ALU)
Datatyper
Input/Output devices
Registers
Maskininstruktioner
Instruktionsformater
Main
Memory
Disk
Printer
Addresseringsformer
Bus
8
Register, bus og lager evolution
8080
A
B
C
D
H
L
Memory
0
1
8-bit
Address Bus
8-bit
Data Bus
2
…
255
8086
AX
AH AL
BX
BH BL
CX
CH CL
DX
DH DL
SP
BP
SI
DI
80386
Memory
segment
EAX
0
EBX
1
20-bit
Address Bus
16-bit
Data Bus
2
ECX
…
65,535
9
EDX
ESP
EBP
ESI
EDI
AX
AH AL
BX
BH BL
CX
CH CL
DX
DH DL
SP
BP
SI
DI
Memory
0
1
32-bit
Address Bus
32-bit
Data Bus
2
…
4.294.967.295
IA-32 registre
6 generelle 32-bit registre
EAX – akkumulator for operander og resultater.
EBX – bruges oftest til pointere (lager adresser).
ECX – bruges specielt ifm. løkker.
EDX – bruges specielt ifm. multiplikation/division.
ESI/EDI – bruges specielt ifm. manipulation af strenge.
4 specielle 32-bit registre
EBP – Pointer register der udpeger aktuelt stakafsnit.
ESP – Pointer register der udpeger staktoppen.
EIP – programtælleren (instruction pointer).
EFLAGS – status register (Program Status Word – PSW).
Herudover diverse andre registre til FP, MMX, …
10
Eksempel: EFLAGS register
Fungerer som
Program Status Word
(PSW)
CF
overflow på ikke-negative heltals
operationer
OF
overflow på heltals operationer
11
AMD64, aka x86-64: 64 bits brede registre
Core i7
RAX EAX
R8
R9
R10
R11
R12
R13
R14
R15
RBX EBX
RCX ECX
RDX
RSP
RBP
RSI
RDI
12
EDX
ESP
EBP
ESI
EDI
AX
AH AL
BX
BH BL
CX
CH CL
DX
DH DL
SP
BP
SI
DI
Memory
0
1
48-bit
Address Bus
64-bit
Data Bus
2
…
256 TiB
Linux x86-64 lagermodel
Et 256TB lineært, byte adressérbart lager opdelt i sektioner
text svarer til method area på IJVM
data til constant pool
stack er som IJVMs stak
RSP
bss er afsat, endnu ikke brugt plads
heap er tilgængeligt lager som kan benyttes efter behov
heap er dét, som allokeres v.hj.a. malloc
…
stack
dynamisk lager (uallokeret)
Uinitialiseret?
har som udgangspunkt ikke nogen værdi
behøver derfor ikke ligge i den udførbare fil
0x00007FFF'FFFFFFFF
RIP
heap
dynamisk lager (allokeret)
bss
uinitialiseret data (buffere)
data
initialiseret data
text
program
…
13
0x00000000'00000000
Numeriske datatyper på x86-64
Type
8 bits
16 bits
32 bits
64 bits
Signed Integer
✔
✔
✔
✔
Unsigned integer
✔
✔
✔
✔
Binary coded decimal integer
✔
✔
✔
Floating point
Hertil kommer vektortyper, mv.
14
Intel IA-32 & x86-64/AMD64 dokumentation
Intels egen referencemanual
3020 sider
http://download.intel.com/products/processor/manual/325462.pdf
AMD64 ABI referencemanual
System V Application Binary Interface AMD64 Architecture Processor Supplement
128 sider omkring kaldkonventionerne til Linux
http://www.x86-64.org/documentation/abi.pdf
15
Nogle af de mere end 500 instruktioner
16
Oversigt
Historie
Maskinsprogsniveauet
Symbolsk maskinsprog
Funktionskald
Argumentoverførsel & biblioteksfunktioner
17
Programmering i x86 symbolsk maskinsprog
Der findes mange assemblers til x86 maskinkode:
GAS
as86
NASM
Anvender forskellig syntax:
Intel Syntax
AT&T Syntax
GAS (GNU assembler, AT&T syntax) under Linux OS
18
AT&T vs Intel syntaks
$6, %eax
;
12(%ebp), %eax ;
16(%ebp), %eax ;
%eax, -12(%ebp);
8(%ebp), %eax ;
%eax
;
$4, %esp
-12(%ebp)
;
12(%ebp)
;
%eax
;
start k = 6 - i - j
eax = 6 - i
eax = 6 - i - j
k = eax
start towers(n-1, i, k)
eax = n-1
AT&T
.L2:
movl
subl
subl
movl
movl
decl
subl
pushl
pushl
pushl
k
i
n-1
;
;
;
;
;
;
start k = 6 - i - j
eax = 6 - i
eax = 6 - i - j
k = eax
start towers(n-1, i, k)
eax = n-1
Intel
.L2:
mov eax, 6
sub eax, DWORD PTR [ebp+12]
sub eax, DWORD PTR [ebp+16]
mov DWORD PTR [ebp-12], eax
mov eax, DWORD PTR [ebp+8]
dec eax
sub esp, 4
push DWORD PTR [ebp-12]
push DWORD PTR [ebp+12]
push eax
; k
; i
; n-1
Hvad er pænest? Smag og behag…
19
GAS syntaks
% bruges til at referere til registre
%ebp, %esp, %eax, %ebx,...
$ bruges til konstanter (immediate addressing)
$42, $53, $0xff,...
Suffix på symbolske instruktioner giver størrelsen på operander
b - byte (8 bit)
w - word (16 bit)
l - long word (32 bit)
q-quad word (64 bit)
(hvis man bruger 8-, 16- eller 32-bit operander, skal man bruge de matchende registre)
20
GAS syntaks
Immediate, register addressing:
movq $42,%rax
# rax = 42 // flyt (kopiér, faktisk) 42 til rax
Direct addressing:
pushq %rax
# push indholdet af rax på stakken
Register indirect addressing, register addressing:
movq (%rbp),%rax
# rax = m[rbp]
Indexed addressing, register addressing:
movq 8(%rbp),%rax # rax = m[rbp+8] / husk 8 bytes alignment
21
find_max.c
/* find_max.c */
> gcc find_max.c -o find_max
> ./find_max
> echo $?
53
>
#include <stdio.h>
int main(int argc, char *argv[])
{
long a = 53;
long b = 42;
long m = 0;
indeholder returnkoden fra
det netop afsluttede program
if (a >= b) {
m = a;
} else {
m = b;
}
}
return m;
22
find_max.S
.section .data
a:
b:
m:
# start of data section
.section .text
# start of text section
.global
# make _start symbol visible
_start
_start: pushq %rbp
movq %rsp,%rbp
movq a,%rax
movq b,%rbx
cmpq %rax,%rbx
jle if
jmp else
if:
movq %rax,m
jmp endif
else:
movq %rbx,m
endif:
> as find_max.S -o find_max.o
> ld find_max.o -o find_max
> ./find_max
> echo $?
53
>
.quad 53
.quad 42
.quad 0
movq m,%rdi
movq $60,%rax
leave
syscall
#
#
#
#
#
#
save old rbp to stack
set new value for rbp
a to rax
b to rbx
compute b-a <=0 and set eflags
if (a >= b) {
#
#
#
#
#
#
#
#
m = a
} else {
m = b
}
put m in rdi
system call 60 is exit
clean up
exit(m)
23
Oversættelse og sammenlinking
as
ld
På Unix (herunder Linux) (og mange andre steder, f.eks. Wii og Playstation 3)
anvendes formatet ELF (Executable and Linkable Format) til objektfiler og
udførbare filer
24
Oversættelse til objektkode
> as find_max.S -o find_max.o
> objdump -dxs find_max.o > find_max.elf
find_max_as.o:
file format elf64-x86-64
find_max_as.o
architecture: i386:x86-64, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x0000000000000000
Sections:
Idx Name
0 .text
Size
0000003f
CONTENTS,
00000018
CONTENTS,
00000000
ALLOC
1 .data
2 .bss
SYMBOL TABLE:
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000008
0000000000000010
000000000000001b
0000000000000025
000000000000002d
0000000000000000
l
l
l
l
l
l
l
l
l
g
d
d
d
Disassembly of section .text:
VMA
LMA
0000000000000000 0000000000000000
ALLOC, LOAD, RELOC, READONLY, CODE
0000000000000000 0000000000000000
ALLOC, LOAD, DATA
0000000000000000 0000000000000000
.text
0000000000000000
.data
0000000000000000
.bss 0000000000000000 .bss
.data
0000000000000000
.data
0000000000000000
.data
0000000000000000
.text
0000000000000000
.text
0000000000000000
.text
0000000000000000
.text
0000000000000000
Contents of section .text:
0000 554889e5 488b0425 00000000
0010 00000000 4839c37e 02eb0a48
0020 000000eb 0848891c 25000000
0030 25000000 0048c7c0 3c000000
Contents of section .data:
0000 35000000 00000000 2a000000
0010 00000000 00000000
.text
.data
a
b
m
if
else
endif
_start
488b1c25
89042500
00488b3c
c90f05
UH..H..%....H..%
....H9.~...H..%.
.....H..%....H.<
%....H..<......
00000000
5.......*.......
........
File off
00000040
Algn
2**2
00000080
2**2
00000098
2**2
0000000000000000 <_start>:
0: 55
1: 48 89 e5
4: 48 8b 04 25 00 00 00
b: 00
8: R_X86_64_32S
c: 48 8b 1c 25 00 00 00
13: 00
10: R_X86_64_32S
14: 48 39 c3
17: 7e 02
19: eb 0a
000000000000001b <if>:
1b: 48 89 04 25 00 00 00
22: 00
1f: R_X86_64_32S
23: eb 08
A’s adresse
0000000000000025 <else>:
25: 48 89 1c 25 00 00 00
2c: 00
29: R_X86_64_32S
B’s adresse
000000000000002d <endif>:
2d: 48 8b 3c 25 00 00 00
34: 00
31: R_X86_64_32S
35: 48 c7 c0 3c 00 00 00
3c: c9
3d: 0f 05
25
push
mov
mov
%rbp
%rsp,%rbp
0x0,%rax
.data
mov
0x0,%rbx
.data+0x8
cmp
%rax,%rbx
jle
1b <if>
jmp
25 <else>
mov
%rax,0x0
.data+0x10
jmp
2d <endif>
mov
%rbx,0x0
.data+0x10
mov
0x0,%rdi
.data+0x10
mov
$0x3c,%rax
leaveq
syscall
Indskudt sætning: Little- og Big-Endian
Man kan arrangere bytes i words på to måder, little- og big-endian:
Big-endian: De mest betydende bytes kommer først
00 00 00 00 00 00 00 35₁₆ = 53₁₀
03 e8₁₆ = 1000₁₀
Little-endian: De mindst betydende bytes kommer først
35 00 00 00 00 00 00 00₁₆ = 53₁₀
e8 03₁₆ = 1000₁₀
Intel er little-endian, så derfor skrives bs værdi (4210) som 2a00000000000000
Så længe man holder sig til én maskinearkitektur, er der ingen problemer…
men det er noget, som man skal være opmærksom på, når man udveksler data mellem forskellige maskinarkitekturer
26
Assembly, sammenlinking og disassembly
> ld find_max.o -o find_max
> objdump -dxs find_max > find_max.elf
4000c0 f8006000 4839c37e 02eb0a48
4000d0 016000eb 0848891c 25000160
4000e0 25000160 0048c7c0 3c000000
Contents of section .data:
6000f0 35000000 00000000 2a000000
600100 00000000 00000000
find_max_as:
file format elf64-x86-64
find_max_as
architecture: i386:x86-64, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x00000000004000b0
Program Header:
LOAD off
0x0000000000000000 vaddr 0x0000000000400000 paddr
0x0000000000400000 align 2**21
filesz 0x00000000000000ef memsz 0x00000000000000ef flags r-x
LOAD off
0x00000000000000f0 vaddr 0x00000000006000f0 paddr
0x00000000006000f0 align 2**21
filesz 0x0000000000000018 memsz 0x0000000000000018 flags rwSections:
Idx Name
0 .text
Size
0000003f
CONTENTS,
00000018
CONTENTS,
1 .data
SYMBOL TABLE:
00000000004000b0
00000000006000f0
00000000006000f0
00000000006000f8
0000000000600100
00000000004000cb
00000000004000d5
00000000004000dd
00000000004000b0
0000000000600108
0000000000600108
0000000000600108
l
l
l
l
l
l
l
l
g
g
g
g
d
d
VMA
LMA
00000000004000b0 00000000004000b0
ALLOC, LOAD, READONLY, CODE
00000000006000f0 00000000006000f0
ALLOC, LOAD, DATA
.text
.data
.data
.data
.data
.text
.text
.text
.text
*ABS*
*ABS*
*ABS*
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
Contents of section .text:
4000b0 554889e5 488b0425 f0006000 488b1c25
89042500
00488b3c
c90f05
..`.H9.~...H..%.
.`...H..%..`.H.<
%..`.H..<......
00000000
5.......*.......
........
Disassembly of section .text:
00000000004000b0
4000b0: 55
4000b1: 48 89
4000b4: 48 8b
4000bb: 00
4000bc: 48 8b
4000c3: 00
4000c4: 48 39
4000c7: 7e 02
4000c9: eb 0a
A’s adresse
File off
000000b0
Algn
2**2
000000f0
2**2
<_start>:
e5
04 25 f0 00 60
push
mov
mov
%rbp
%rsp,%rbp
0x6000f0,%rax
1c 25 f8 00 60
mov
0x6000f8,%rbx
c3
cmp
jle
jmp
%rax,%rbx
4000cb <if>
4000d5 <else>
mov
%rax,0x600100
jmp
4000dd <endif>
mov
%rbx,0x600100
mov
0x600100,%rdi
00000000004000cb <if>:
4000cb: 48 89 04 25 00 01 60
4000d2: 00
4000d3: eb 08
.text
.data
a
b
m
if
else
endif
_start
__bss_start
_edata
_end
00000000004000d5 <else>:
4000d5: 48 89 1c 25 00 01 60
4000dc: 00
00000000004000dd <endif>:
4000dd: 48 8b 3c 25 00 01 60
4000e4: 00
4000e5: 48 c7 c0 3c 00 00 00
4000ec: c9
4000ed: 0f 05
UH..H..%..`.H..%
27
mov
$0x3c,%rax
leaveq
syscall
B’s adresse
find_max.S: Take Two
.section .data
a:
b:
m:
# start of data section
.section .text
# start of text section
.global
# make main symbol visible
main:
if:
else:
endif:
> gcc find_max.S -o find_max
> ./find_max
> echo $?
53
>
.quad 53
.quad 42
.quad 0
main
pushq %rbp
movq %rsp,%rbp
movq a,%rax
movq b,%rbx
cmpq %rax,%rbx
jle if
jmp else
movq %rax,m
jmp endif
movq %rbx,m
movq m, %rax
leave
ret
#
#
#
#
#
#
save old rbp to stack
setup new base pointer
a to rax
b to rbx
compute b-a <=0 and set eflags
if (a >= b) {
#
#
#
#
#
#
#
m = a
} else {
m = b
}
put m in rax
clean up
return m (rax)
Typisk anvender man ikke as og ld
seperat, da gcc kan håndtere det hele
Da er konventionerne en smule
anderledes—hovedfunktionen
hedder main, og man bruger blot ret
med exit koden i rax
28
Oversigt
Historie
Maskinsprogsniveauet
Symbolsk maskinsprog
Funktionskald
Argumentoverførsel & biblioteksfunktioner
29
Standarder for funktionskald
For en given platform (en kombination af CPU og operativsystem) eksisterer
konventioner for, hvorledes funktioner/procedurer/metoder kaldes
vi ser i denne sammenhæng på Linux og på x86 processorer
Kaldkonventionen har ændret sig fra IA-32 til x86-64
processoren har nu mange registre, hvilket ikke var tilfældet før
30
IA-32 Funktionskald
Stakken bruges til funktionskald
med IA-32’s lange historie og mange forskellige operativsystemer, så er der faktisk en del forskellige måder at kalde
funktioner på, men vi vil prøve at holde os til stakken og Unix/Linux
Ikke ulig invokevirtual og return, så har IA-32 instruktionerne call og ret
call A
læg “næste” eip på stakken, og sæt eip=A
ret
sæt eip=staktop og fjern det øverste element
Dette er konventionen for C, og da Unix (og dermed Linux) er skrevet i C, er
det den rigtige måde at gøre det på i Linux
31
C kaldkonventionen: IA-32
1-4
ebp
1.
2.
3.
4.
5.
6.
7.
8.
9.
Læg parametre på stak i omvendt rækkefølge (caller)
Kald funktion via call instruktionen (caller)
Etabler stakafsnit (manipulering af ebp og esp) (callee)
Gør plads til eventuelle lokale variable (callee)
Udfør kroppen af funktionen (callee)
Læg returværdi i eax registret (callee)
Nedlæg stakafsnit (manipulation af ebp og esp) (callee)
Returner fra kald via ret instruktionen (callee)
Fjern parametre fra stak (caller)
6-9
esp
parametre
gammel eip ebp+4
ebp
esp
32
ebp+8
gammel ebp
lokale
variabler
ebp-4
find_max2.c — nu med funktion!
/* find_max2.c */
int main(int argc, char *argv[])
{
long a = 42;
long b = 53;
long r = 0;
#include <stdio.h>
long find_max(long a, long b)
{
long m = 0;
r = find_max(a, b);
if (a >= b) {
m = a;
} else {
m = b;
}
return r;
}
return m;
}
33
find_max2.S - IA-32
.section .data
x:
.long 42
y:
.long 53
r:
.long 0
.section .text
.global _start
_start:
pushl y
pushl x
call max
addl $8,%esp
movl %eax,r
movl r,%ebx
movl $1, %eax
int $0x80
1-2
9
.type max, @function
max:
pushl %ebp
movl %esp,%ebp
subl $4,%esp
movl $0,-4(%ebp)
movl 8(%ebp),%eax
movl 12(%ebp),%ebx
cmpl %eax,%ebx
3-4
#
#
#
#
#
#
#
#
#
int main (void)
push y on stack
push x on stack
invoke max
pop parameters from stack
r = max(x,y) (%eax)
return value in ebx register
opcode for exit system call
return r (%ebx)
if:
else:
endif:
6-8
34
jle if
jmp else
movl %eax,-4(%ebp)
jmp endif
movl %ebx,-4(%ebp)
movl -4(%ebp),%eax
movl %ebp,%esp
popl %ebp
ret
#
#
#
#
#
#
#
#
#
#
int max (long a,long b)
push prev base pointer
setup new base pointer
local variable m
m = 0
load a into eax
load b into ebx
compute b-a <=0,
set eflags
if (a >= b)
# m = a
#
#
#
#
#
#
m = y
return value in %eax
restore esp (remove
locals)
restore ebp
return m
Funktionskald: x86-64
Platform
System V i386
Return
Parameter Registers
Value
eax, edx none
Additional
Parameters
Stack
Alignment
stack (right to left)
Scratch Registers
eax, ecx, edx
Preserved Registers
ebx, esi, edi, ebp, esp
System V X86_64 rax, rdx rdi, rsi, rdx, rcx, r8, r9 stack (right to left) 16-byte at call rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 rbx, rsp, rbp, r12, r13, r14, r15
ARM
r0, r1
r0, r1, r2, r3
stack
8 byte
r0, r1, r2, r3, r12
r4, r5, r6, r7, r8, r9, r10, r11, r13, r14
Seks registre afsættes til argumenter, ni registre kan frit benyttes
registre er ~100 gange hurtigere end RAM
Yderligere argumenter puttes på stakken
Da der kún er de samme registre, skal man være disciplineret om anvendelse
hvis man bruger ‘Preserved Registers’ (også kaldet ‘Callee Save’), skal man genskabe dem, inden man returnerer
Stakken holdes 16-bytes aligned
Returnværdier i rax og rdx
35
C kaldkonventionen: x86-64
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
Gem registre på stakken, om nødvendigt (caller)
Fyld parametre i registrene rdi,rsi,rdx,rcx,r8,r9 (caller)
rbp
Læg overskydende parametre på stak i omvendt rækkefølge (caller)
Kald funktion via call instruktionen (caller)
Etabler stakafsnit (manipulering af rbp og rsp) (callee)
rsp
Gør plads til eventuelle lokale variable (callee) (husk 16-byte alignment)
Gem Callee-Save registre, om nødvendigt (callee)
Udfør kroppen af funktionen (callee)
Læg returværdi i rax samt (evt.) rdx (callee)
Genskab gemte registreværdier, om nogen (callee)
Nedlæg stakafsnit via leave instruktionen (callee)
Returner fra kald via ret instruktionen (callee)
Læs returnværdi fra rax, samt (evt.) rdx (caller)
Genskab gemte registreværdier fra stakken, om nogen (caller)
36
1-7
9-14
rdi
rsi
rdx
rcx
r8
r9
ekstra
parametre rbp+16
gammel rip rbp+8
rbp
rsp
gammel rbp
lokale
variabler
rbp-8
find_max2.S - x86-64: (ineffektiv udgave)
.section .data
a:
b:
m:
# start of data section
5-7
.quad 53
.quad 42
.quad 0
.section .text
# start of text section
.global main
# make main symbol visible
main:
#
#
#
#
1-4
13-14
max:
pushq %rbp
movq %rsp,%rbp
movq a,%rdi
movq b,%rsi
call max
movq %rax,m
leave
ret
save old rbp to stack
set new value for rbp
a to rdi (first argument)
b to rsi (2nd argument)
if:
else:
endif:
9-12
# return m (rax)
37
push %rbp
movq %rsp,%rbp
subq $32,%rsp
movq %rdi,-8(%rbp)
movq %rsi,-16(%rbp)
movq $0,-24(%rbp)
movq -8(%rbp),%rax
movq -16(%rbp),%rbx
cmpq %rax,%rbx
jle if
jmp else
movq %rax,-24(%rbp)
jmp endif
movq %rbx,-24(%rbp)
movq -24(%rbp),%rax
leave
ret
#
#
#
#
#
#
#
#
#
#
push prev. base pointer
setup new base pointer
need 24, but 16-aligned
save a
save b
init m
a
b
if (a >= b) {
#
#
#
#
#
#
m = a
} else {
m = b
copy m to rax # }
clean up
return m (rax)
find_max2.S - x86-64: (mindre ineffektiv udgave)
.section .data
# start of data section
max:
.section .text
# start of text section
if:
.global main
# make main symbol visible
else:
main:
#
#
#
#
endif:
a:
b:
.quad 53
.quad 42
pushq %rbp
movq %rsp,%rbp
movq a,%rdi
movq b,%rsi
call max
leave
ret
save old rbp to stack
set new value for rbp
a to rdi (first argument)
b to rsi (second argument)
push %rbp
movq %rsp,%rbp
cmpq %rdi,%rsi
jle if
jmp else
movq %rdi,%rax
jmp endif
movq %rsi,%rax
leave
ret
# push previous base pointer
# setup new base pointer
# if (a >= b) {
#
#
#
#
#
#
#
m = a
} else {
m = b
}
clean up
return m (rax)
# return m (rax)
Med masser af registre til rådighed, er der ingen grund til at unødigt bruge
stakken
38
Oversigt
Historie
Maskinsprogsniveauet
Symbolsk maskinsprog
Funktionskald
Argumentoverførsel & biblioteksfunktioner
39
Programkald
Som med IJVM kan man give kommandolinjeargumenter til sit program
> find_max3 8 9
Hvordan overføres de af operativsystemet til vores program?
i registrene og i hukommelsen
40
Biblioteksfunktioner og systemkald
Et operativsystem kommer med standardbiblioteker og systemkald, der kan
udføre mange funktioner for os
Standardbiblioteket kræver, at man instruerer ld korrekt:
ld find_max3.o -dynamic-linker /lib/ld-linux.so.2 -lc -o find_max3
I praksis er det langt det nemmeste at bruge gcc i stedet for as/ld
Standardbiblioteksfunktioner kaldes v.hj.a. C call konventionen
som almindelige funktioner (seks parametre i registerne, resten på stakken)
Systemkald foregår via argumenter i registrene og instruktionen syscall
Systemkaldets kode placeres (forvirrende nok) i rax, og argumenter i rdi,rsi,rdx,r10,r8,r9
0x60 er koden for systemkaldet ‘exit’, og argumentet placeres i rdi
41
Det initielle kald
Fra kommandolinjen kaldes et program med en række
argumenter
Bemærk signaturen på et C programs main funktion:
0x00000000
argument n
|
argument 2
argument 1
int main(int argc, char *argv[])
programnavn
rdi indeholder argc og rsi indeholder argv
Argumenterne peger på null-terminerede strenge
dvs. almindelige C strenge
42
rdi: #argumenter
rsi: pointer array
find_max3.c
/* find_max3.c */
int main(int argc, char *argv[])
{
long long a = 0;
long long b = 0;
long long r = 0;
#include <stdio.h>
#include <stdlib.h>
long long find_max(long long a, long long b)
{
long long m = 0;
a = atol(argv[1]);
b = atol(argv[2]);
if (a >= b) {
m = a;
} else {
m = b;
}
r = find_max(a, b);
return r;
}
return m;
}
> gcc find_max3.c -o find_max3
> ./find_max3 8 9 ; echo $?
9
43
find_max3.S
.section .data
argc:
.quad 0
argv:
.quad 0
a:
.quad 0
b:
.quad 0
.section .text
# start of data section
max:
# start of text section
if:
.global main
# make main symbol visible
else:
main:
#
#
#
#
#
endif:
pushq %rbp
movq %rsp,%rbp
movq %rdi, argc
movq %rsi, argv
movq 8(%rsi), %rdi
call atol
movq %rax, a
movq argv, %rax
movq 16(%rax),%rdi
call atol
movq %rax, b
movq a,%rdi
movq %rax,%rsi
call max
leave
ret
save old rbp to stack
set new value for rbp
save argc
save argv
first argument
# save a's value
# get argv back
# second argument
push %rbp
movq %rsp,%rbp
cmpq %rdi,%rsi
jle if
jmp else
movq %rdi,%rax
jmp endif
movq %rsi,%rax
leave
ret
# push previous base pointer
# setup new base pointer
# if (a >= b) {
#
#
#
#
#
#
#
m = a
} else {
m = b
}
clean up
return m (rax)
> gcc find_max3.S -o find_max3
> ./find_max3 8 9 ; echo $?
9
# save b's value
# get a back
# b still in rax
# return m (rax)
44
Sammenblanding af C og symbolsk maskinkode
I praksis har man sjældent lyst til at skrive hele sit program i symbolsk
maskinkode
C og andre højniveausprog er langt lettere at have med at gøre
Heldigvis understøtter f.eks. C, at man har funktioner skrevet i symbolsk
maskinkode
nemt at gøre, fordi der er en kaldkonvention, og så længe den overholdes er alting godt
45
find_max.S og find_max4.c
find_max:
if:
else:
endif:
.section .text
.global find_max
push %rbp
movq %rsp,%rbp
cmpq %rdi,%rsi
jle if
jmp else
movq %rdi,%rax
jmp endif
movq %rsi,%rax
leave
ret
#include <stdio.h>
#include <stdlib.h>
# push previous base pointr
# setup new base pointer
# if (a >= b) {
#
#
#
#
#
#
#
extern long long find_max(long long a, long long b);
int main(int argc, char *argv[])
{
long long a, b, r;
m = a
} else {
m = b
}
clean up
return m (rax)
a = atol(argv[1]);
b = atol(argv[2]);
r = find_max(a, b);
return r;
}
> gcc find_max4.c find_max.S -o find_max4
> ./find_max4 8 9 ; echo $?
9
46
Systemkald i find_max.S
.section .data
a:
b:
m:
# start of data section
.section .text
# start of text section
.global
# make _start symbol visible
_start
_start: pushq %rbp
movq %rsp,%rbp
movq a,%rax
movq b,%rbx
cmpq %rax,%rbx
jle if
jmp else
if:
movq %rax,m
jmp endif
else:
movq %rbx,m
endif:
> as find_max.S -o find_max.o
> ld find_max.o -o find_max
> ./find_max
> echo $?
53
>
.quad 53
.quad 42
.quad 0
movq m,%rdi
movq $60,%rax
leave
syscall
#
#
#
#
#
#
save old rbp to stack
set new value for rbp
a to rax
b to rbx
compute b-a <=0 and set eflags
if (a >= b) {
#
#
#
#
#
#
#
#
m = a
} else {
m = b
}
put m in rdi
system call 60 is exit
clean up
exit(m)
47
Forskellige systemkald
Name rax
rdi
rsi
rdx
sys_exit
60
int
sys_read
0
uint
ch*
size_t
sys_write
1
uint
ch*
size_t
sys_open
2
ch*
int
int
sys_close
3
uint
r10
r8
48
r9
Description
terminate the current process read from file descriptor
write to file descriptor
open / create file or device
close a file descriptor
find_max.S og find_max5.c — med output!
.section .text
.global find_max
find_max: push %rbp
movq %rsp,%rbp
cmpq %rdi,%rsi
jle if
jmp else
if:
movq %rdi,%rax
jmp endif
else:
movq %rsi,%rax
endif:
leave
ret
#include <stdio.h>
#include <stdlib.h>
# push previous base pointr
# setup new base pointer
# if (a >= b) {
#
#
#
#
#
#
#
extern long long find_max(long long a, long long b);
int main(int argc, char *argv[])
{
long long a, b, r;
m = a
} else {
m = b
}
clean up
return m (rax)
a = atol(argv[1]);
b = atol(argv[2]);
r = find_max(a, b);
printf("max(%lld,%lld) = %lld\n",a,b,r);
return 0;
}
> gcc find_max5.c find_max.S -o find_max5
> ./find_max5 8 9
max(8,9) = 9
49
find_max2.S og find_max5.c: Conditional move
.section .text
.global find_max
find_max: push %rbp
# push previous base pointr
movq %rsp,%rbp
# setup new base pointer
cmpq %rdi, %rsi
# b-a ?
cmovge %rsi, %rdi # if≥0: overwrite a with b
movq %rdi, %rax
# max→rax
leave
# clean up
ret
# return max
#include <stdio.h>
#include <stdlib.h>
extern long long find_max(long long a, long long b);
int main(int argc, char *argv[])
{
long long a, b, r;
a = atol(argv[1]);
b = atol(argv[2]);
r = find_max(a, b);
printf("max(%lld,%lld) = %lld\n",a,b,r);
return 0;
}
> gcc find_max5.c find_max2.S -o find_max5
> ./find_max5 8 9
max(8,9) = 9
50
Opsummering
AMD64/x86-64 er en registermaskine
man regner i registrene, og som regel får man argumenter/resultater i registre
Linux på AMD64/x86-64 har konventioner
for typiske funktionskald, herunder standardbiblioteket, samt for systemkald
f.eks. hvilke registre, man må bruge, og 16-byte alignment af stakken
Kald af funktioner kræver forberedelse, orden og oprydning
overholder man ikke reglerne, går det galt
Overgangen fra få registre til mange registre gør mange ting langt enklere
en tiltrængt modernisering—udført af Intels konkurrent!
Det gør livet langt enklere, at man kan blande C og symbolsk maskinsprog
51
Ugeopgave 5
52