The Linux Programming Interface Chương 41 · Fundamentals of Shared Libraries

Chương 41 · Nền tảng thư viện dùng chung Chapter 41 · Fundamentals of Shared Libraries

Một bản mã, chia sẻ cho mọi chương trình — giải phẫu thư viện dùng chung Một bản mã dùng chung cho nhiều chương trình One copy of the code, shared by every program — the anatomy of shared libraries

Kerrisk mở đầu bằng thư viện đối tượng (object library) — một tập hợp các mô-đun đối tượng đã biên dịch, gom lại thành một đơn vị để nhiều chương trình liên kết tới. Linux có hai loại: thư viện tĩnh (static library, hay archive, tên dạng libname.a) và thư viện dùng chung (shared library, tên dạng libname.so).1 Chương đi qua: vì sao thư viện tĩnh lãng phí; ý tưởng cốt lõi của thư viện dùng chung; cách tạo và dùng nó (mã độc lập vị trí với -fPIC, dựng bằng cc -shared, liên kết với -l/-L, cách tìm thư viện lúc chạy); soname cùng tên thật và tên liên kết; cài đặt và chạy ldconfig; biến LD_LIBRARY_PATH với trình liên kết động ld.so; và các công cụ ldd, objdump, readelf, nm.1

Hãy hình dung một công thức nấu ăn mà nhiều đầu bếp cùng cần. Thay vì ai cũng chép lại cả cuốn vào sổ của mình (tốn giấy, khó sửa), ta treo một tấm bảng công thức chung giữa bếp để mọi người cùng nhìn. Thư viện dùng chung là tấm bảng ấy: một bản mã nằm trong bộ nhớ, mọi chương trình cùng dùng. Nhớ nhé: thư viện dùng chung là một bản mã treo chung cho nhiều chương trình.

Kerrisk opens with the object library — a collection of compiled object modules gathered into one unit that many programs can link against. Linux provides two types: the static library (or archive, named libname.a) and the shared library (named libname.so).1 The chapter covers: why static libraries are wasteful; the core idea of a shared library; how to create and use one (position-independent code with -fPIC, building with cc -shared, linking with -l/-L, the run-time search); the soname together with the real name and the linker name; installing and running ldconfig; the LD_LIBRARY_PATH variable and the dynamic linker ld.so; and the tools ldd, objdump, readelf, and nm.1

Điểm mấu chốt để cả chương sáng tỏ: khi liên kết với thư viện tĩnh, bản sao các mô-đun đối tượng được chép thẳng vào tệp thực thi; khi liên kết với thư viện dùng chung, tệp thực thi chỉ ghi lại tên thư viện nó cần (thẻ DT_NEEDED trong ELF), còn một bản duy nhất của thư viện được trình liên kết động nạp vào bộ nhớ lúc chạy, khi chương trình đầu tiên cần tới nó khởi động.16The hinge that makes the whole chapter cohere: linking against a static library copies the object modules straight into the executable; linking against a shared library records only the name of the library the executable needs (the DT_NEEDED tag in ELF), while a single copy of the library is loaded into memory at run time by the dynamic linker, when the first program needing it starts.16

Luận đề của chương: một thư viện dùng chung tách mã khỏi tệp thực thi — thay vì chép mã vào từng chương trình, ta để một bản duy nhất trong bộ nhớ và cho mọi chương trình dùng chung lúc chạy. Hiểu được -fPIC, bộ ba tên (tên thật / soname / tên liên kết), cách ld.so tìm thư viện, và vai trò của ldconfig — là nắm được cơ chế liên kết động làm nền cho gần như mọi phần mềm Linux hiện đại.1

Ý chính: đừng chép mã vào từng chương trình; hãy để một bản dùng chung và cho mọi chương trình mượn lúc chạy. Vừa tiết kiệm đĩa và bộ nhớ, vừa dễ vá lỗi. Nhớ nhé: một bản mã, nhiều người dùng, sửa một chỗ là xong.

The chapter's thesis: a shared library separates the code from the executable — instead of copying code into each program, a single copy lives in memory and every program shares it at run time. Grasping -fPIC, the triad of names (real name / soname / linker name), how ld.so finds libraries, and the role of ldconfig — is grasping the dynamic-linking machinery that underpins nearly all modern Linux software.1

Cụm khái niệm 01 Concept cluster 01

Thư viện tĩnh gom mã lại — thư viện dùng chung thì chia sẻ nó Thư viện tĩnh chép mã vào từng chương trình; thư viện dùng chung thì để chung một bản A static library bundles the code — a shared library shares it

Một thư viện đối tượng là một đơn vị gom nhiều mô-đun đối tượng đã biên dịch, để tránh phải biên dịch lại mã nguồn hay liệt kê hàng loạt tệp .o mỗi lần dựng chương trình. Loại đầu tiên trên UNIX là thư viện tĩnh — thực chất là một tệp lưu trữ (archive) chứa bản sao các tệp đối tượng, tạo và bảo trì bằng lệnh ar(1), tên theo quy ước libname.a.17 Khi liên kết với thư viện tĩnh, trình liên kết chỉ trích những mô-đun chương trình thực sự cần và chép chúng vào tệp thực thi.1

Thư viện là một "hộp" chứa sẵn nhiều mẩu mã đã dịch, để ta khỏi phải dịch lại hay gõ lại tên từng mẩu mỗi lần. Kiểu cũ nhất — thư viện tĩnh — giống như photo từng mẩu cần dùng rồi dán thẳng vào chương trình. Chương trình nào cũng tự mang theo một bản photo riêng. Nhớ nhé: thư viện tĩnh là chép mã dán thẳng vào chương trình.

An object library is a unit that gathers many compiled object modules, so you need not recompile source or list a long series of .o files each time you build a program. The first type on UNIX is the static library — essentially an archive holding copies of the object files, created and maintained with the ar(1) command and named by convention libname.a.17 When you link against a static library, the linker only extracts the modules the program actually needs and copies them into the executable.1

Định nghĩa. Thư viện dùng chung (shared library) dựa trên một ý tưởng: một bản duy nhất của các mô-đun đối tượng được mọi chương trình cần tới dùng chung. Các mô-đun không được chép vào tệp thực thi; thay vào đó, một bản của thư viện được nạp vào bộ nhớ lúc chạy, khi chương trình đầu tiên cần nó khởi động. Các chương trình chạy sau dùng lại chính bản đã nạp đó.1

Nói đơn giản. Thay vì mỗi chương trình mang một bản photo, ta để một bản gốc trong bộ nhớ và cho mọi chương trình cùng dùng. Nhớ nhé: một bản gốc dùng chung, không ai phải photo riêng.

Definition. A shared library rests on one idea: a single copy of the object modules is shared by all programs requiring them. The modules are not copied into the executable; instead, one copy of the library is loaded into memory at run time, when the first program needing it starts. Later programs reuse that already-loaded copy.1

