실행 파일(Executable Image)을 만들기 위한 링커(Linker)의 역할
1. Merge segments(세그먼트 병합)
2. Resolve Labels(심볼 주소 결정)
3. Patch location-dependent and externel references(주소 종속 및 외부 참조 수정)
명령어 중 일부는 메모리 주소에 따라 변하는데, 이걸 실제 주소로 수정한다.
다른 파일에 정의된 함수/변수를 참조할 경우 그 위치도 패치한다.
이전에는 링커가 모든 위치 종속을 고쳐야 했지만, Relocating Loader가 일부를 맡을 수도 있었음
하지만 오늘날에는 가상 메모리(Virtual Memory) 덕분에, 프로그램은 고정된 가상 주소에 로드할 수 있어서 이 작업이 단순해졌다.
디스크에 저장된 실행 이미지를 메모리에 적재하여 실행 가능하게 만드는 과정
1. Read header to determine segment sizes
2. Create virtual address space
3. Copy segments into memory(or use lazy loading)
4. Set up stack with program arguments
5. Initialize registers
6. Jump to startup routine
런타임 초기화 코드로 점프
여기서 main(argc, argv)를 호출한다.
main()이 종료되면 시스템 호출을 통해 프로세스를 종료시킨다.
시작 루틴(Startup routine): C언어 기준으로 _start() 같은 함수가 여기에 해당한다. 라이브러리 초기화도 여기서 이루어진다.
.bss 세그먼트는 초기화된 데이터가 없기 때문에 실제로 메모리에서 0으로 초기화만 된다.
라이브러리를 실행 중 필요한 시점에 링크하여 사용하는 기법
Link/load only when needed : 함수가 호출될 때 해당 라이브러리를 로딩(예: dlopen(), dlsym() 등 사용)
Requires relocatable code : 메모리 주소가 달라져도 실행 가능하도록 코드가 작성되어야 한다.(PIC: Position-Independent Code)
Avoids image blaot : 정적 링킹(static linking)은 참조된 모든 코드가 실행 파일에 포함되어 커지지만, 동적 링킹은 필요할 때만 로딩하여 크기를 절약한다.
Automatic library version updates : 시스템에 설치된 최신 버전의 라이브러리를 자동 사용. 보안 패치 적용에도 유리하다.
.so(shared object, 유닉스) 또는 .dll(windows) 파일이 동적 라이브러리이다.
OS의 dynamic linker/loader가 실행 중에 해당 라이브러리를 찾고 메모리에 로딩한다.
단점 : 실행 중 라이브러리가 없거나 버전이 맞지 않으면 오류 발생 가능
전체 흐름 요약

void swap(int v[], int k) {
int temp;
temp = v[k];
v[k] = v[k+1];
v[k+1] = temp;
}
void sort(int v[], int n) {
int i, j;
for (i = 0; i < n; i++) {
for (j = i-1; j >= 0 && v[j] > v[j+1]; j--) {
swap(v, j);
}
}
}
void sort(int v[], int n) {
int i, j;
for (i = 0; i < n; i++) {
for (j = i-1; j >= 0 && v[j] > v[j+1]; j--) {
swap(v, j);
}
}
}
swap:
sll $t1, $a1, 2 # $t1 = k * 4 (int는 4바이트니까 offset 계산)
add $t1, $a0, $t1 # $t1 = &v[k]
lw $t0, 0($t1) # temp = v[k]
lw $t2, 4($t1) # $t2 = v[k+1]
sw $t2, 0($t1) # v[k] = v[k+1]
sw $t0, 4($t1) # v[k+1] = temp
jr $ra # return
# Move params
move $s2, $a0 # save v[] in $s2
move $s3, $a1 # save n in $s3
move $s0, $zero # i = 0
for1tst: # outer loop
slt $t0, $s0, $s3 # if i < n -> $t0 = 1
beq $t0, $zero, exit1 # if i >= n, go to exit1
addi $s1, $s0, -1 # j = i - 1
for2tst: # inner loop
slti $t0, $s1, 0 # if j < 0 -> $t0 = 1
bne $t0, $zero, exit2 # if j < 0, exit inner loop
sll $t1, $s1, 2 # t1 = j * 4
add $t2, $s2, $t1 # t2 = &v[j]
lw $t3, 0($t2) # t3 = v[j]
lw $t4, 4($t2) # t4 = v[j+1]
slt $t0, $t4, $t3 # if v[j+1] < v[j]
beq $t0, $zero, exit2 # if not, exit inner loop
# pass params & call
move $a0, $s2 # pass v[] to swap
move $a1, $s1 # pass j to swap
jal swap # call swap(v, j)
# inner loop
addi $s1, $s1, -1 # j--
j for2tst # repeat inner loop
exit2: # outer loop
addi $s0, $s0, 1 # i++
j for1tst # repeat outer loop
sort:
addi $sp, $sp, -20 # make room on stack for 5 registers
sw $ra, 16($sp) # save $ra on stack
sw $s3, 12($sp) # save $s3 on stack
sw $s2, 8($sp) # save $s2 on stack
sw $s1, 4($sp) # save $s1 on stack
sw $s0, 0($sp) # save $s0 on stack
# ... procedure body ...
exit1:
lw $s0, 0($sp) # restore $s0 from stack
lw $s1, 4($sp) # restore $s1 from stack
lw $s2, 8($sp) # restore $s2 from stack
lw $s3, 12($sp) # restore $s3 from stack
lw $ra, 16($sp) # restore $ra from stack
addi $sp, $sp, 20 # restore stack pointer
jr $ra # return to calling routine