Bỏ qua điều hướng, tới nội dung chính
Học C
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ạnNghĩa
T.textHàm công khai, chương trình khác gọi được
t.textHàm khai báo static, chỉ tệp đó dùng
D.dataBiến toàn cục có giá trị khởi tạo khác 0
d.dataBiến static có giá trị khởi tạo khác 0
B.bssBiến toàn cục khởi tạo bằng 0 hoặc không khởi tạo
b.bssBiến static tương ứng
R.rodataHằng số công khai, vùng chỉ đọc
r.rodataHằng số static
Ukhông cóCẦN từ nơi khác, chưa định nghĩa
WyếuKý hiệu yếu, định nghĩa khác đè lên được
CchungKý hiệu chung, biến khai báo mà chưa gán, kiểu cũ
Atuyệt đốiGiá 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 app
terminal
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áoNguyê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 -lfooSai đường dẫn -L, hoặc tên tệp thiếu tiền tố lib
relocation R_X86_64_PC32 ... recompile with -fPICQuên -fPIC khi tạo thư viện chia sẻ
DSO missing from command linePhụ 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ớ
nmBảng ký hiệu, nhanh và gọnnm -A *.o | grep ten
objdumpMọi thứ: hợp ngữ, phân đoạn, phụ thuộc, dời chỗobjdump -d -M intel, objdump -p
readelfChỉ ELF, nhưng chi tiết nhất về cấu trúc tệpreadelf -d, readelf -S, readelf -h
sizeKích thước từng phân đoạn, một dòngsize -A app
stringsChuỗi văn bản trong tệp nhị phânstrings app | grep -i version
stripXóa ký hiệu gỡ lỗi để giảm kích thướcstrip --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 SONAME

Tự làm thử

  1. Viết tệp có đủ mọi loại ký hiệu và đối chiếu nm với bảng bảy chữ cái.
  2. Dùng nm -u để liệt kê thứ một chương trình cần từ bên ngoài.
  3. Chạy strip rồi so nm với nm -D.
  4. Tạo một lỗi undefined reference rồi dò theo quy trình bốn bước.
  5. Dịch một tệp C bằng g++ và đọc thông báo lỗi tên biến đổi.
  6. 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.
  • U không có địa chỉ, và undefined reference chỉ nghĩa là một U không tìm được T tương ứng.
  • Tệp .so có hai bảng ký hiệu, và strip chỉ xóa được bảng đầy đủ chứ không xóa được bảng động.
  • ldd hiện đường dẫn thật sự được nạp, còn objdump -p chỉ 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++.