# thư viện tĩnh (archive)
$ cc -g -c mod1.c mod2.c mod3.c
$ ar r libdemo.a mod1.o mod2.o mod3.o
$ cc -g -o prog prog.o -ldemo
/* mã được chép vào prog */

ar r libdemo.a *.o  --> gom vào hộp
cc -o prog prog.o -ldemo
(mã dán thẳng vào prog)

# static library (archive)
$ cc -g -c mod1.c mod2.c mod3.c
$ ar r libdemo.a mod1.o mod2.o mod3.o
$ cc -g -o prog prog.o -ldemo
/* code copied into prog */

  1. ar / libname.a

    Thư viện tĩnh là một archive của các tệp .o Thư viện tĩnh là một hộp chứa các tệp .o A static library is an archive of .o files

    Lệnh ar(1) tạo và bảo trì archive; đối số options gồm một mã thao tác kèm các chữ bổ nghĩa. Thường dùng: r (replace) để chèn hoặc thay một tệp đối tượng, t (table of contents) để liệt kê nội dung — thêm v sẽ hiện cả quyền, user/group ID, kích thước, thời điểm sửa — và d (delete) để xóa một mô-đun.17 Dù archive chứa nhiều mô-đun, trình liên kết chỉ lấy những mô-đun chương trình cần.1

    Lệnh ar giống như quản lý một hộp đồ: r để bỏ thêm hay thay một món, t để xem danh sách món trong hộp, d để lấy một món ra. Khi dựng chương trình, hệ thống chỉ lấy đúng những món cần. Nhớ nhé: ar quản lý cái hộp .o, chương trình chỉ lấy món nó cần.

    The ar(1) command creates and maintains the archive; its options argument holds one operation code plus modifier letters. Common ones: r (replace) to insert or replace an object file, t (table of contents) to list its contents — adding v shows permissions, user/group IDs, size, and modification time — and d (delete) to remove a module.17 Though the archive holds many modules, the linker pulls in only those the program needs.1

    Bạn gặp nó ở đâuWhere you meet it Có thể liên kết theo hai cách: nêu tên tệp archive trực tiếp (cc -o prog prog.o libdemo.a), hoặc đặt nó vào thư mục chuẩn rồi dùng -ldemo, thêm -Lmylibdir nếu nó ở thư mục khác.1 Khi dựng chương trình, bạn chỉ cần nói tên thư viện bằng -ldemo thay vì liệt kê từng tệp .o. You can link in two ways: name the archive directly (cc -o prog prog.o libdemo.a), or place it in a standard directory and use -ldemo, adding -Lmylibdir if it lives elsewhere.1

    Chi tiết của tác giảThe author's detail

    Archive còn lưu các thuộc tính của từng tệp đối tượng thành phần: quyền tệp, user/group ID dạng số, và thời điểm sửa lần cuối. Sau khi đã dựng thư viện, có thể xóa các tệp .o gốc vì chúng không còn cần nữa.1The archive also records each component object file's attributes: file permissions, numeric user and group IDs, and last-modification time. After building the library you may delete the original .o files, since they are no longer required.1

    Vì sao chọn -gWhy -g

    Tác giả khuyên luôn biên dịch với cờ -g để kèm thông tin gỡ lỗi; đây là thói quen tốt khi dựng cả chương trình lẫn thư viện.14The author advises always compiling with -g to include debugging information; it is a good habit when building both programs and libraries.14

  2. static redundancy

    Vì sao thư viện tĩnh lãng phí: đĩa, bộ nhớ, và việc vá lỗi Thư viện tĩnh tốn chỗ và khó vá lỗi Why static wastes: disk, memory, and patching

    Vì mỗi tệp thực thi mang bản sao riêng của các mô-đun, sự dư thừa này có ba nhược điểm: tốn đĩa vì lưu nhiều bản của cùng mô-đun; tốn bộ nhớ ảo vì nhiều chương trình chạy cùng lúc mỗi bên giữ bản riêng; và khó vá vì mỗi khi một mô-đun cần sửa (vá bảo mật hay sửa lỗi), mọi tệp thực thi dùng nó phải được liên kết lại.1

    Ba cái tốn khi ai cũng mang bản photo riêng: tốn chỗ chứa, tốn bộ nhớ lúc chạy, và mỗi lần sửa một mẩu mã phải sửa lại tất cả các bản photo. Nhớ nhé: chép mã khắp nơi thì sửa một lỗi phải đi sửa khắp nơi.

    Because each executable carries its own copy of the modules, this redundancy has three drawbacks: wasted disk storing many copies of the same module; wasted virtual memory as several running programs each hold their own copy; and hard patching, since each time a module needs a change (a security or bug fix), every executable using it must be relinked.1

    Bạn gặp nó ở đâuWhere you meet it Mỗi bản vá bảo mật cho một thư viện C nền tảng: với thư viện dùng chung, chỉ cần thay một tệp; với thư viện tĩnh, phải liên kết lại mọi ứng dụng dùng nó.1 Khi có lỗ hổng bảo mật trong một thư viện lớn: dùng chung thì vá một chỗ là xong cho tất cả. Every security patch to a core C library: with a shared library you replace one file; with a static library you must relink every application that uses it.1
  3. code shared, data not

    Mã thì dùng chung — nhưng biến thì không Dùng chung mã, nhưng mỗi chương trình có biến riêng Code is shared — but variables are not

    Một điểm dễ nhầm: tuy mã của thư viện dùng chung được chia sẻ giữa nhiều tiến trình, các biến của nó thì không. Mỗi tiến trình dùng thư viện có bản sao riêng của các biến toàn cục và tĩnh được định nghĩa trong thư viện.1 Thư viện dùng chung còn có lợi ích phụ: do kích thước chương trình nhỏ hơn, trong một số trường hợp chương trình được nạp và khởi động nhanh hơn — nhưng chỉ đúng khi thư viện lớn đã được một chương trình khác dùng sẵn; chương trình đầu tiên nạp nó thì lại khởi động chậm hơn.1

    Dùng chung là dùng chung phần mã lệnh — còn các biến (dữ liệu) thì mỗi chương trình giữ riêng một bộ, không lẫn vào nhau. Giống như nhiều người đọc chung một cuốn công thức, nhưng ai nấu nồi của người đó. Nhớ nhé: chung công thức, riêng nồi.

    A common confusion: although a shared library's code is shared among processes, its variables are not. Each process using the library has its own copies of the global and static variables defined within the library.1 Shared libraries bring a side benefit too: since program size is smaller, in some cases a program loads and starts faster — but only when a large library is already in use by another program; the first program to load it actually starts more slowly.1

    Bạn gặp nó ở đâuWhere you meet it Chi phí kèm theo: thư viện dùng chung phức tạp hơn, phải biên dịch bằng mã độc lập vị trí (có phí tổn hiệu năng), và cần tái định vị ký hiệu lúc chạy.1 Đổi lại sự tiện lợi, thư viện dùng chung phức tạp hơn và chạy có thể chậm hơn một chút do phải sắp chỗ lúc chạy. The costs: shared libraries are more complex, must be compiled as position-independent code (a performance overhead), and require symbol relocation at run time.1

