Object file và ELF¶
Bài này mở file hello.o ra xem bên trong có gì. Đây là kiến thức nền để hiểu linker làm gì ở bài 3 và loader làm gì ở bài 4.
Ví dụ dùng lại hello.c và math.c ở bài 1.
ELF có hai "góc nhìn"¶
ELF (Executable and Linkable Format) là định dạng chung cho object file, executable, shared library và core dump trên Linux. Cùng một file ELF được nhìn theo hai cách:
| Linking view | Execution view | |
|---|---|---|
| Đơn vị | Section | Segment (program header) |
| Ai dùng | Linker, objdump, readelf -S |
Kernel/loader, readelf -l |
| Có trong | .o, executable, .so |
executable, .so (không cần trong .o) |
File .o chỉ có section. Executable có cả hai: loader dùng segment để biết cần map gì vào memory, còn section vẫn được giữ lại để phục vụ debug và phân tích (xem bài 4).
.o (relocatable) executable
┌──────────────────┐ ┌──────────────────┐
│ ELF header │ │ ELF header │
│ Section headers │ │ Program headers │ ◀─ execution view
│ .text .data ... │ │ .text .data ... │
│ .symtab .rela.* │ │ Section headers │ ◀─ linking view
└──────────────────┘ └──────────────────┘
Section¶
readelf -SW hello.o (rút gọn):
$ readelf -SW hello.o
[Nr] Name Type Off Size Flg
[ 1] .text PROGBITS 000040 00004b AX
[ 2] .rela.text RELA 0001f8 000060 I
[ 3] .data PROGBITS 00008b 000000 WA
[ 4] .bss NOBITS 00008b 000000 WA
[ 5] .rodata PROGBITS 000090 000018 A
[ 6] .comment PROGBITS 0000a8 00002e MS
[ 9] .eh_frame PROGBITS 0000f8 000038 A
[11] .symtab SYMTAB 000130 0000a8
[12] .strtab STRTAB 0001d8 00001c
| Section | Nội dung | Flag |
|---|---|---|
.text |
machine code của các hàm | AX (alloc, execute) |
.rodata |
hằng, chuỗi literal | A |
.data |
biến global/static đã khởi tạo giá trị khác 0 | WA (write, alloc) |
.bss |
biến global/static chưa khởi tạo hoặc bằng 0; chỉ lưu kích thước, không chiếm chỗ trong file (type NOBITS) |
WA |
.symtab / .strtab |
bảng symbol và bảng tên | |
.rela.text |
relocation cho .text |
|
.eh_frame |
thông tin unwind stack (exception, backtrace) | A |
.comment |
phiên bản compiler |
Có thể xem nội dung .rodata của hello.o:
$ objdump -s -j .rodata hello.o
Contents of section .rodata:
0000 61726561 203d2025 660a0000 00000000 area = %f.......
0010 6e861bf0 f9210940 n....!.@
16 byte đầu là chuỗi "area = %f\n"; 8 byte sau là hằng 3.14159 ở dạng double (IEEE 754), do compiler đưa vào chứ không phải do ta khai báo thành biến.
size cho cái nhìn tổng quát về ba vùng chính:
size gộp section
Cột text của size gồm .text, .rodata và vài section chỉ đọc khác, không chỉ riêng .text. Với hello.o: .text (0x4b) + .rodata (0x18) + .eh_frame (0x38) + .note.gnu.property (0x20) = 187.
Biến nằm ở section nào¶
int a = 5; // .data
int b; // .bss
int c = 0; // .bss (khởi tạo bằng 0 được tối ưu vào .bss)
const int d = 7; // .rodata
static int e = 9; // .data (nhưng symbol là LOCAL)
void f(void) {
int x; // không ở section nào: stack, tồn tại lúc chạy
static int y; // .bss
char *p = "hi"; // p: stack; chuỗi "hi": .rodata
}
Biến local thường (automatic) không nằm trong object file, vì chúng chỉ tồn tại trên stack lúc hàm chạy.
Symbol table¶
readelf -sW hello.o:
$ readelf -sW hello.o
Num: Value Size Type Bind Vis Ndx Name
1: 00000000 0 FILE LOCAL DEFAULT ABS hello.c
2: 00000000 0 SECTION LOCAL DEFAULT 1 .text
3: 00000000 0 SECTION LOCAL DEFAULT 5 .rodata
4: 00000000 75 FUNC GLOBAL DEFAULT 1 main
5: 00000000 0 NOTYPE GLOBAL DEFAULT UND square
6: 00000000 0 NOTYPE GLOBAL DEFAULT UND printf
Các cột đáng chú ý:
- Ndx: section chứa symbol.
UND(undefined) nghĩa là symbol này được dùng nhưng nằm ở file khác: đây là "món nợ" mà linker phải trả.mainở section 1 (.text) nên là symbol được định nghĩa. - Bind:
LOCAL(chỉ thấy trong file này, ví dụ biến/hàmstatic),GLOBAL(cho file khác thấy),WEAK(xem bài 3). - Type:
FUNC,OBJECT(biến),SECTION,FILE,NOTYPE. - Size:
maindài 75 byte (0x4b), bằng kích thước.text.
nm là cách xem gọn hơn của cùng bảng này. Mỗi ký tự là loại symbol, chữ hoa là global, chữ thường là local:
| Ký tự | Ý nghĩa |
|---|---|
T / t |
.text |
D / d |
.data |
B / b |
.bss |
R / r |
.rodata |
U |
undefined |
W / w |
weak |
$ nm a.o
0000000000000000 D counter # int counter = 1; global, .data
0000000000000000 T get_hidden
0000000000000004 d hidden # static int hidden = 5; local, .data
Tìm hiểu nhanh một symbol
nm -C file.o giải mã tên C++ (name mangling); nm -S --size-sort file.o sắp theo kích thước, hữu ích để tìm hàm/biến "ngốn" chỗ nhất.
Relocation¶
Assembler không biết địa chỉ cuối cùng của symbol, nên với mỗi chỗ cần địa chỉ nó để trống và ghi một relocation entry:
$ readelf -rW hello.o
Relocation section '.rela.text' at offset 0x1f8 contains 4 entries:
Offset Type Symbol's Name + Addend
000000000000000e R_X86_64_PLT32 square - 4
000000000000001e R_X86_64_PC32 .rodata + c
0000000000000033 R_X86_64_PC32 .rodata - 4
0000000000000040 R_X86_64_PLT32 printf - 4
Mỗi entry gồm: offset (vị trí cần vá trong .text), symbol (cần địa chỉ của gì), type (cách tính giá trị) và addend (hằng số cộng thêm).
- Hai entry
R_X86_64_PLT32ứng với hai lệnhcalltớisquarevàprintf(đã thấye8 00 00 00 00ở bài 1). - Hai entry
R_X86_64_PC32trỏ vào.rodata: entry ở offset0x1e(.rodata + 0xc, tức đích là.rodata + 0x10sau khi bù addend) là hằng3.14159, entry ở offset0x33(.rodata - 4, đích là.rodata + 0) là chuỗi"area = %f\n". Đây là PC-relative: giá trị điền vào là khoảng cách từ lệnh đến đích, nên code không phụ thuộc vị trí tuyệt đối. - Addend
-4có mặt vì CPU tính offset từ lệnh kế tiếp, tức sau 4 byte operand.
Công thức cho kiểu PC-relative: giá trị = S + A - P, với S là địa chỉ symbol, A là addend, P là địa chỉ của chỗ đang vá. Ví dụ với call square ở bài 3: S = 0x1194, A = -4, P = 0x1157 cho ra 0x1194 - 4 - 0x1157 = 0x39, đúng giá trị 39 00 00 00 trong executable.
Mỗi kiến trúc có bộ relocation type riêng (R_ARM_CALL, R_AARCH64_CALL26, ...). Đây là lý do object file của x86-64 không thể link với object file ARM.
Tóm tắt¶
- Object file là ELF relocatable, chỉ có section; executable thêm program header cho loader.
- Section chính:
.text(code),.rodata,.data,.bss(không chiếm chỗ trong file). - Symbol table cho biết file định nghĩa gì (
T,D, ...) và cần gì (U). - Relocation entry đánh dấu những chỗ linker phải điền địa chỉ thật.
Tiếp theo: Linking.
References¶
man elf,man readelf,man nm- System V ABI / ELF specification
- Randal E. Bryant, David R. O'Hallaron - Computer Systems: A Programmer's Perspective, mục 7.4-7.7