CSL373: Operating Systems Linking
CSL373: Operating Systems Linking
CSL373: Operating Systems Linking
Linking
Today
• Linking
ld a.out
c.c gcc c.s as c.o
1234: 0
a: 0 1238: 0
b: 0 main (1300):
long a, b;
main: 110, 23,34,
int main() { gcc as
mov b, r2 …,
a = b + 1;
add r1, r2, 1 21,0,0,0,0,
print(a);
mov r1, a …
}
push a
a.c call print patches:
ret 1343print
a.s a.o
called “relocations” (stored in symbol table)
Perspectives on information in
memory
• Programming language view:
• instructions: specify operations to perform
• variables: operands that can change over time
• constants: operands that never change
• Address versus data
• Addresses used to locate something: if you move it, must
update address
• Examples: linkers, garbage collectors, changing apartment
• Binding time: when is a value determined/computed?
• Compile time
• Link time early to late
• Run time
How is a process specified?
• shell$ ./a.out
– A process is created from an executable
foo:
ret
Linker: object files executable
external ref
“foo.o”
Header: code/data size, code=110
symbol table offset data=8, ...
Object code: instructions 0 foo:
and data gen‟d by compiler call 0
Symbol table: ret
bar:
external defs 40 ret
(exported objects in file)
l: “hello world\n”
external refs
foo: 0: T
(global symbols used in file) bar: 40: t
4: printf
How is a process created?
• On Unix systems, read by “loader”
Compile time runtime
ld loader Cache
reads all code/data segs into buffer cache; maps code (read
only) and initialized data (r/w) into addr space
fakes process state to look like switched out
• Big optimization fun:
Zero-initialized data does not need to be read in
Demand load: wait until code used before get from disk
Copies of same program running? Share code
Multiple programs use same routines: share code (harder)
What does a process look like? (Unix)
• Process address space divided into “segments”
text (code), data, heap (dynamic data), and stack
address 2^n-1
stack
heap
initialized data address >= 0
code
Why? (1) different allocation patterns;
(2) separate code/data
Who builds what?
• Heap: constructed and layout by allocator
(malloc)
Compiler, linker not involved other than saying
where it can start
Namespace constructed dynamically and managed
by programmer (names stored in pointers, and
organized as data structures)
OS provides sbrk() system call to allocate a new
chunk of memory for the heap (called internally
by malloc()).
Who builds what?
• Stack: allocated dynamically (proc call), layout by
compiler
names are relative off stack pointer
managed by compiler (alloc on proc entry, dealloc on
exit)
linker not involved because name space entirely local:
compiler has enough information to build it.
0 foo: 4000
printf:
...
printf.o bar: 4040
30 printf: 4080
Problem 2: where is everything? (ref)
• How to call procedures or reference variables?
e.g., call to printf needs a target addr
compiler places a 0 for the address
Emits an external reference telling the linker the
instruction’s offset and the symbol it needs
0 foo:
call 0
ret
40 bar:
…
ret
foo: 0: T
bar: 40: t
4: printf
0 foo: 4000
printf:
...
printf.o bar: 4040
30 printf: 4080
Example: two modules and C lib
main.c: math.c:
extern float sin(); float sin(float x) {
extern int printf(), scanf(); float tmp1, tmp2;
float val; static float res;
main() { static float lastx;
static float x; if(x != lastx) {
printf(“enter number”); lastx = x;
scanf(“%f”, &x); … compute sin(x)…
val = sin(x); }
printf(“Sine is %f”, val); return res;
} }
C library:
int scanf(char *fmt, …) { … }
int printf(char *fmt, …) { … }
Initial object files
Main.o:
Math.o:
def: val @ 0:D symbols
symbols
def: main @ 0:T
def: sin @0:T
def: x @ 4:d
def: res @ 0:d
relocation
def: lastx @4:d
ref: printf @ 8:T,12:T
relocation
ref: scanf @ 4:T
ref: lastx@0:T,4:T
ref: x @ 4:T, 8:T
ref res @24:T
ref: sin @ ?:T
ref: val @ ?:T, ?:T
0 res: data
0 x:
data 4 lastx:
4 val:
0 if(x != lastx) text
0 call printf
4 lastx = x;
4 call scanf(&x) text
… … compute sin(x)…
8 val = call sin(x)
24 return res;
12 call printf(val)
Pass 1: Linker reorganization
a.out: Starting virtual addr: 4000
symbol table Symbol table:
data starts @ 0
0 val: text starts @ 16
4 x: def: val @ 0
8 res: def: x @ 4
data def: res @ 8
12 lastx:
def: main @ 16
16 main: …
… … ref: printf @ 26
26 call printf(val) ref: res @ 50
30 sin: …
… … (what are some other refs?)
50 return res; text
64 printf: …
80 scanf: …
Pass 2: relocation (insert virtual addrs)
“final” a.out:
Starting virtual addr: 4000
symbol table Symbol table:
data starts 4000
0 val: 4000 text starts 4016
4 x: 4004 def: val @ 0
8 res: 4008 def: x @ 4
12 lastx: data 4012 def: res @ 8
def: main @ 14
16 main: 4016 def: sin @ 30
… … … def: printf @ 64
26 call ??(??) //printf(val) 4026 def: scanf @80
30 sin: 4030 …
text
… … … (usually don’t keep refs,
50 return load ??; // res 4050 since won’t relink. Defs
64 printf: … 4064 are for debugger: can
80 scanf: … 4080 be stripped out)
What gets written out
a.out:
virtual addr: 4016
symbol table
Symbol table:
16 main: … 4016 initialized data = 4000
… … … uninitialized data = 4000
26 call 4064(4000) 4026 text = 4016
30 sin: … 4030 def: val @ 0
… … … def: x @ 4
50 return load 4008; 4050 def: res @ 8
64 printf: … 4064 def: main @ 14
80 scanf: … 4080 def: sin @ 30
def: printf @ 64
def: scanf @80
printf: printf:
scanf: scanf:
... ...
0xfff0000 math.a
…
Linker links program against lib (why?) but does not bring in
actual code
Loader marks shared lib region as unreadable
When process calls lib code, seg faults: enclosed linker brings in
lib code from known place & maps it in.
So? Different running programs can now share code!
Linking variation 2: dynamic shared
libs
• Problem: static shared libraries require system-
wide pre-allocation of address space: clumsy
We want to link code anywhere in address space
• Problem 1: linker won’t know how to resolve refs
do resolution at runtime
link in stubs that know where to get code from
program calls stub, goes and gets code
ls
“/usr/shrd-lib/libc.a” gcc
4500 9000
printf: libc.a
... printf_stub:
printf_stub:
sscanf: scanf_stub:
scanf_stub:
... ...
...
Problem 2: Dynamic shared libraries
• Code must simultaneously run at different locations!
• Solution: make lib code “position independent” (re-entrant)
– Refer to routines, data using relative addressing (base + constant
offset) rather than absolute addresses
0xf00 printf: 0x0
printf:
... libc.a ...
call 0xf44 call libc_base+0x44
0xf44 write: 0x44 write:
… …
... ...
• Example:
– Internal call “call 0xf44” becomes “call lib_base + 0x44”
– “lib_base” contains the base address of library (private to each
process) and 0x44 is the called-routine’s internal offset
Code = data, data = code
• No inherent difference between code and data
– Code is just something that can be run through a CPU without
causing an “illegal instruction fault”
– Can be written/read at runtime just like data (dynamically-
generated code)