Cụm khái niệm 02 Concept cluster 02

Dựng một thư viện dùng chung: -fPIC, cc -shared, rồi liên kết hai pha Dựng thư viện dùng chung và gắn chương trình vào Building a shared library: -fPIC, cc -shared, then two-phase linking

Chỉ cần hai lệnh để có một thư viện dùng chung. Trước hết, biên dịch các mô-đun bằng cờ -fPIC để sinh mã độc lập vị trí; sau đó dùng cc -shared gom chúng thành tệp .so. Theo quy ước, thư viện dùng chung mang tiền tố lib và hậu tố .so (shared object).14 Chương chỉ bàn thư viện định dạng ELF — định dạng cho tệp thực thi và thư viện dùng chung trên Linux hiện đại, thay cho a.out và COFF cũ.16

Chỉ cần hai bước: dịch mã với cờ -fPIC (để mã "đi đâu cũng chạy được"), rồi gộp lại bằng cc -shared thành một tệp đuôi .so. Tên thư viện bắt đầu bằng lib và kết thúc bằng .so. Nhớ nhé: -fPIC để dịch, -shared để gộp, đuôi .so để nhận ra.

Just two commands produce a shared library. First, compile the modules with the -fPIC flag to generate position-independent code; then use cc -shared to gather them into a .so file. By convention, shared libraries carry the prefix lib and the suffix .so (shared object).14 The chapter deals only with ELF shared libraries — the format used for executables and shared libraries on modern Linux, superseding the older a.out and COFF.16

Định nghĩa. Mã độc lập vị trí (position-independent code, PIC) là mã được sinh sao cho có thể đặt ở bất kỳ địa chỉ ảo nào lúc chạy. Điều này cần cho thư viện dùng chung, vì lúc liên kết không thể biết trước thư viện sẽ nằm ở đâu trong bộ nhớ — vị trí đó phụ thuộc vào bộ nhớ chương trình đã dùng và các thư viện khác đã nạp.14

Nói đơn giản. Mã độc lập vị trí là mã "đi đâu cũng chạy được" — đặt ở chỗ nào trong bộ nhớ nó cũng hoạt động. Cần thế vì lúc dựng ta chưa biết thư viện sẽ nằm ở đâu lúc chạy. Nhớ nhé: -fPIC làm mã chạy được dù nằm ở bất cứ đâu.

Definition. Position-independent code (PIC) is code generated so it can be placed at any virtual address at run time. This is necessary for a shared library, since at link time there is no way to know where it will sit in memory — that location depends on the memory already used by the program and which other libraries are already loaded.14

# 1) biên dịch độc lập vị trí
$ gcc -g -c -fPIC -Wall mod1.c mod2.c mod3.c
# 2) gộp thành thư viện dùng chung
$ gcc -g -shared -o libfoo.so mod1.o mod2.o mod3.o
# 3) liên kết chương trình
$ gcc -g -Wall -o prog prog.c libfoo.so

gcc -c -fPIC *.c  --> dịch
gcc -shared -o libfoo.so *.o --> gộp
gcc -o prog prog.c libfoo.so

