Object file (file .o) adalah hasil tahap assembling — file biner berformat ELF yang berisi instruksi mesin, tetapi belum siap dijalankan karena alamat simbol masih perlu diselesaikan oleh linker. Memahami struktur object file penting karena pesan error seperti undefined reference to 'foo' atau multiple definition of 'bar' berasal dari tahap linking yang memeriksa simbol-simbol di file .o. Pada chapter ini kita akan membedah sebuah object file dengan file, nm, dan objdump untuk melihat section, symbol table, dan relocation entry.
Membuat Object File #
Kode C berikut memiliki dua fungsi global (tambah, cetak), satu fungsi static (kali), satu variabel global (global_counter), dan satu variabel static (internal_state):
#include <stdio.h>
int tambah(int a, int b) {
return a + b;
}
static int kali(int a, int b) {
return a * b;
}
int global_counter = 42;
static int internal_state = 7;
void cetak(void) {
printf("counter=%d state=%d jumlah=%d\n",
global_counter, internal_state, kali(2, 3));
}Kompilasi menjadi object file dengan flag -c:
gcc -std=c11 -Wall -Wextra -c math_ops.c -o math_ops.o
file math_ops.oOutput yang Dihasilkan:
math_ops.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not strippedKata kunci relocatable adalah penanda bahwa file ini adalah object file — alamat-alamat di dalamnya belum absolut dan perlu direlokasi oleh linker. Kebalikannya, executable akan dilabeli executable, dan shared library shared object.
Symbol Table dengan nm #
Perintah nm menampilkan symbol table sebuah object file atau executable. Symbol table adalah daftar semua nama (fungsi, variabel) yang didefinisikan atau dirujuk di file tersebut:
nm math_ops.oOutput yang Dihasilkan:
0000000000000027 T cetak
0000000000000000 D global_counter
0000000000000004 d internal_state
0000000000000014 t kali
U printf
0000000000000000 T tambahMari kita telaah baris-baris output nm di atas:
- Baris
0000000000000027 T cetak: simbolcetakadalah fungsi global (huruf besarT= text section, huruf besar = global/external) pada offset0x27di section.text. - Baris
0000000000000000 D global_counter: simbolglobal_counteradalah variabel global terinisialisasi (hurufD= data section, huruf besar = global) pada offset0x0di section.data. - Baris
0000000000000004 d internal_state: simbolinternal_stateadalah variabelstatic(huruf kecild= local data section) pada offset0x4. - Baris
0000000000000014 t kali: simbolkaliadalah fungsistatic(huruf kecilt= local text). Karenastatic,kalitidak terlihat dari object file lain — inilah mekanisme file-scope encapsulation di C. - Baris
U printf: simbolprintfberlabelU(undefined) — dirujuk tetapi tidak didefinisikan di file ini. Linker harus mencarinya di file lain (misallibc.so). - Baris
0000000000000000 T tambah: fungsi globaltambahpada offset0x0di section.text.
Section dengan objdump -h #
Object file ELF dibagi menjadi beberapa section. Setiap section menyimpan kategori data tertentu. Perintah objdump -h menampilkan daftar section:
objdump -h math_ops.oOutput yang Dihasilkan:
math_ops.o: file format elf64-x86-64
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000061 0000000000000000 0000000000000000 00000040 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000008 0000000000000000 0000000000000000 000000a4 2**2
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 0000000000000000 0000000000000000 000000ac 2**0
ALLOC
3 .rodata 0000001f 0000000000000000 0000000000000000 000000b0 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .comment 00000020 0000000000000000 0000000000000000 000000cf 2**0
CONTENTS, READONLY
5 .note.GNU-stack 00000000 0000000000000000 0000000000000000 000000ef 2**0
CONTENTS, READONLY
6 .eh_frame 00000078 0000000000000000 0000000000000000 000000f0 2**3
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATASection penting yang perlu kita pahami:
.text(97 byte): instruksi mesin kode program. SifatnyaREADONLY, CODE— tidak bisa ditulis saat runtime..data(8 byte): variabel global terinisialisasi (global_counter4 byte +internal_state4 byte = 8 byte). SifatnyaDATA— bisa dibaca dan ditulis..bss(0 byte): variabel global tidak terinisialisasi (misalint x;di luar fungsi). Ukuran 0 di file karena BSS dialokasikan saat runtime oleh OS, menghemat ukuran binary..rodata(31 byte): literal string format"counter=%d state=%d jumlah=%d\n". SifatnyaREADONLY— menulis di section ini memicu segfault..eh_frame(120 byte): tabel exception handling untuk stack unwinding — dipakai oleh runtime saat melempar sinyal atau menjalankanlongjmp.
Disassembly dengan objdump -d #
Perintah objdump -d melakukan disassembly pada section .text, menampilkan instruksi mesin beserta kode heksadesimalnya:
objdump -d math_ops.o | head -25Output yang Dihasilkan:
math_ops.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <tambah>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 89 7d fc mov %edi,-0x4(%rbp)
7: 89 75 f8 mov %esi,-0x8(%rbp)
a: 8b 55 fc mov -0x4(%rbp),%edx
d: 8b 45 f8 mov -0x8(%rbp),%eax
10: 01 d0 add %edx,%eax
12: 5d pop %rbp
13: c3 ret
0000000000000014 <kali>:
14: 55 push %rbp
15: 48 89 e5 mov %rsp,%rbp
18: 89 7d fc mov %edi,-0x4(%rbp)
1b: 89 75 f8 mov -0x8(%rbp),%esi
1e: 8b 45 fc mov -0x4(%rbp),%eax
21: 0f af 45 f8 imul -0x8(%rbp),%eax
25: 5d pop %rbp
26: c3 retPerhatikan kolom paling kiri (0:, 1:, 4:, dst.) — angka-angka tersebut adalah offset byte di section .text, bukan alamat absolut. Karena ini adalah object file relocatable, alamat 0x0 hanyanya offset sementara yang akan direlokasi saat linking. Instruksi push %rbp; mov %rsp,%rbp adalah prolog fungsi standar System V AMD64 ABI, dan pop %rbp; ret adalah epilog.
Relocation dengan readelf -r #
Object file berisi relocation entry — catatan untuk linker tentang lokasi mana di kode mesin yang perlu diisi dengan alamat simbol saat linking. Perintah readelf -r menampilkan daftar tersebut:
readelf -r math_ops.oOutput yang Dihasilkan:
Relocation section '.rela.text' at offset 0x2b8 contains 4 entries:
Offset Info Type Sym. Value Sym. Name + Addend
00000000003e 000300000002 R_X86_64_PC32 0000000000000000 .data + 0
000000000044 000800000002 R_X86_64_PC32 0000000000000000 global_counter - 4
00000000004d 000600000002 R_X86_64_PC32 0000000000000000 .rodata - 4
00000000005a 000a00000004 R_X86_64_PLT32 0000000000000000 printf - 4
Relocation section '.rela.eh_frame' at offset 0x318 contains 3 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000000020 000200000002 R_X86_64_PC32 0000000000000000 .text + 0
000000000040 000200000002 R_X86_64_PC32 0000000000000000 .text + 14
000000000060 000200000002 R_X86_64_PC32 0000000000000000 .text + 27Mari kita telaah relocation utama di section .text:
- Baris pertama (
0x3e → .data + 0): pada offset0x3edi section.text(di dalam fungsicetak), linker harus menulis alamat section.data(untuk membacainternal_state). - Baris kedua (
0x44 → global_counter - 4): pada offset0x44, linker harus menulis alamat simbolglobal_counterminus 4 byte. Addend-4muncul karena encoding instruksimov 0x0(%rip),%edxmenghitung alamat relatif terhadap akhir instruksi, bukan awalnya. - Baris ketiga (
0x4d → .rodata - 4): alamat format string di.rodata(untuk argumen pertamaprintf) akan diisi pada offset0x4d. - Baris keempat (
0x5a → printf - 4): pemanggilanprintfdi-encode sebagaicall printf@PLT. TipeR_X86_64_PLT32menandakan ini adalah panggilan melalui Procedure Linkage Table — mekanisme lazy binding untuk shared library.