Bỏ qua điều hướng, tới nội dung chính
Học C
Bài 39.924 phút đọc

Đánh số phiên bản và SONAME

Sau bài này bạn sẽ làm được

  • Đặt SONAME khi tạo thư viện chia sẻ
  • Giải thích ba tầng liên kết tượng trưng và vai trò từng cái
  • Áp dụng quy tắc MAJOR MINOR PATCH cho thay đổi ABI
  • Biết khi nào bắt buộc phải tăng số MAJOR

Ba tệp cho một thư viện nghe có vẻ rườm rà, nhưng mỗi cái giải quyết một vấn đề khác nhau: một cái cho người dịch, một cái cho chương trình đang chạy, một cái là tệp thật.

#Ba tệp, ba vai trò

gcc -shared -Wl,-soname,libmath.so.1 *.o -o libmath.so.1.2.3

ln -sf libmath.so.1.2.3  libmath.so.1     # SONAME
ln -sf libmath.so.1      libmath.so       # ten khi lien ket

$ ls -l
lrwxrwxrwx  libmath.so     -> libmath.so.1
lrwxrwxrwx  libmath.so.1   -> libmath.so.1.2.3
-rwxr-xr-x  libmath.so.1.2.3
TênAi dùngKhi nào
libmath.soTrình liên kếtLúc dịch, khi bạn viết -lmath
libmath.so.1Trình nạp độngLúc chạy, tên này được ghi vào tệp nhị phân
libmath.so.1.2.3Người quản trị và trình quản lý góiTệp thật trên đĩa, để biết chính xác bản nào đang cài

#SONAME làm gì

SONAME
Một chuỗi ghi bên trong tệp thư viện. Khi trình liên kết dùng thư viện đó, nó chép chuỗi này vào tệp nhị phân thay vì chép tên tệp. Lúc chạy, trình nạp tìm đúng chuỗi đó.
$ objdump -p libmath.so.1.2.3 | grep SONAME
  SONAME               libmath.so.1

$ gcc app.c -L. -lmath -o app          # -lmath tim thay libmath.so
$ objdump -p app | grep NEEDED
  NEEDED               libmath.so.1     # KHONG phai libmath.so

/* Trinh lien ket doc SONAME tu thu vien va ghi CHUOI DO vao app.
   Nen luc chay, app tim libmath.so.1, khong phai libmath.so.

   Do la ly do ban co the xoa lien ket tuong trung libmath.so
   sau khi dich xong ma chuong trinh van chay. Cac ban phan phoi
   Linux dua vao dieu nay: goi "libmath1" chua tep .so.1, con goi
   "libmath-dev" moi chua lien ket .so va cac header. */
Makefile với đầy đủ ba mức
TEN     = math
CHINH   = 1
PHU     = 2
VA      = 3

SO_DAY  = lib$(TEN).so.$(CHINH).$(PHU).$(VA)
SO_NAME = lib$(TEN).so.$(CHINH)
SO_LINK = lib$(TEN).so

$(SO_DAY): $(DOI_TUONG)
	$(CC) -shared -Wl,-soname,$(SO_NAME) $^ -o $@
	ln -sf $(SO_DAY)  $(SO_NAME)
	ln -sf $(SO_NAME) $(SO_LINK)

cai: $(SO_DAY)
	install -D -m 755 $(SO_DAY) $(DESTDIR)/usr/local/lib/$(SO_DAY)
	ln -sf $(SO_DAY)  $(DESTDIR)/usr/local/lib/$(SO_NAME)
	ln -sf $(SO_NAME) $(DESTDIR)/usr/local/lib/$(SO_LINK)
	install -D -m 644 include/$(TEN).h $(DESTDIR)/usr/local/include/$(TEN).h
	ldconfig

.PHONY: cai

#Semantic versioning cho thư viện

Loại thay đổiSố nào tăngSONAME đổiVí dụ
Sửa lỗi, không đổi API hay ABIPATCHKhông1.2.3 thành 1.2.4
Thêm hàm mới, ABI cũ còn nguyênMINORKhông1.2.3 thành 1.3.0
Phá vỡ ABIMAJORCÓ1.2.3 thành 2.0.0, SONAME thành libmath.so.2
Phá ABI mà giữ SONAME
# Ban 1.5.0 -> 1.6.0
# Thay doi: doi kieu tra ve cua ml_luu tu int sang long

gcc -shared -Wl,-soname,libmath.so.1 *.o -o libmath.so.1.6.0

/* SONAME van la .so.1, nen moi chuong trinh cu tu dong dung
   ban moi va nhan sai kieu tra ve.

   Loi im lang tren toan he thong. Day la kich ban te nhat
   trong ca phan nay. */