# 1) compile position-independent
$ gcc -g -c -fPIC -Wall mod1.c mod2.c mod3.c
# 2) gather into a shared library
$ gcc -g -shared -o libfoo.so mod1.o mod2.o mod3.o
# 3) link the program
$ gcc -g -Wall -o prog prog.c libfoo.so

  1. -fPIC

    -fPIC: mã chạy được ở bất kỳ địa chỉ nào -fPIC: mã đặt đâu cũng chạy -fPIC: code that runs at any address

    Cờ -fPIC đổi cách trình biên dịch sinh mã cho các thao tác như truy cập biến toàn cục/tĩnh/ngoài, truy cập hằng chuỗi, và lấy địa chỉ hàm — nhờ đó mã đặt được ở bất kỳ đâu.14 Để kiểm tra một tệp đối tượng có được biên dịch với -fPIC không, tìm ký hiệu _GLOBAL_OFFSET_TABLE_ trong bảng ký hiệu bằng nm hoặc readelf -s.1911

    Cờ -fPIC bảo trình dịch sinh ra loại mã không gắn cứng vào một địa chỉ cố định, nên đặt chỗ nào trong bộ nhớ cũng chạy. Muốn biết một tệp đã dịch kiểu này chưa, tìm cái tên _GLOBAL_OFFSET_TABLE_ trong nó. Nhớ nhé: thấy _GLOBAL_OFFSET_TABLE_ là biết mã đã độc lập vị trí.

    The -fPIC flag changes how the compiler generates code for operations such as accessing global/static/external variables, string constants, and taking function addresses — so the code can be located anywhere.14 To check whether an object file was compiled with -fPIC, look for the symbol _GLOBAL_OFFSET_TABLE_ in its symbol table using nm or readelf -s.1911

    Bạn gặp nó ở đâuWhere you meet it Nếu objdump --all-headers libfoo.so | grep TEXTREL (hay readelf -d) có kết xuất, thư viện chứa ít nhất một mô-đun không biên dịch bằng -fPIC — segment text của nó cần tái định vị lúc chạy.110 Nếu lệnh kiểm tra tìm thấy chữ TEXTREL, nghĩa là trong thư viện còn mẩu mã quên dùng -fPIC. If objdump --all-headers libfoo.so | grep TEXTREL (or readelf -d) yields output, the library contains at least one module not compiled with -fPIC — its text segment needs run-time relocation.110

    Phí tổnThe overhead

    Trên hầu hết kiến trúc, mã độc lập vị trí có phí tổn hiệu năng vì cần một thanh ghi phụ. Trên Linux/x86-32, vẫn dựng được thư viện dùng chung từ mô-đun không -fPIC, nhưng mất đi lợi ích chia sẻ (các trang text chứa tham chiếu phụ thuộc vị trí không dùng chung được giữa các tiến trình); trên một số kiến trúc khác thì bắt buộc phải có -fPIC.1On most architectures, position-independent code carries a performance cost because it needs an extra register. On Linux/x86-32 you can still build a shared library from non--fPIC modules, but you lose the sharing benefit (text pages with position-dependent references cannot be shared across processes); on some other architectures -fPIC is mandatory.1

    Cờ phụ thuộc trình dịchCompiler-dependent flags

    Tác giả dùng gcc (không phải cc) để nhấn mạnh rằng các cờ dựng thư viện dùng chung phụ thuộc trình biên dịch; một trình dịch C khác trên UNIX khác có thể cần cờ khác.14The author uses gcc (not cc) to stress that the flags for building shared libraries are compiler-dependent; a different C compiler on another UNIX may need different options.14

  2. cc -shared

    cc -shared dựng tệp .so — và nó là một khối liền cc -shared gộp lại thành một tệp .so cc -shared builds the .so — as one unit

    Lệnh cc -shared tạo một thư viện dùng chung chứa các mô-đun đối tượng. Khác với thư viện tĩnh, không thể thêm hay bớt từng mô-đun khỏi một thư viện dùng chung đã dựng: như với tệp thực thi thường, các tệp đối tượng bên trong không còn giữ danh tính riêng.14

    Khi đã gộp bằng cc -shared, các mẩu mã hòa làm một khối — không tách ra hay thêm từng mẩu được nữa, khác với cái hộp .o của thư viện tĩnh. Nhớ nhé: tĩnh là hộp tháo lắp được, dùng chung là khối liền.

    The cc -shared command creates a shared library containing the object modules. Unlike a static library, you cannot add or remove individual modules from a shared library once built: as with an ordinary executable, the object files inside no longer keep distinct identities.14

    Bạn gặp nó ở đâuWhere you meet it Có thể gộp biên dịch và dựng thư viện trong một lệnh (gcc -g -fPIC -Wall mod1.c mod2.c mod3.c -shared -o libfoo.so), nhưng tách hai bước cho rõ ràng hơn.1 Có thể làm cả hai bước trong một lệnh, nhưng tách ra cho dễ theo dõi. You can combine compiling and building into one command (gcc -g -fPIC -Wall mod1.c mod2.c mod3.c -shared -o libfoo.so), but separating the two steps is clearer.1
  3. static vs dynamic linking

    Liên kết hai pha: tĩnh lúc dựng, động lúc chạy Hai bước liên kết: lúc dựng và lúc chạy Two-phase linking: static at build, dynamic at run

    Dùng thư viện dùng chung cần hai việc mà thư viện tĩnh không có. Thứ nhất, lúc liên kết, tên thư viện cần được nhúng vào tệp thực thi (trong ELF là thẻ DT_NEEDED; toàn bộ gọi là dynamic dependency list). Thứ hai, lúc chạy, phải có cơ chế giải tên đó — tìm tệp thư viện tương ứng và nạp vào bộ nhớ nếu chưa có — do trình liên kết động đảm nhận.16 Mọi chương trình đều qua pha liên kết tĩnh (bằng ld); chương trình dùng thư viện dùng chung còn qua thêm pha liên kết động lúc chạy.15

    Thư viện dùng chung cần hai bước: lúc dựng, ghi vào chương trình tên thư viện nó cần; lúc chạy, có một "người giao hàng" đi tìm đúng tệp thư viện đó rồi nạp vào. Người giao hàng ấy là trình liên kết động. Nhớ nhé: lúc dựng ghi tên, lúc chạy đi tìm và nạp.

    Using a shared library needs two things a static library does not. First, at link time the library's name is embedded in the executable (in ELF, the DT_NEEDED tag; the whole set is the dynamic dependency list). Second, at run time there must be a mechanism to resolve that name — find the matching library file and load it into memory if not already present — performed by the dynamic linker.16 Every program goes through a static-linking phase (via ld); a program using shared libraries additionally undergoes dynamic linking at run time.15

    Bạn gặp nó ở đâuWhere you meet it Lỗi quen thuộc error while loading shared libraries: ... cannot open shared object file xảy ra khi trình liên kết động không tìm thấy thư viện trong các thư mục chuẩn — ví dụ thư viện nằm ở thư mục làm việc hiện tại.12 Lỗi "cannot open shared object file" nghĩa là chương trình chạy nhưng không tìm thấy thư viện nó cần. The familiar error while loading shared libraries: ... cannot open shared object file happens when the dynamic linker cannot find the library in the standard directories — for example, when it sits in the current working directory.12

    Trình liên kết động là aiWho the dynamic linker is

    Bản thân trình liên kết động là một thư viện dùng chung, tên /lib/ld-linux.so.2 — thường là liên kết tượng trưng tới tệp thực thi ld-version.so (với version là phiên bản glibc cài trên máy, ví dụ ld-2.11.so). Mọi tệp thực thi ELF dùng thư viện dùng chung đều nhờ nó.12The dynamic linker is itself a shared library, named /lib/ld-linux.so.2 — usually a symbolic link to the executable file ld-version.so (where version is the installed glibc version, e.g. ld-2.11.so). Every ELF executable using shared libraries relies on it.12

    32-bit và 64-bit32-bit and 64-bit

    Trên các kiến trúc chạy được cả chương trình 32-bit lẫn 64-bit (như x86-64), thư viện 32-bit nằm trong các thư mục con */lib, còn thư viện 64-bit nằm trong */lib64.12On architectures that run both 32-bit and 64-bit programs (such as x86-64), 32-bit libraries live in */lib subdirectories and 64-bit libraries in */lib64.12

Cụm khái niệm 03 Concept cluster 03

Một thư viện, ba cái tên: tên thật, soname, và tên liên kết Một thư viện có ba cái tên khác nhau One library, three names: real name, soname, and linker name

Cơ chế đặt tên theo quy ước là phần tinh tế nhất của chương. Một thư viện dùng chung có thể mang một bí danh gọi là soname (thẻ DT_SONAME trong ELF). Khi thư viện có soname, lúc liên kết tĩnh chính soname — chứ không phải tên tệp thật — được nhúng vào tệp thực thi, rồi dùng để tìm thư viện lúc chạy. Soname tạo một lớp gián tiếp: tệp thực thi gắn với soname, còn soname lại là một liên kết tượng trưng trỏ tới tên tệp thật hiện hành.16

Đây là phần tinh tế nhất. Một thư viện có thể có một "biệt danh" (soname). Khi dựng chương trình, ta ghi vào chương trình cái biệt danh ấy, chứ không ghi tên tệp cụ thể. Lúc chạy, biệt danh đó chỉ đường tới tệp thật đang dùng. Nhớ nhé: chương trình nhớ biệt danh, biệt danh chỉ tới tệp thật.

The conventional naming scheme is the subtlest part of the chapter. A shared library may carry an alias called a soname (the DT_SONAME tag in ELF). When a library has a soname, at static-link time the soname — not the real filename — is embedded in the executable, then used to find the library at run time. The soname provides a level of indirection: the executable binds to the soname, while the soname is itself a symbolic link pointing to the current real filename.16

