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
© Copyright 2024