Bài 39.528 phút đọc
Đọc bảng ký hiệu
Sau bài này bạn sẽ làm được
- Đọc được ý nghĩa của T, t, D, B, R, U, W
- Tìm ký hiệu một chương trình cần từ bên ngoài
- Xem danh sách thư viện mà một tệp nhị phân phụ thuộc
- Dò được một lỗi undefined reference tới đúng tệp đối tượng
Bảng ký hiệu là danh bạ của một tệp nhị phân: nó định nghĩa những gì và nó cần những gì. Đọc được bảng đó thì phần lớn lỗi liên kết trở thành chuyện mười giây, thay vì một buổi chiều.
#Bảy chữ cái
terminal
nm build/libmath.a
hinhhoc.o: 000000000000002b T cv_hinh_tron 0000000000000000 T dt_hinh_tron sohoc.o: 0000000000000012 T giai_thua 0000000000000000 t tru 000000000000004c T uoc_chung_lon_nhat
| Chữ | Phân đoạn | Nghĩa |
|---|---|---|
| T | .text | Hàm công khai, chương trình khác gọi được |
| t | .text | Hàm khai báo static, chỉ tệp đó dùng |
| D | .data | Biến toàn cục có giá trị khởi tạo khác 0 |
| d | .data | Biến static có giá trị khởi tạo khác 0 |
| B | .bss | Biến toàn cục khởi tạo bằng 0 hoặc không khởi tạo |
| b | .bss | Biến static tương ứng |
| R | .rodata | Hằng số công khai, vùng chỉ đọc |
| r | .rodata | Hằng số static |
| U | không có | CẦN từ nơi khác, chưa định nghĩa |
| W | yếu | Ký hiệu yếu, định nghĩa khác đè lên được |
| C | chung | Ký hiệu chung, biến khai báo mà chưa gán, kiểu cũ |
| A | tuyệt đối | Giá trị cố định, không dời được |
Ví dụ đủ mọi loại
/* kyhieu.c */
int g_dem = 5; /* D: toan cuc, khoi tao khac 0 */
int g_tong; /* B hoac C, xem chu thich duoi */
static int s_rieng = 7; /* d: static, khoi tao khac 0 */
static int s_khong; /* b: static, bang 0 */
const char *const TEN = "abc"; /* R: hang cong khai */
static const int BANG[] = {1,2}; /* r: hang static */
void ham_cong_khai(void) { } /* T */
static void ham_rieng(void) { } /* t */
extern void ham_o_noi_khac(void); /* U: can tu ngoai */
void f(void) { ham_o_noi_khac(); ham_rieng(); }terminal
gcc -c kyhieu.c -o kyhieu.o && nm kyhieu.o | sort -k3
U ham_o_noi_khac 0000000000000010 r BANG 0000000000000008 R TEN 0000000000000004 T f 0000000000000000 D g_dem 0000000000000004 C g_tong 0000000000000000 T ham_cong_khai 0000000000000002 t ham_rieng 0000000000000000 b s_khong 0000000000000004 d s_rieng
#nm và các cờ hay dùng
nm file.o # moi ky hieu
nm -g file.o # chi ky hieu TOAN CUC, tuc chu hoa
nm --defined-only file.o # bo cac ky hieu U
nm -u file.o # CHI cac ky hieu U
nm -C file.o # giai ma ten C++
nm -S file.o # kem KICH THUOC cua tung ky hieu
nm -n file.o # sap theo dia chi
nm --size-sort file.o # sap theo kich thuoc
nm -A *.o # kem ten tep o dau moi dong
nm -D lib.so # bang ky hieu DONG, cho .so# Tim ham to nhat trong thu vien, huu ich khi giam kich thuoc
$ nm --size-sort -S --radix=d libmath.a | tail -5
0000000000000112 T uoc_chung_lon_nhat
0000000000000156 T giai_thua
0000000000000284 T dt_hinh_tron
# --radix=d in kich thuoc he muoi thay vi he muoi sau
# Va tren ban thuc thi, tim phan doan nao chiem cho:
$ size app
text data bss dec hex filename
9241 644 24 9909 26b5 app#Xem phụ thuộc
terminal
# Trên Windows
objdump -p app_dyn.exe | grep 'DLL Name'
DLL Name: libmath.dll DLL Name: KERNEL32.dll DLL Name: msvcrt.dll
# Linux
ldd app # danh sach va DUONG DAN THAT SU duoc nap
objdump -p app | grep NEEDED # danh sach ghi trong tep, khong giai duong dan
readelf -d app # ca phan doan dong
$ ldd app
linux-vdso.so.1 (0x00007ffd8f3f1000)
libmath.so => /home/a/duan/build/libmath.so (0x00007f2b1c000000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2b1bc00000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2b1c200000)
# macOS
otool -L app
# Windows
objdump -p app.exe | grep 'DLL Name'
dumpbin /dependents app.exe # neu co Visual Studio#Dò một lỗi undefined reference
Quy trình bốn bước
# Trieu chung:
# undefined reference to 'sv_them'
# Buoc 1: ky hieu do co ton tai o dau khong?
nm -A build/*.o build/*.a 2>/dev/null | grep sv_them
# Neu KHONG ra gi -> ban chua dich tep chua ham do
# Neu ra " U sv_them" -> chi co noi CAN, khong co noi DINH NGHIA
# Neu ra " t sv_them" -> ham do la static, do la nguyen nhan
# Neu ra " T sv_them" -> co day du, van de la THU TU lien ket
# Buoc 2: neu co T, kiem tra thu tu
gcc main.c -Lbuild -lsv -o app # thu vien SAU main.c
# Buoc 3: xem trinh lien ket mo nhung tep nao
gcc main.c -Lbuild -lsv -Wl,-t -o app
# Buoc 4: theo dau mot ky hieu cu the
gcc main.c -Lbuild -lsv -Wl,--trace-symbol=sv_them -o appterminal
gcc main.c -Lbuild -lmath -Wl,--trace-symbol=dt_hinh_tron -o app
C:\Users\DELL\AppData\Local\Temp\cc0BGh3a.o: reference to dt_hinh_tron build\libmath.a(hinhhoc.o): definition of dt_hinh_tron
| Thông báo | Nguyên nhân thường gặp nhất |
|---|---|
| undefined reference to `f' | Thiếu tệp .o, sai thứ tự -l, hoặc f là static ở tệp khác |
| undefined reference to `f(int)' | Tệp C được dịch bằng g++; cần extern "C" |
| multiple definition of `g' | Định nghĩa biến trong header, xem Bài 20.2 |
| cannot find -lfoo | Sai đường dẫn -L, hoặc tên tệp thiếu tiền tố lib |
| relocation R_X86_64_PC32 ... recompile with -fPIC | Quên -fPIC khi tạo thư viện chia sẻ |
| DSO missing from command line | Phụ thuộc bắc cầu, cần thêm -l cho thư viện tầng dưới |
#Bốn công cụ và chỗ mạnh của từng cái
| Công cụ | Mạnh ở | Lệnh đáng nhớ |
|---|---|---|
| nm | Bảng ký hiệu, nhanh và gọn | nm -A *.o | grep ten |
| objdump | Mọi thứ: hợp ngữ, phân đoạn, phụ thuộc, dời chỗ | objdump -d -M intel, objdump -p |
| readelf | Chỉ ELF, nhưng chi tiết nhất về cấu trúc tệp | readelf -d, readelf -S, readelf -h |
| size | Kích thước từng phân đoạn, một dòng | size -A app |
| strings | Chuỗi văn bản trong tệp nhị phân | strings app | grep -i version |
| strip | Xóa ký hiệu gỡ lỗi để giảm kích thước | strip --strip-unneeded lib.so |
# Bo lenh kiem tra mot thu vien truoc khi phat hanh
echo "=== ky hieu xuat ra ==="
nm -D --defined-only libmath.so | grep -E ' [TDBR] ' | wc -l
echo "=== ky hieu can tu ngoai ==="
nm -D -u libmath.so | wc -l
echo "=== phu thuoc ==="
objdump -p libmath.so | grep NEEDED
echo "=== kich thuoc tung phan doan ==="
size -A libmath.so | head -8
echo "=== co con thong tin go loi khong ==="
readelf -S libmath.so | grep debug | wc -l
echo "=== SONAME ==="
objdump -p libmath.so | grep SONAMETự làm thử
- Viết tệp có đủ mọi loại ký hiệu và đối chiếu
nmvới bảng bảy chữ cái. - Dùng
nm -uđể liệt kê thứ một chương trình cần từ bên ngoài. - Chạy
striprồi sonmvớinm -D. - Tạo một lỗi
undefined referencerồi dò theo quy trình bốn bước. - Dịch một tệp C bằng
g++và đọc thông báo lỗi tên biến đổi. - Viết kịch bản kiểm tra thư viện trước khi phát hành.
Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.
Tóm tắt
- Chữ hoa là toàn cục, chữ thường là cục bộ. Quy tắc đó đúng cho mọi chữ cái.
Ukhông có địa chỉ, vàundefined referencechỉ nghĩa là mộtUkhông tìm đượcTtương ứng.- Tệp
.socó hai bảng ký hiệu, vàstripchỉ xóa được bảng đầy đủ chứ không xóa được bảng động. lddhiện đường dẫn thật sự được nạp, cònobjdump -pchỉ hiện tên ghi trong tệp.- Header của thư viện C phát hành ra ngoài nên có bốn dòng
extern "C"để dùng được từ C++.