Định nghĩa. Theo quy ước phiên bản: tên thật (real name) có dạng libname.so.major.minor — tệp chứa mã, mỗi phiên bản major+minor một bản. Soname có dạng libname.so.major — mỗi major một bản, nhúng vào tệp thực thi, và là liên kết trỏ tới tên thật mới nhất. Tên liên kết (linker name) có dạng libname.so — không số phiên bản, dùng khi liên kết, là liên kết trỏ tới tên thật hoặc (thường hơn) tới soname mới nhất.1

Nói đơn giản. Ba cái tên: tên thật có đủ số (libfoo.so.1.0.1) là tệp mã thật; soname chỉ có số lớn (libfoo.so.1) là biệt danh theo phiên bản lớn; tên liên kết không có số (libfoo.so) dùng lúc dựng chương trình. Nhớ nhé: càng nhiều số thì càng cụ thể, không số thì để dựng.

Definition. By the versioning convention: the real name has the form libname.so.major.minor — the file holding the code, one per major+minor version. The soname has the form libname.so.major — one per major version, embedded in the executable, and a symbolic link pointing to the latest real name. The linker name has the form libname.so — no version numbers, used when linking, a link to the real name or (more usually) the latest soname.1

# dựng với soname
$ gcc -g -shared -Wl,-soname,libdemo.so.1 \
    -o libdemo.so.1.0.1 mod1.o mod2.o mod3.o
# chuỗi liên kết tượng trưng
$ ln -s libdemo.so.1.0.1 libdemo.so.1  # soname
$ ln -s libdemo.so.1     libdemo.so   # linker
$ gcc -g -o prog prog.c -L. -ldemo

libdemo.so      --> libdemo.so.1
libdemo.so.1   --> libdemo.so.1.0.1
libdemo.so.1.0.1 (tệp mã thật)

# build with a soname
$ gcc -g -shared -Wl,-soname,libdemo.so.1 \
    -o libdemo.so.1.0.1 mod1.o mod2.o mod3.o
# the symbolic-link chain
$ ln -s libdemo.so.1.0.1 libdemo.so.1  # soname
$ ln -s libdemo.so.1     libdemo.so   # linker
$ gcc -g -o prog prog.c -L. -ldemo

  1. -Wl,-soname

    Gắn soname lúc dựng, kiểm tra bằng objdump/readelf Đặt biệt danh cho thư viện khi dựng Set the soname at build, check with objdump/readelf

    Để gắn soname, truyền tùy chọn -Wl,-soname,libbar.so — một chỉ thị cho trình liên kết ld đánh dấu thư viện với soname đó. Có thể đọc soname của một thư viện có sẵn bằng objdump -p libfoo.so | grep SONAME hoặc readelf -d libfoo.so | grep SONAME.151011

    Khi dựng, thêm -Wl,-soname,... để đặt biệt danh cho thư viện. Muốn xem biệt danh của một thư viện có sẵn, dùng objdump -p hay readelf -d rồi tìm dòng SONAME. Nhớ nhé: -Wl,-soname để đặt, grep SONAME để xem.

    To set a soname, pass the option -Wl,-soname,libbar.so — an instruction to the linker ld to mark the library with that soname. You can read an existing library's soname with objdump -p libfoo.so | grep SONAME or readelf -d libfoo.so | grep SONAME.151011

    Bạn gặp nó ở đâuWhere you meet it Khi dùng soname, cần thêm một bước: tạo liên kết tượng trưng từ soname tới tên thật, nếu không trình liên kết động sẽ không tìm thấy thư viện lúc chạy.1 Dùng soname thì phải tạo một liên kết trỏ từ biệt danh sang tệp thật, nếu không lúc chạy sẽ không tìm được. When using a soname, one more step is required: create a symbolic link from the soname to the real name, or the dynamic linker will not find the library at run time.1
  2. real / soname / linker

    Chuỗi liên kết: tên liên kết → soname → tên thật Dây chuyền: tên không số → biệt danh → tệp thật The chain: linker name → soname → real name

    Ba cái tên được xâu thành một chuỗi liên kết tượng trưng: tên liên kết libdemo.so trỏ tới soname libdemo.so.1, soname trỏ tới tên thật libdemo.so.1.0.1. Nhờ tên liên kết (không số phiên bản), lệnh liên kết trở nên độc lập phiên bản: gcc -o prog prog.c -L. -ldemo không hề nhắc tới số phiên bản.1

    Ba cái tên móc nối nhau như dây chuyền: tên không số chỉ tới biệt danh, biệt danh chỉ tới tệp thật. Nhờ vậy khi dựng chương trình ta chỉ cần gọi -ldemo, không phải nhớ số phiên bản. Nhớ nhé: dây chuyền tên giúp dựng mà khỏi lo số phiên bản.

    The three names form a chain of symbolic links: the linker name libdemo.so points to the soname libdemo.so.1, which points to the real name libdemo.so.1.0.1. Thanks to the linker name (no version numbers), the link command becomes version-independent: gcc -o prog prog.c -L. -ldemo mentions no version number.1

    Bạn gặp nó ở đâuWhere you meet it Chạy ls -l libdemo.so* trên một máy Linux: bạn sẽ thấy đúng chuỗi này — hai liên kết tượng trưng và một tệp thật, ví dụ libreadline.so -> libreadline.so.5.1 Gõ ls -l trong thư mục thư viện, bạn sẽ thấy các mũi tên nối tên này sang tên kia — đó chính là dây chuyền. Run ls -l libdemo.so* on a Linux box: you will see exactly this chain — two symbolic links and one real file, e.g. libreadline.so -> libreadline.so.5.1

    Vì sao tên liên kết nên trỏ tới sonameWhy the linker name should point at the soname

    Tên liên kết thường được tạo trong cùng thư mục với tệp nó trỏ tới, và nên trỏ tới soname (chứ không phải thẳng tên thật), để khi soname đổi thì tên liên kết tự động phản ánh theo — một việc mà ldconfig tự lo nếu ta theo đúng quy ước.13The linker name is usually created in the same directory as the file it refers to, and should point at the soname (not straight at the real name), so that changes to the soname are automatically reflected in the linker name — a job ldconfig handles for us if we follow the convention.13

    Liên kết với phiên bản major cũLinking against an older major version

    Nếu muốn liên kết với một phiên bản major cũ, không dùng được tên liên kết; phải chỉ rõ một tên thật hoặc soname cụ thể trong lệnh liên kết.1If you want to link against an older major version, you cannot use the linker name; you must name a specific real name or soname in the link command.1

  3. compatible vs incompatible

    Major đổi khi phá vỡ tương thích, minor đổi khi vẫn tương thích Số lớn đổi khi "vỡ", số nhỏ đổi khi vẫn hợp Major changes break compatibility, minor keeps it

    Sơ đồ phiên bản cho phép chương trình tự động dùng phiên bản minor mới nhất của thư viện mà không cần liên kết lại, đồng thời cho nhiều phiên bản major không tương thích cùng tồn tại. Một thay đổi là tương thích (chỉ tăng minor) nếu ngữ nghĩa mọi hàm/biến công khai giữ nguyên, không xóa hàm/biến nào khỏi API công khai, và các cấu trúc công khai không đổi; ngược lại phải định nghĩa một major mới.1

    Khi sửa thư viện mà không làm hỏng cách dùng cũ, chỉ tăng số nhỏ (minor). Khi sửa làm thay đổi cách dùng, phải tăng số lớn (major) và coi như một phiên bản khác hẳn. Nhớ nhé: còn dùng được như cũ thì tăng số nhỏ, không thì tăng số lớn.

    The versioning scheme lets a program automatically use the latest minor version without relinking, while letting multiple incompatible major versions coexist. A change is compatible (only the minor rises) if the semantics of every public function/variable stay the same, no public function/variable is removed, and public structures are unchanged; otherwise a new major version must be defined.1

    Bạn gặp nó ở đâuWhere you meet it Có thể nâng cấp lên phiên bản minor hoặc major mới ngay cả khi các chương trình đang chạy vẫn dùng phiên bản cũ; chúng chỉ chuyển sang bản mới khi được khởi động lại.1 Có thể cài phiên bản mới ngay cả khi chương trình cũ đang chạy; chương trình chỉ dùng bản mới sau khi khởi động lại. You can upgrade to a new minor or major version even while running programs use the old one; they switch to the new version only when restarted.1