Phá ABI thì đổi SONAME
# Ban 1.5.0 -> 2.0.0
gcc -shared -Wl,-soname,libmath.so.2 *.o -o libmath.so.2.0.0

# Va GIU LAI ban 1 tren he thong:
/usr/lib/libmath.so.1 -> libmath.so.1.5.0
/usr/lib/libmath.so.2 -> libmath.so.2.0.0

/* Chuong trinh cu tim .so.1, tim thay, chay dung.
   Chuong trinh moi tim .so.2, chay dung.
   Ca hai song song, khong ai anh huong ai. */

#Trên Windows

terminal
objdump -p app_dyn.exe | grep 'DLL Name'
	DLL Name: libmath.dll
	DLL Name: KERNEL32.dll
	DLL Name: msvcrt.dll
# Cach dat ten co so phien ban tren MinGW:

gcc -shared *.o -o libmath1.dll -Wl,--out-implib,libmath.dll.a

#   DLL co so 1 trong ten
#   Thu vien nhap KHONG co so, nen nguoi dung van viet -lmath
#
# Khi len ban 2, ban tao libmath2.dll va mot thu vien nhap moi.
# Chuong trinh cu van tim libmath1.dll, ban ay van con tren dia.

# Va thu tu tim DLL cua Windows:
#   1. Thu muc chua tep exe          <- quan trong nhat
#   2. Thu muc he thong (System32)
#   3. Thu muc Windows
#   4. Thu muc hien tai
#   5. Cac thu muc trong PATH
#
# Tu Windows 7 co che do an toan bo buoc 4 di, vi no la lo hong
# bao mat: ke tan cong dat mot DLL gia trong thu muc hien tai.

#Kiểm tra phiên bản lúc chạy

Ba thứ cần có trong header
/* mathlib.h */

#define ML_PHIEN_BAN_CHINH  1
#define ML_PHIEN_BAN_PHU    2
#define ML_PHIEN_BAN_VA     3

#define ML_PHIEN_BAN \
    (ML_PHIEN_BAN_CHINH * 10000 + ML_PHIEN_BAN_PHU * 100 + ML_PHIEN_BAN_VA)

#define ML_PHIEN_BAN_CHUOI  "1.2.3"

/* Va MOT HAM, vi macro o tren la phien ban LUC DICH: */
unsigned    ml_phien_ban(void);        /* phien ban LUC CHAY */
const char *ml_phien_ban_chuoi(void);
Người dùng kiểm tra
#include <stdio.h>
#include "mathlib.h"

int main(void) {
    unsigned luc_dich = ML_PHIEN_BAN;
    unsigned luc_chay = ml_phien_ban();

    printf("dich voi %s, chay voi %s\n",
           ML_PHIEN_BAN_CHUOI, ml_phien_ban_chuoi());

    if (luc_chay / 10000 != luc_dich / 10000) {
        fprintf(stderr, "sai ban CHINH: can %u, co %u\n",
                luc_dich / 10000, luc_chay / 10000);
        return 1;
    }

    if (luc_chay < luc_dich) {
        fprintf(stderr, "thu vien qua cu\n");
        return 1;
    }

    ...
}

Tự làm thử

  1. Tạo thư viện với -Wl,-soname và ba liên kết tượng trưng.
  2. Xác nhận bằng objdump -p rằng chương trình phụ thuộc SONAME chứ không phải tên tệp.
  3. Quên -soname, dịch chương trình, rồi xem nó phụ thuộc vào gì.
  4. Cài hai bản MAJOR khác nhau và chứng minh hai chương trình dùng đúng bản của mình.
  5. Viết cặp macro và hàm phiên bản, rồi cố tình cho chúng lệch nhau.
  6. Trên Windows, đặt số phiên bản vào tên DLL và kiểm tra bằng objdump -p.

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

  • Ba tệp: .so cho trình liên kết, .so.1 cho trình nạp, .so.1.2.3 là tệp thật.
  • SONAME là chuỗi ghi trong thư viện; trình liên kết chép nó vào tệp nhị phân thay vì chép tên tệp.
  • Quên -Wl,-soname làm chương trình phụ thuộc vào liên kết tượng trưng, tức luôn dùng bản mới nhất.
  • Đổi SONAME khi và chỉ khi phá vỡ ABI; thêm hàm là MINOR, xóa hàm là MAJOR.
  • Windows không có SONAME, nên phải đặt số phiên bản vào tên tệp hoặc để DLL cạnh tệp thực thi.