KAIST 04 Machine Level Programming Basics
KAIST 04 Machine Level Programming Basics
Machine‐Level Programming I: Basics
CS230: System Programming
4th Lecture
Instructor:
Jaehyuk Huh (허재혁)
Lecture slides based on CS:App from CMU and CS230 KAIST (Prof. Kim Daeyoung)
1
KAIST
Today: Machine Programming I: Basics
• History of Intel processors and architectures
• C, assembly, machine code
• Assembly Basics: Registers, operands, move
• Intro to x86‐64
KAIST
Intel x86 Processors
• Totally dominate laptop/desktop/server market
• Evolutionary design
– Backwards compatible up until 8086, introduced in 1978
– Added more features as time goes on
• Complex instruction set computer (CISC)
– Many different instructions with many different formats
• But, only small subset encountered with Linux programs
– Hard to match performance of Reduced Instruction Set Computers
(RISC)
– But, Intel has done just that!
• In terms of speed. Less so for low power.
KAIST
Intel x86 Evolution: Milestones
Name Date Transistors MHz
• 8086 1978 29K 5‐10
– First 16‐bit processor. Basis for IBM PC & DOS
– 1MB address space
• 386 1985 275K 16‐33
– First 32 bit processor , referred to as IA32
– Added “flat addressing”
– Capable of running Unix
– 32‐bit Linux/gcc uses no instructions introduced in later models
• Pentium 4F 2004 125M 2800‐3800
– First 64‐bit processor, referred to as x86‐64
• Core i7 2008 731M 2667‐3333
– Our lab machines
KAIST
Intel x86 Processors: Overview
Architectures Processors
X86‐16 8086
286
X86‐32/IA32 386
486
Pentium
MMX Pentium MMX
SSE Pentium III
SSE2 Pentium 4
SSE3 Pentium 4E
X86‐64 / EM64t Pentium 4F time
Core 2 Duo
SSE4 Core i7
IA: often redefined as latest Intel architecture
KAIST
Intel x86 Processors, contd.
• Machine Evolution
– 386 1985 0.3M
– Pentium 1993 3.1M
– Pentium/MMX 1997 4.5M
– PentiumPro 1995 6.5M
– Pentium III 1999 8.2M
– Pentium 4 2001 42M
– Core 2 Duo 2006 291M
– Core i7 2008 731M
• Added Features
– Instructions to support multimedia operations
• Parallel operations on 1, 2, and 4‐byte data, both integer & FP
– Instructions to enable more efficient conditional operations
• Linux/GCC Evolution
– Two major steps: 1) support 32‐bit 386. 2) support 64‐bit x86‐64
KAIST
More Information
• Intel processors (Wikipedia)
• Intel microarchitectures
KAIST
New Species: ia64, then IPF, then
Itanium,…
Name DateTransistors
• Itanium 2001 10M
– First shot at 64‐bit architecture: first called IA64
– Radically new instruction set designed for high performance
– Can run existing IA32 programs
• On‐board “x86 engine”
– Joint project with Hewlett‐Packard
• Itanium 2 2002 221M
– Big performance boost
• Itanium 2 Dual‐Core 2006 1.7B
• Itanium has not taken off in marketplace
– Lack of backward compatibility, no good compiler support, Pentium 4 got
too good
KAIST
x86 Clones: Advanced Micro Devices
(AMD)
• Historically
–AMD has followed just behind Intel
–A little bit slower, a lot cheaper
• Then
–Recruited top circuit designers from Digital Equipment Corp. and other
downward trending companies
–Built Opteron: tough competitor to Pentium 4
–Developed x86‐64, their own extension to 64 bits
KAIST
Intel’s 64‐Bit
• Intel Attempted Radical Shift from IA32 to IA64
– Totally different architecture (Itanium)
– Executes IA32 code only as legacy
– Performance disappointing
• AMD Stepped in with Evolutionary Solution
– x86‐64 (now called “AMD64”)
• Intel Felt Obligated to Focus on IA64
– Hard to admit mistake or that AMD is better
• 2004: Intel Announces EM64T extension to IA32
– Extended Memory 64‐bit Technology
– Almost identical to x86‐64!
• All but low‐end x86 processors support x86‐64
– But, lots of code still runs in 32‐bit mode
KAIST
Our Coverage
• IA32
– The traditional x86
• x86‐64/EM64T
– The emerging standard
• Presentation
– Book presents IA32 in Sections 3.1—3.12
– Covers x86‐64 in 3.13
– We will cover both simultaneously
– Some labs will be based on x86‐64, others on IA32
KAIST
Today: Machine Programming I: Basics
• History of Intel processors and architectures
• C, assembly, machine code
• Assembly Basics: Registers, operands, move
• Intro to x86‐64
KAIST
Definitions
• Architecture: (also instruction set architecture: ISA) The parts
of a processor design that one needs to understand to write
assembly code.
– Examples: instruction set specification, registers.
• Microarchitecture: Implementation of the architecture.
– Examples: cache sizes and core frequency.
• Example ISAs (Intel): x86, IA, IPF
KAIST
Assembly Programmer’s View
CPU Memory
Addresses
Registers Object Code
PC
Data Program Data
Condition OS Data
Instructions
Codes
Stack
• Programmer‐Visible State
– PC: Program counter
• Address of next instruction
• Called “EIP” (IA32) or “RIP” (x86‐64)
– Register file
• Heavily used program data
– Memory
• Byte addressable array
– Condition codes
• Code, user data, (some) OS data
• Store status information about most
recent arithmetic operation • Includes stack used to support
procedures
• Used for conditional branching
KAIST
Turning C into Object Code
– Code in files p1.c p2.c
– Compile with command: gcc –O1 p1.c p2.c -o p
• Use basic optimizations (-O1)
• Put resulting binary in file p
Compiler (gcc -S)
Assembler (gcc or as)
binary Executable program (p)
KAIST
Compiling Into Assembly
C Code Generated IA32 Assembly
int sum(int x, int y) sum:
{ pushl %ebp
int t = x+y; movl %esp,%ebp
return t; movl 12(%ebp),%eax
} addl 8(%ebp),%eax
popl %ebp
ret
Some compilers use instruction
“leave”
Obtain with command
/usr/local/bin/gcc –O1 -S code.c
Produces file code.s
KAIST
Assembly Characteristics: Data Types
• “Integer” data of 1, 2, or 4 bytes
– Data values
– Addresses (untyped pointers)
• Floating point data of 4, 8, or 10 bytes
• No aggregate types such as arrays or structures
– Just contiguously allocated bytes in memory
KAIST
Assembly Characteristics: Operations
• Perform arithmetic function on register or memory data
• Transfer data between memory and register
– Load data from memory into register
– Store register data into memory
• Transfer control
– Unconditional jumps to/from procedures
– Conditional branches
KAIST
Object Code
Code for sum
• Assembler
0x401040 <sum>:
0x55
– Translates .s into .o
0x89 – Binary encoding of each instruction
0xe5 – Nearly‐complete image of executable code
0x8b
0x45
– Missing linkages between code in different
0x0c files
0x03 • Linker
0x45
0x08 – Resolves references between files
• Total of 11 bytes
0x5d – Combines with static run‐time libraries
0xc3 • Each instruction • E.g., code for malloc, printf
1, 2, or 3 bytes
– Some libraries are dynamically linked
• Starts at address
0x401040 • Linking occurs when program begins
execution
KAIST
Machine Instruction Example
• C Code
int t = x+y;
– Add two signed integers
• Assembly
– Add 2 4‐byte integers
addl 8(%ebp),%eax
• “Long” words in GCC parlance
Similar to expression: • Same instruction whether signed
x += y or unsigned
More precisely: – Operands:
int eax; x: Register %eax
int *ebp; y: Memory M[%ebp+8]
eax += ebp[2] t: Register %eax
– Return function value in %eax
0x80483ca: 03 45 08 • Object Code
– 3‐byte instruction
– Stored at address 0x80483ca
KAIST
Disassembling Object Code
Disassembled
080483c4 <sum>:
80483c4: 55 push %ebp
80483c5: 89 e5 mov %esp,%ebp
80483c7: 8b 45 0c mov 0xc(%ebp),%eax
80483ca: 03 45 08 add 0x8(%ebp),%eax
80483cd: 5d pop %ebp
80483ce: c3 ret
• Disassembler
objdump -d p
– Useful tool for examining object code
– Analyzes bit pattern of series of instructions
– Produces approximate rendition of assembly code
– Can be run on either a.out (complete executable) or .o file
KAIST
Alternate Disassembly
Disassembled
Object
0x401040:
0x55 Dump of assembler code for function sum:
0x89 0x080483c4 <sum+0>: push %ebp
0xe5 0x080483c5 <sum+1>: mov %esp,%ebp
0x8b 0x080483c7 <sum+3>: mov 0xc(%ebp),%eax
0x45 0x080483ca <sum+6>: add 0x8(%ebp),%eax
0x0c 0x080483cd <sum+9>: pop %ebp
0x03 0x080483ce <sum+10>: ret
0x45
0x08
0x5d • Within gdb Debugger
0xc3 gdb p
disassemble sum
– Disassemble procedure
x/11xb sum
– Examine the 11 bytes starting at sum
KAIST
What Can be Disassembled?
% objdump -d WINWORD.EXE
No symbols in "WINWORD.EXE".
Disassembly of section .text:
30001000 <.text>:
30001000: 55 push %ebp
30001001: 8b ec mov %esp,%ebp
30001003: 6a ff push $0xffffffff
30001005: 68 90 10 00 30 push $0x30001090
3000100a: 68 91 dc 4c 30 push $0x304cdc91
• Anything that can be interpreted as executable code
• Disassembler examines bytes and reconstructs assembly source
KAIST
Today: Machine Programming I: Basics
• History of Intel processors and architectures
• C, assembly, machine code
• Assembly Basics: Registers, operands, move
• Intro to x86‐64
KAIST
Integer Registers (IA32) Origin
(mostly obsolete)
%eax %ax %ah %al accumulate
source
%esi %si index
destination
%edi %di index
stack
%esp %sp
pointer
base
%ebp %bp
pointer
16‐bit virtual registers
(backwards compatibility)
KAIST
Moving Data: IA32 %eax
• Moving Data %ecx
movl Source, Dest: %edx
• Operand Types %ebx
– Immediate: Constant integer data %esi
• Example: $0x400, $-533
%edi
• Like C constant, but prefixed with ‘$’
• Encoded with 1, 2, or 4 bytes %esp
– Register: One of 8 integer registers %ebp
• Example: %eax, %edx
• But %esp and %ebp reserved for special use
• Others have special uses for particular instructions
– Memory: 4 consecutive bytes of memory at address given by register
• Simplest example: (%eax)
• Various other “address modes”
KAIST
movl Operand Combinations
Source Dest Src,Dest C Analog
Cannot do memory‐memory transfer with a single instruction
KAIST
Simple Memory Addressing
Modes
• Normal (R) Mem[Reg[R]]
– Register R specifies memory address
movl (%ecx),%eax
movl 8(%ebp),%edx
KAIST
Using Simple Addressing Modes
swap:
pushl %ebp
void swap(int *xp, int *yp) Set
movl %esp,%ebp
{ Up
pushl %ebx
int t0 = *xp;
int t1 = *yp;
*xp = t1; movl 8(%ebp), %edx
*yp = t0; movl 12(%ebp), %ecx
} movl (%edx), %ebx Body
movl (%ecx), %eax
movl %eax, (%edx)
movl %ebx, (%ecx)
popl %ebx
popl %ebp Finish
ret
KAIST
Using Simple Addressing Modes
swap:
pushl %ebp
void swap(int *xp, int *yp) Set
movl %esp,%ebp
{ Up
pushl %ebx
int t0 = *xp;
int t1 = *yp;
*xp = t1; movl 8(%ebp), %edx
*yp = t0; movl 12(%ebp), %ecx
} movl (%edx), %ebx Body
movl (%ecx), %eax
movl %eax, (%edx)
movl %ebx, (%ecx)
popl %ebx
popl %ebp Finish
ret
KAIST
Understanding Swap
void swap(int *xp, int *yp) •
{ • Stack
int t0 = *xp; • (in memory)
Offset
int t1 = *yp;
*xp = t1; 12 yp
*yp = t0; 8 xp
}
4 Rtn adr
0 Old %ebp %ebp
-4 Old %ebx %esp
Register Value
%edx xp
%ecx yp
movl 8(%ebp), %edx # edx = xp
%ebx t0
movl 12(%ebp), %ecx # ecx = yp
%eax t1 movl (%edx), %ebx # ebx = *xp (t0)
movl (%ecx), %eax # eax = *yp (t1)
movl %eax, (%edx) # *xp = t1
movl %ebx, (%ecx) # *yp = t0
KAIST
Address
Understanding Swap 123 0x124
456 0x120
0x11c
%eax 0x118
%edx Offset
0x114
%ecx yp 12 0x120 0x110
xp 8 0x124 0x10c
%ebx
4 Rtn adr 0x108
%esi
%ebp 0
0x104
%edi -4
0x100
%esp
movl 8(%ebp), %edx # edx = xp
%ebp 0x104 movl 12(%ebp), %ecx # ecx = yp
movl (%edx), %ebx # ebx = *xp (t0)
movl (%ecx), %eax # eax = *yp (t1)
movl %eax, (%edx) # *xp = t1
movl %ebx, (%ecx) # *yp = t0
KAIST
Address
Understanding Swap 123 0x124
456 0x120
0x11c
%eax 0x118
%edx 0x124 Offset
0x114
%ecx yp 12 0x120 0x110
xp 8 0x124 0x10c
%ebx
4 Rtn adr 0x108
%esi
%ebp 0
0x104
%edi -4
0x100
%esp
movl 8(%ebp), %edx # edx = xp
%ebp 0x104 movl 12(%ebp), %ecx # ecx = yp
movl (%edx), %ebx # ebx = *xp (t0)
movl (%ecx), %eax # eax = *yp (t1)
movl %eax, (%edx) # *xp = t1
movl %ebx, (%ecx) # *yp = t0
KAIST
Address
Understanding Swap 123 0x124
456 0x120
0x11c
%eax 0x118
%edx 0x124 Offset
0x114
%ecx 0x120 yp 12 0x120 0x110
xp 8 0x124 0x10c
%ebx
4 Rtn adr 0x108
%esi
%ebp 0
0x104
%edi -4
0x100
%esp
movl 8(%ebp), %edx # edx = xp
%ebp 0x104 movl 12(%ebp), %ecx # ecx = yp
movl (%edx), %ebx # ebx = *xp (t0)
movl (%ecx), %eax # eax = *yp (t1)
movl %eax, (%edx) # *xp = t1
movl %ebx, (%ecx) # *yp = t0
KAIST
Address
Understanding Swap 123 0x124
456 0x120
0x11c
%eax 0x118
%edx 0x124 Offset
0x114
%ecx 0x120 yp 12 0x120 0x110
xp 8 0x124 0x10c
%ebx 123
4 Rtn adr 0x108
%esi
%ebp 0
0x104
%edi -4
0x100
%esp
movl 8(%ebp), %edx # edx = xp
%ebp 0x104 movl 12(%ebp), %ecx # ecx = yp
movl (%edx), %ebx # ebx = *xp (t0)
movl (%ecx), %eax # eax = *yp (t1)
movl %eax, (%edx) # *xp = t1
movl %ebx, (%ecx) # *yp = t0
KAIST
Address
Understanding Swap 123 0x124
456 0x120
0x11c
%eax 456 0x118
%edx 0x124 Offset
0x114
%ecx 0x120 yp 12 0x120 0x110
xp 8 0x124 0x10c
%ebx 123
4 Rtn adr 0x108
%esi
%ebp 0
0x104
%edi -4
0x100
%esp
movl 8(%ebp), %edx # edx = xp
%ebp 0x104 movl 12(%ebp), %ecx # ecx = yp
movl (%edx), %ebx # ebx = *xp (t0)
movl (%ecx), %eax # eax = *yp (t1)
movl %eax, (%edx) # *xp = t1
movl %ebx, (%ecx) # *yp = t0
KAIST
Address
Understanding Swap 456 0x124
456 0x120
0x11c
%eax 456
456 0x118
%edx 0x124 Offset
0x114
%ecx 0x120 yp 12 0x120 0x110
xp 8 0x124 0x10c
%ebx 123
4 Rtn adr 0x108
%esi
%ebp 0
0x104
%edi -4
0x100
%esp
movl 8(%ebp), %edx # edx = xp
%ebp 0x104 movl 12(%ebp), %ecx # ecx = yp
movl (%edx), %ebx # ebx = *xp (t0)
movl (%ecx), %eax # eax = *yp (t1)
movl %eax, (%edx) # *xp = t1
movl %ebx, (%ecx) # *yp = t0
KAIST
Address
Understanding Swap 456 0x124
123 0x120
0x11c
%eax 456 0x118
%edx 0x124 Offset
0x114
%ecx 0x120 yp 12 0x120 0x110
xp 8 0x124 0x10c
%ebx 123
4 Rtn adr 0x108
%esi
%ebp 0
0x104
%edi -4
0x100
%esp
movl 8(%ebp), %edx # edx = xp
%ebp 0x104 movl 12(%ebp), %ecx # ecx = yp
movl (%edx), %ebx # ebx = *xp (t0)
movl (%ecx), %eax # eax = *yp (t1)
movl %eax, (%edx) # *xp = t1
movl %ebx, (%ecx) # *yp = t0
KAIST
Complete Memory Addressing Modes
• Most General Form
D(Rb,Ri,S) Mem[Reg[Rb]+S*Reg[Ri]+ D]
– D: Constant “displacement” 1, 2, or 4 bytes
– Rb: Base register: Any of 8 integer registers
– Ri: Index register: Any, except for %esp
• Unlikely you’d use %ebp, either
– S: Scale: 1, 2, 4, or 8 (why these numbers?)
• Special Cases
(Rb,Ri) Mem[Reg[Rb]+Reg[Ri]]
D(Rb,Ri) Mem[Reg[Rb]+Reg[Ri]+D]
(Rb,Ri,S) Mem[Reg[Rb]+S*Reg[Ri]]
KAIST
Today: Machine Programming I: Basics
• History of Intel processors and architectures
• C, assembly, machine code
• Assembly Basics: Registers, operands, move
• Intro to x86‐64
KAIST
Data Representations: IA32 + x86‐64
• Sizes of C Objects (in Bytes)
– C Data Type Generic 32‐bit Intel IA32 x86‐64
• unsigned 4 4 4
• int 4 4 4
• long int 4 4 8
• char 1 1 1
• short 2 2 2
• float 4 4 4
• double 8 8 8
• long double 8 10/12 16
• char * 4 4 8
– Or any other pointer
KAIST
x86‐64 Integer Registers
%rax %eax %r8 %r8d
– Extend existing registers. Add 8 new ones.
– Make %ebp/%rbp general purpose
KAIST
Instructions
• Long word l (4 Bytes) ↔ Quad word q (8 Bytes)
• New instructions:
– movl ➙ movq
– addl ➙ addq
– sall ➙ salq
– etc.
• 32‐bit instructions that generate 32‐bit results
– Set higher order bits of destination register to 0
– Example: addl
KAIST
32‐bit code for swap
swap:
pushl %ebp
void swap(int *xp, int *yp) Set
{
movl %esp,%ebp
Up
int t0 = *xp; pushl %ebx
int t1 = *yp;
*xp = t1; movl 8(%ebp), %edx
*yp = t0; movl 12(%ebp), %ecx
} movl (%edx), %ebx Body
movl (%ecx), %eax
movl %eax, (%edx)
movl %ebx, (%ecx)
popl %ebx
popl %ebp Finish
ret
KAIST
64‐bit code for swap
swap:
Set
void swap(int *xp, int *yp) Up
{
int t0 = *xp; movl (%rdi), %edx
int t1 = *yp; movl (%rsi), %eax
Body
*xp = t1; movl %eax, (%rdi)
*yp = t0; movl %edx, (%rsi)
}
ret Finish
• Operands passed in registers (why useful?)
– First (xp) in %rdi, second (yp) in %rsi
– 64‐bit pointers
• No stack operations required
• 32‐bit data
– Data held in registers %eax and %edx
– movl operation
KAIST
64‐bit code for long int swap
swap_l:
Set
void swap(long *xp, long *yp) Up
{
long t0 = *xp; movq (%rdi), %rdx
long t1 = *yp; movq (%rsi), %rax
Body
*xp = t1; movq %rax, (%rdi)
*yp = t0; movq %rdx, (%rsi)
}
ret Finish
• 64‐bit data
– Data held in registers %rax and %rdx
– movq operation
• “q” stands for quad‐word
KAIST
Machine Programming I: Summary
• History of Intel processors and architectures
– Evolutionary design leads to many quirks and artifacts
• C, assembly, machine code
– Compiler must transform statements, expressions, procedures into
low‐level instruction sequences
• Assembly Basics: Registers, operands, move
– The x86 move instructions cover wide range of data movement forms
• Intro to x86‐64
– A major departure from the style of code seen in IA32