Cụm khái niệm 04 Concept cluster 04

Cài đặt đúng chỗ, để ldconfig và ld.so lo phần còn lại Cài đúng chỗ, dùng ldconfig, và các công cụ soi thư viện Install in the right place, let ldconfig and ld.so do the rest

Ở các ví dụ trước, thư viện nằm trong thư mục riêng của người dùng và được tìm thấy nhờ LD_LIBRARY_PATH — tiện cho học tập nhưng không nên dùng cho ứng dụng thật. Thông thường, thư viện cùng các liên kết tượng trưng của nó được cài vào một thư mục chuẩn: /usr/lib (phần lớn thư viện chuẩn), /lib (thư viện cần lúc khởi động hệ thống), /usr/local/lib (thư viện phi chuẩn hay thử nghiệm), hoặc một thư mục liệt kê trong /etc/ld.so.conf.12 Thường cần quyền siêu người dùng để chép vào các thư mục này.1

Trước đó ta để thư viện ở thư mục riêng và dùng LD_LIBRARY_PATH để máy tìm thấy — cách này tiện để học nhưng không dùng cho sản phẩm thật. Cách đúng là chép thư viện vào các thư mục chuẩn như /usr/lib hay /lib (cần quyền quản trị). Nhớ nhé: học thì dùng LD_LIBRARY_PATH, làm thật thì cài vào thư mục chuẩn.

In earlier examples the library sat in a user-private directory and was found via LD_LIBRARY_PATH — handy for learning but not for production applications. Normally the library and its symbolic links are installed in a standard directory: /usr/lib (most standard libraries), /lib (libraries needed at system startup), /usr/local/lib (nonstandard or experimental libraries), or a directory listed in /etc/ld.so.conf.12 Copying into these directories usually requires superuser privilege.1

Định nghĩa. ldconfig(8) giải hai vấn đề của thư viện dùng chung. (1) Nó duyệt một tập thư mục chuẩn và tạo/cập nhật tệp bộ đệm /etc/ld.so.cache — danh sách các phiên bản major mới nhất — để trình liên kết động tra cứu nhanh lúc chạy. (2) Nó xét phiên bản minor mới nhất của mỗi major, đọc soname nhúng trong đó, rồi tạo/cập nhật liên kết tượng trưng cho mỗi soname trong cùng thư mục.13

Nói đơn giản. ldconfig làm hai việc: lập một danh bạ để máy tìm thư viện cho nhanh, và tự dựng/sửa các "mũi tên" soname cho đúng. Chạy nó mỗi khi cài, cập nhật hay gỡ một thư viện. Nhớ nhé: cài xong thư viện thì chạy ldconfig để máy nhận ra.

Definition. ldconfig(8) solves two problems of shared libraries. (1) It scans a standard set of directories and creates/updates the cache file /etc/ld.so.cache — a list of the latest major versions — so the dynamic linker can resolve names quickly at run time. (2) It examines the latest minor version of each major, reads the embedded soname, and creates/updates a symbolic link for each soname in the same directory.13

# cập nhật bộ đệm + soname symlinks
$ sudo ldconfig -v | grep libdemo
$ ldconfig -p     # xem /etc/ld.so.cache
# tìm thư viện (cách tạm cho học)
$ LD_LIBRARY_PATH=. ./prog
# xem phụ thuộc động
$ ldd prog

ldconfig   --> cập nhật danh bạ
ldconfig -p --> xem danh bạ
ldd prog   --> xem thư viện cần

# update cache + soname symlinks
$ sudo ldconfig -v | grep libdemo
$ ldconfig -p     # view /etc/ld.so.cache
# find the library (learning shortcut)
$ LD_LIBRARY_PATH=. ./prog
# view dynamic dependencies
$ ldd prog

  1. ldconfig

    ldconfig: danh bạ bộ đệm cộng với các soname symlink ldconfig lập danh bạ và dựng mũi tên soname ldconfig: the cache plus the soname symlinks

    Để dựng bộ đệm, ldconfig duyệt các thư mục trong /etc/ld.so.conf rồi tới /lib và /usr/lib. Nên chạy nó mỗi khi cài, cập nhật hay gỡ thư viện, hoặc khi danh sách thư mục thay đổi. Các cờ: -v hiện chi tiết, -N không dựng lại bộ đệm, -X không tạo soname symlink, và -n chỉ xử lý thư mục trên dòng lệnh (hữu ích cho thư viện riêng, không chạm bộ đệm).13

    ldconfig đi khắp các thư mục thư viện chuẩn để cập nhật danh bạ và dựng các mũi tên soname. Nhớ chạy nó sau mỗi lần cài hay gỡ thư viện. Thêm -v để xem nó làm gì. Nhớ nhé: cài/gỡ thư viện xong là chạy ldconfig.

    To build the cache, ldconfig scans the directories in /etc/ld.so.conf, then /lib and /usr/lib. Run it whenever you install, update, or remove a library, or when the directory list changes. Flags: -v for verbose output, -N to skip rebuilding the cache, -X to skip creating soname symlinks, and -n to process only command-line directories (useful for a private library, leaving the cache untouched).13

    Bạn gặp nó ở đâuWhere you meet it Vì tên liên kết trỏ tới soname mới nhất, khi cài một bản minor 2.x mới, ldconfig cập nhật soname và nhờ đó cũng giữ tên liên kết đúng — nhưng tên liên kết cho một major mới vẫn phải tạo tay.1 Khi lên bản minor mới, ldconfig tự lo giúp; nhưng lên một phiên bản lớn mới thì bạn phải tự tạo lại mũi tên tên liên kết. Because the linker name points at the latest soname, installing a new 2.x minor lets ldconfig update the soname and thereby keep the linker name correct too — but the linker name for a new major must still be created by hand.1
  2. LD_LIBRARY_PATH / ld.so

    LD_LIBRARY_PATH và thứ tự tìm kiếm của ld.so Nơi máy đi tìm thư viện lúc chạy LD_LIBRARY_PATH and ld.so's search order

    Biến LD_LIBRARY_PATH là danh sách thư mục ngăn cách bằng dấu hai chấm; nếu được đặt, trình liên kết động tìm trong các thư mục này trước các thư mục chuẩn. Thứ tự tìm của ld.so lúc chạy: (1) danh sách DT_RPATH trong tệp (nếu không có DT_RUNPATH); (2) LD_LIBRARY_PATH; (3) danh sách DT_RUNPATH; (4) /etc/ld.so.cache; (5) /lib rồi /usr/lib.12

    LD_LIBRARY_PATH là cách nói với máy "tìm thư viện trong các thư mục này trước". Lúc chạy, máy lần lượt soi theo một thứ tự cố định cho tới khi thấy thư viện. Nhớ nhé: LD_LIBRARY_PATH được soi trước các thư mục hệ thống.

    The LD_LIBRARY_PATH variable is a colon-separated list of directories; if set, the dynamic linker searches them before the standard directories. The run-time search order of ld.so: (1) the DT_RPATH list in the file (if there is no DT_RUNPATH); (2) LD_LIBRARY_PATH; (3) the DT_RUNPATH list; (4) /etc/ld.so.cache; (5) /lib then /usr/lib.12

    Bạn gặp nó ở đâuWhere you meet it Một lý do an toàn khiến không nên dựa vào LD_LIBRARY_PATH trong sản phẩm: với chương trình set-user-ID hay set-group-ID, trình liên kết động bỏ qua biến này, để người dùng không lừa nó nạp một thư viện giả cùng tên.12 Với các chương trình đặc quyền (set-UID), máy cố tình bỏ qua LD_LIBRARY_PATH để tránh bị lừa nạp thư viện giả. A security reason not to rely on LD_LIBRARY_PATH in production: for set-user-ID or set-group-ID programs, the dynamic linker ignores it, so users cannot trick it into loading a fake library of the same name.12

    rpath nhúng trong tệprpath embedded in the file

    Có thể nhúng sẵn thư mục tìm kiếm vào tệp thực thi bằng tùy chọn liên kết -rpath, tạo danh sách rpath. Đặc tả ELF cũ dùng thẻ DT_RPATH (ưu tiên cao hơn LD_LIBRARY_PATH); đặc tả mới dùng DT_RUNPATH (ưu tiên thấp hơn), bật bằng --enable-new-dtags. Hỗ trợ DT_RUNPATH được thêm từ glibc 2.2.15Search directories can be embedded in the executable with the -rpath linker option, forming an rpath list. The old ELF spec used the DT_RPATH tag (higher precedence than LD_LIBRARY_PATH); newer specs use DT_RUNPATH (lower precedence), enabled with --enable-new-dtags. Support for DT_RUNPATH was added in glibc 2.2.15

    $ORIGIN cho ứng dụng "cắm là chạy"$ORIGIN for turn-key apps

    Trình liên kết động hiểu chuỗi đặc biệt $ORIGIN trong rpath, nghĩa là "thư mục chứa ứng dụng". Nhờ đó, dựng với -Wl,-rpath,'$ORIGIN'/lib cho phép người dùng giải nén và chạy ứng dụng ở bất kỳ đâu mà không phải cài thư viện vào thư mục chuẩn.12The dynamic linker understands the special string $ORIGIN in an rpath, meaning "the directory containing the application." So building with -Wl,-rpath,'$ORIGIN'/lib lets a user unpack and run the application anywhere without installing libraries into a standard directory.12

  3. ldd / nm / objdump / readelf

    Bộ công cụ soi thư viện: ldd, nm, objdump, readelf Công cụ để xem bên trong thư viện The inspection toolkit: ldd, nm, objdump, readelf

    ldd(1) (list dynamic dependencies) liệt kê các thư viện dùng chung mà một chương trình cần, giải mỗi tên thành đường dẫn theo đúng quy ước tìm của trình liên kết động; với phần lớn tệp ELF, nó liệt kê ít nhất ld-linux.so.2 và libc.so.6.18 nm(1) liệt kê ký hiệu định nghĩa trong một thư viện hay chương trình — ví dụ tìm thư viện nào định nghĩa crypt(). objdump và readelf hiển thị thông tin từ các section ELF (và objdump còn gỡ hợp ngữ), mỗi lệnh một định dạng khác nhau.191011

    Bốn công cụ để "soi" thư viện: ldd cho biết một chương trình cần những thư viện nào; nm cho biết thư viện nào chứa một hàm; còn objdump và readelf cho xem các phần bên trong tệp ELF. Nhớ nhé: ldd xem cần gì, nm xem có gì, objdump/readelf xem bên trong.

    ldd(1) (list dynamic dependencies) lists the shared libraries a program requires, resolving each name to a path using the same conventions as the dynamic linker; for most ELF files it lists at least ld-linux.so.2 and libc.so.6.18 nm(1) lists the symbols defined in a library or program — for example, to find which library defines crypt(). objdump and readelf display information from the ELF sections (and objdump can disassemble), each in a different format.191011

    Bạn gặp nó ở đâuWhere you meet it Khi một chương trình báo thiếu thư viện, ldd prog là lệnh đầu tiên nên chạy: những dòng not found chỉ ra đúng thư viện mà trình liên kết động không giải được.18 Khi chương trình kêu thiếu thư viện, chạy ldd prog để xem nó đang thiếu thư viện nào. When a program complains about a missing library, ldd prog is the first command to run: the not found lines point to exactly the library the dynamic linker could not resolve.18

    Mẹo dùng nmAn nm tip

    Cờ -A của nm in tên thư viện ở đầu mỗi dòng ký hiệu — cần thiết để lọc, ví dụ nm -A /usr/lib/lib*.so 2> /dev/null | grep ' crypt$' cho thấy crypt() được định nghĩa trong libcrypt.19The -A flag to nm prints the library name at the start of each symbol line — needed for filtering, e.g. nm -A /usr/lib/lib*.so 2> /dev/null | grep ' crypt$' shows that crypt() is defined in libcrypt.19

    Đọc thêmFurther reading

    Phần "Further information" của chương dẫn tới các trang man ar(1), gcc(1), ld(1), ldconfig(8), ld.so(8), dlopen(3), objdump(1) cùng tài liệu info của ld và readelf — đúng bộ nguồn sơ cấp cho mọi chi tiết ở đây.112The chapter's "Further information" points to the ar(1), gcc(1), ld(1), ldconfig(8), ld.so(8), dlopen(3), objdump(1) manual pages and the info documentation for ld and readelf — exactly the primary sources for every detail here.112

Ba ngộ nhận thường gặp Three common misconceptions

“Dùng chung nghĩa là các tiến trình chia sẻ cả biến” “Sharing means processes share the variables too”

Ngộ nhận: nếu nhiều tiến trình dùng chung một thư viện thì chúng cũng dùng chung các biến của thư viện đó.

Thực tế: chỉ mã được chia sẻ; mỗi tiến trình có bản sao riêng của các biến toàn cục và tĩnh định nghĩa trong thư viện.1

Myth: if several processes share a library, they also share that library's variables.

Reality: only the code is shared; each process has its own copies of the global and static variables defined in the library.1

“Tên nhúng trong tệp thực thi là tên tệp thật” “The name embedded in the executable is the real filename”

Ngộ nhận: tệp thực thi luôn ghi tên tệp thật của thư viện (libfoo.so.1.0.1).

Thực tế: khi thư viện có soname, chính soname (libfoo.so.1) được nhúng vào — đó là lớp gián tiếp cho phép đổi bản minor mà không liên kết lại.16

Myth: the executable always records the library's real filename (libfoo.so.1.0.1).

Reality: when the library has a soname, the soname (libfoo.so.1) is embedded instead — the indirection that lets the minor version change without relinking.16

“LD_LIBRARY_PATH là cách đúng để tìm thư viện trong sản phẩm” “LD_LIBRARY_PATH is the right way to find libraries in production”

Ngộ nhận: cứ đặt LD_LIBRARY_PATH là xong, dùng mãi cũng được.

Thực tế: ứng dụng sản phẩm không nên dựa vào nó; nó bị bỏ qua với chương trình set-UID/GID. Hãy cài vào thư mục chuẩn và chạy ldconfig.12

Myth: just set LD_LIBRARY_PATH and you are done, forever.

Reality: production applications should not rely on it; it is ignored for set-UID/GID programs. Install into a standard directory and run ldconfig.12

Tự kiểm Self-check

Năm câu hỏi để biết bạn đã nắm cơ chế thư viện dùng chung Năm câu hỏi kiểm tra nhanh Five questions to check you grasped shared libraries

Trả lời được những câu này nghĩa là bạn đã nắm vì sao có thư viện dùng chung, cách dựng và dùng nó, cùng cơ chế tên và tìm kiếm lúc chạy — nền cho các chủ đề nâng cao như nạp động với dlopen().

Nếu trả lời được, nghĩa là bạn đã hiểu chương này.

Answering these means you grasp why shared libraries exist, how to build and use them, and the naming and run-time search machinery — the groundwork for advanced topics such as dynamic loading with dlopen().

  1. Phân biệt thư viện tĩnh và thư viện dùng chung. Nêu ba nhược điểm của sự dư thừa trong liên kết tĩnh.

    Gợi ý sâu hơn: khi liên kết dùng chung, thứ gì được ghi vào tệp thực thi thay cho bản sao mã, và thẻ ELF nào biểu diễn nó?

  2. -fPIC làm gì, và vì sao mã độc lập vị trí lại cần cho thư viện dùng chung? Làm sao kiểm tra một tệp đã dùng -fPIC chưa?

    Gợi ý sâu hơn: phí tổn của mã độc lập vị trí là gì, và chuỗi TEXTREL cho biết điều gì?

  3. Phân biệt liên kết tĩnh và liên kết động. Trình liên kết động là ai và nằm ở đâu?

    Gợi ý sâu hơn: vì sao mọi chương trình đều qua pha liên kết tĩnh, nhưng chỉ chương trình dùng thư viện dùng chung mới qua pha liên kết động?

  4. Nêu ba cái tên của một thư viện (tên thật, soname, tên liên kết) và vai trò từng cái. Cái nào được nhúng vào tệp thực thi?

    Gợi ý sâu hơn: vì sao tên liên kết nên trỏ tới soname chứ không trỏ thẳng tên thật?

  5. ldconfig làm hai việc gì? Vì sao không nên dựa vào LD_LIBRARY_PATH cho ứng dụng sản phẩm?

    Gợi ý sâu hơn: nêu thứ tự năm bước mà ld.so dùng để tìm một thư viện lúc chạy.

  1. Thư viện tĩnh và thư viện dùng chung khác nhau chỗ nào?

  2. Cờ -fPIC dùng để làm gì?

  3. Khi chạy, ai đi tìm và nạp thư viện cho chương trình?

  4. Một thư viện có ba cái tên nào?

  5. Chạy lệnh gì sau khi cài một thư viện mới?

  1. Distinguish static from shared libraries. Give the three drawbacks of static-linking redundancy.

  2. What does -fPIC do, and why does a shared library need position-independent code? How do you check whether a file used -fPIC?

  3. Distinguish static from dynamic linking. Who is the dynamic linker and where does it live?

  4. Name a library's three names (real name, soname, linker name) and each one's role. Which is embedded in the executable?

  5. What two tasks does ldconfig perform? Why should production applications not rely on LD_LIBRARY_PATH?

Khép lại chương Closing the chapter

Một thư viện dùng chung là một bản mã duy nhất, nạp một lần, dùng chung bởi mọi chương trình: tệp thực thi chỉ ghi tên nó cần, và trình liên kết động tìm rồi nạp thư viện lúc chạy. Bộ ba tên — tên thật, soname, tên liên kết — cùng ldconfig điều phối việc chọn phiên bản và nâng cấp không cần liên kết lại. Nắm được nền tảng này, bạn đã sẵn sàng cho các tính năng nâng cao — nạp thư viện tùy ý lúc chạy bằng dlopen().112

Thư viện dùng chung là một bản mã treo chung cho nhiều chương trình: chương trình chỉ nhớ tên, còn máy đi tìm và nạp lúc chạy. Ba cái tên và ldconfig lo chuyện phiên bản. Nhớ nhé: một bản mã, nhiều người dùng, máy tự tìm.

A shared library is a single copy of the code, loaded once, shared by every program: the executable records only the name it needs, and the dynamic linker finds and loads the library at run time. The triad of names — real name, soname, linker name — together with ldconfig governs version selection and relink-free upgrades. Grasp this foundation and you are ready for the advanced features — loading libraries on demand at run time with dlopen().112