# Bộ liên kết (Linker) là gì? Nguyên lý hoạt động, cấu trúc và vai trò trong quá trình biên dịch
**Canonical:** https://wiki.quizzman.com/wiki/bo-lien-ket-linker-la-gi-nguyen-ly-hoat-dong-cau-truc-va-vai-tro-trong-qua-trinh-bien-dich
**Author:** virgorilabs_112522  |  **Published:** 2026-10-16  |  **Updated:** 2026-10-16

> Bộ liên kết (Linker) là một thành phần quan trọng trong hệ thống công cụ phát triển phần mềm, có nhiệm vụ kết hợp các tệp mã đối tượng, thư viện và những thành phần nhị phân khác để tạo ra tệp thực thi, thư viện dùng chung hoặc một tệp đối tượng mới.

# Bộ liên kết (Linker) là gì? Nguyên lý hoạt động, cấu trúc và vai trò trong quá trình biên dịch

## Tóm tắt

Bộ liên kết (Linker) là một thành phần quan trọng trong hệ thống công cụ phát triển phần mềm, có nhiệm vụ kết hợp các tệp mã đối tượng, thư viện và những thành phần nhị phân khác để tạo ra tệp thực thi, thư viện dùng chung hoặc một tệp đối tượng mới.

Trong quá trình liên kết, bộ liên kết thực hiện nhiều công việc như phân giải ký hiệu (Symbol Resolution), tái định vị địa chỉ (Relocation), bố trí các vùng mã lệnh và dữ liệu, xử lý phụ thuộc thư viện, xác định điểm vào chương trình và tạo ra những cấu trúc nhị phân cần thiết cho hệ điều hành hoặc bộ nạp chương trình.

Sự ra đời của bộ liên kết gắn liền với nhu cầu lập trình mô-đun, cho phép một chương trình được phát triển từ nhiều tệp nguồn và thư viện độc lập mà không cần biên dịch toàn bộ chương trình thành một đơn vị duy nhất.

Bài viết khảo sát nguồn gốc lịch sử, nguyên lý và các giai đoạn xử lý của bộ liên kết, đồng thời phân tích sự khác nhau giữa liên kết tĩnh và liên kết động, cơ chế hoạt động của bộ liên kết trên Linux, macOS và Windows, cũng như những vấn đề liên quan đến giao diện nhị phân ứng dụng (ABI), định dạng tệp đối tượng và tối ưu hóa tại thời điểm liên kết.

**Từ khóa:** bộ liên kết; trình biên dịch; mã đối tượng; phân giải ký hiệu; tái định vị; liên kết tĩnh; liên kết động; ELF; Mach-O; PE/COFF; ABI; LTO.



## 1. Bộ liên kết là gì?

### 1.1. Khái niệm

Bộ liên kết (Linker) là chương trình xử lý các tệp mã đối tượng (Object Files), thư viện và những dữ liệu nhị phân liên quan để tạo ra sản phẩm liên kết.

Sản phẩm có thể là:

Tệp thực thi (Executable File).
Thư viện dùng chung (Shared Library).
Thư viện liên kết động (Dynamic-Link Library).
Tệp đối tượng có thể tiếp tục được liên kết (Relocatable Object File).

GNU mô tả vai trò của bộ liên kết trong tài liệu _LD_:

ld combines a number of object and archive files, relocates their data and ties up symbol references.

_(Dịch nghĩa: ld kết hợp nhiều tệp đối tượng và tệp lưu trữ, thực hiện tái định vị dữ liệu và liên kết các tham chiếu ký hiệu.)_

Nguồn: GNU Binutils, _LD_, mục 1, _Overview_.[^1]

Về bản chất, bộ liên kết giải quyết một vấn đề: **làm thế nào để những thành phần chương trình được biên dịch riêng biệt có thể tham chiếu và hoạt động cùng nhau trong một sản phẩm nhị phân thống nhất?**

### 1.2. Tại sao cần bộ liên kết?

Xét một chương trình C được chia thành hai tệp.

**Tệp `math.c`:**

int add(int a, int b) {
return a + b;
}


**Tệp `main.c`:**

int add(int a, int b);

int main(void) {
return add(20, 22) != 42;
}


Trình biên dịch có thể biên dịch hai tệp riêng biệt:

gcc -c math.c -o math.o
gcc -c main.c -o main.o


Kết quả là:

math.o
main.o


Tệp `math.o` chứa mã máy của hàm `add`.

Tệp `main.o` chứa mã máy của hàm `main`, trong đó có lời gọi tới `add`.

Nhưng khi biên dịch riêng `main.c`, trình biên dịch không nhất thiết biết địa chỉ cuối cùng của hàm `add` trong chương trình thực thi.

Do đó, tệp đối tượng phải lưu thông tin để bộ liên kết có thể xác định hàm cần gọi và hoàn thiện tham chiếu đó.

Quá trình liên kết:

gcc main.o math.o -o program


Bộ liên kết sẽ xác định rằng ký hiệu `add` được định nghĩa trong `math.o`, kết hợp những thành phần cần thiết và xử lý các tham chiếu theo quy định của định dạng tệp đích.

Từ đó, chương trình có thể được xây dựng mà không cần đặt toàn bộ mã nguồn trong một tệp duy nhất.

Lưu ý: lệnh `gcc` ở ví dụ trên là lời gọi trình điều khiển biên dịch (Compiler Driver), thường tự bổ sung các đối tượng khởi động, thư viện và tùy chọn liên kết cần thiết. Nó không đồng nghĩa với việc chỉ truyền hai tệp đối tượng trực tiếp vào chương trình `ld`.

### 1.3. Vai trò trong chuỗi công cụ biên dịch

Trong một hệ thống biên dịch truyền thống, quá trình tạo tệp thực thi có thể được mô tả như sau:

MÃ NGUỒN
main.c       math.c
│            │
▼            ▼
Biên dịch và sinh mã
│            │
▼            ▼
main.o        math.o
│            │
└──────┬─────┘
│
▼
BỘ LIÊN KẾT
(Linker)
│
▼
program
│
▼
Hệ điều hành nạp và thực thi


Các công cụ đảm nhiệm những chức năng khác nhau:

| Thành phần | Chức năng |
|---|---|
| Trình biên dịch (Compiler) | Phân tích mã nguồn và tạo dạng biểu diễn hoặc mã máy |
| Trình hợp dịch (Assembler) | Chuyển mã hợp ngữ thành mã đối tượng |
| Bộ liên kết (Linker) | Kết hợp các đơn vị nhị phân và xử lý tham chiếu giữa chúng |
| Bộ nạp (Loader) | Thiết lập ảnh chương trình trong bộ nhớ để thực thi |
| Bộ liên kết động (Dynamic Linker) | Xử lý việc nạp và liên kết các thư viện động theo cơ chế của nền tảng |

Trong thực tế, một công cụ có thể đảm nhiệm nhiều giai đoạn. Trình biên dịch không bắt buộc phải xuất mã hợp ngữ ra tệp trung gian, và một số hệ thống có thể trực tiếp sinh tệp thực thi mà không sử dụng bộ liên kết độc lập.



## 2. Nguồn gốc và sự phát triển lịch sử

### 2.1. Từ chương trình đơn khối đến lập trình mô-đun

Trong những hệ thống máy tính đầu tiên, chương trình thường được viết và nạp theo những quy trình phụ thuộc mạnh vào tổ chức bộ nhớ cụ thể.

Khi chương trình ngày càng lớn, việc yêu cầu người lập trình xác định thủ công toàn bộ địa chỉ của hàm và dữ liệu trở nên khó quản lý.

Một chương trình có thể sử dụng cùng một thư viện toán học, thao tác tệp hoặc nhập xuất, nhưng không nên phải sao chép và điều chỉnh toàn bộ mã máy của thư viện cho từng ứng dụng.

Từ nhu cầu đó, các cơ chế nạp, tái định vị và liên kết chương trình từng bước phát triển.

Trong lịch sử, những khái niệm như bộ nạp liên kết (Linking Loader), bộ liên kết và bộ nạp không phải lúc nào cũng là những chương trình tách biệt. Một số hệ thống thực hiện việc giải quyết tham chiếu và tái định vị ngay khi nạp chương trình.

### 2.2. Bộ liên kết trong hệ sinh thái Unix

Sự phát triển của Unix và các công cụ biên dịch C góp phần phổ biến mô hình:

Biên dịch các tệp nguồn riêng biệt.
Tạo tệp đối tượng.
Tập hợp các đối tượng thành thư viện.
Liên kết chương trình ở giai đoạn cuối.

Các định dạng như `a.out`, về sau là COFF và ELF, cung cấp những cấu trúc để mô tả mã máy, dữ liệu, bảng ký hiệu và thông tin tái định vị.

Trong hệ sinh thái GNU, `ld` trở thành một bộ liên kết quan trọng, hỗ trợ nhiều kiến trúc và định dạng tệp thông qua thư viện BFD.

### 2.3. Sự xuất hiện của các bộ liên kết hiện đại

Khi quy mô phần mềm gia tăng, bộ liên kết phải xử lý hàng nghìn hoặc hàng triệu ký hiệu, nhiều thư viện và lượng lớn dữ liệu gỡ lỗi.

Các yêu cầu mới bao gồm:

Rút ngắn thời gian liên kết.
Hỗ trợ nhiều kiến trúc CPU.
Tối ưu kích thước tệp thực thi.
Loại bỏ mã không được sử dụng.
Liên kết với mã trung gian để tối ưu toàn chương trình.
Hỗ trợ thông tin gỡ lỗi và bảo mật.
Liên kết chéo giữa những nền tảng khác nhau.

Một số bộ liên kết được sử dụng rộng rãi gồm GNU `ld`, LLVM `lld`, `ld64` của Apple và `link.exe` của Microsoft.

LLVM LLD hỗ trợ nhiều nhóm định dạng như ELF, PE/COFF, Mach-O và WebAssembly thông qua những phần triển khai tương ứng.[^2]



## 3. Tệp đối tượng và những dữ liệu bộ liên kết cần xử lý

### 3.1. Tệp đối tượng (Object File)

Tệp đối tượng là sản phẩm trung gian có thể chứa mã máy, dữ liệu, thông tin ký hiệu, thông tin tái định vị và siêu dữ liệu khác phục vụ quá trình liên kết.

Ví dụ:

gcc -c hello.c -o hello.o


Tệp `hello.o` chưa nhất thiết là một chương trình có thể chạy độc lập.

Nó có thể chứa tham chiếu đến các hàm chưa được định nghĩa trong chính tệp đó.

Một bộ liên kết phải hiểu định dạng tệp đối tượng của nền tảng mà nó xử lý.

Ba nhóm định dạng quan trọng là:

| Định dạng | Nền tảng tiêu biểu | Đặc điểm |
|---|---|---|
| ELF (Executable and Linkable Format) | Linux, nhiều hệ Unix và hệ nhúng | Sử dụng section, segment, bảng ký hiệu và bản ghi tái định vị |
| Mach-O (Mach Object) | macOS, iOS và các nền tảng Apple | Tổ chức bằng header, load command, segment và section |
| COFF / PE (Common Object File Format / Portable Executable) | Windows | COFF thường dùng cho tệp đối tượng; PE cho ảnh thực thi và DLL |

Các định dạng không thể được coi là có cấu trúc giống hệt nhau, dù đều phục vụ những mục tiêu tương tự trong quá trình liên kết và thực thi.

### 3.2. Section và Segment

Section là một đơn vị tổ chức thông tin trong tệp đối tượng.

Trong ELF, những section thường gặp gồm:

| Section | Nội dung điển hình |
|---|---|
| `.text` | Mã lệnh |
| `.rodata` | Dữ liệu chỉ đọc |
| `.data` | Dữ liệu được khởi tạo và có thể ghi |
| `.bss` | Dữ liệu chưa khởi tạo, có dung lượng bộ nhớ nhưng không cần lưu đầy đủ byte trong tệp |
| `.symtab` | Bảng ký hiệu |
| `.strtab` | Bảng chuỗi |
| `.rela.text` | Thông tin tái định vị đối với mã lệnh, khi có |

Segment, trong ngữ cảnh ảnh thực thi ELF, mô tả các vùng được nạp hoặc xử lý theo yêu cầu của hệ điều hành.

Section phục vụ mạnh cho cách nhìn khi liên kết; segment phục vụ mạnh cho cách nhìn khi nạp và thực thi.

Một segment có thể bao gồm dữ liệu từ nhiều section.

Đặc tả ELF mô tả hai cách nhìn đối với tệp: cách nhìn liên kết và cách nhìn thực thi.[^3]

### 3.3. Bảng ký hiệu (Symbol Table)

Bảng ký hiệu lưu thông tin về những tên mà bộ liên kết và các công cụ xử lý nhị phân cần nhận diện.

Ký hiệu có thể đại diện cho:

Hàm.
Biến toàn cục.
Đối tượng dữ liệu.
Ký hiệu do linker script định nghĩa.
Một vị trí trong mã hoặc dữ liệu.

Mỗi ký hiệu có thể đi kèm tên, giá trị hoặc địa chỉ, kích thước, phạm vi liên kết, kiểu và trạng thái định nghĩa.

Ví dụ:

int shared_value = 10;

int get_value(void) {
return shared_value;
}


Một tệp đối tượng có thể chứa ký hiệu `shared_value` và `get_value` với thông tin về vị trí hoặc section của chúng.

Nếu tệp khác sử dụng `get_value`, bộ liên kết phải xác định định nghĩa phù hợp.



## 4. Phân giải ký hiệu (Symbol Resolution)

### 4.1. Ký hiệu được định nghĩa và chưa được định nghĩa

Xét hai tệp:

**`provider.c`:**

int calculate(int x) {
return x * 2;
}


**`consumer.c`:**

extern int calculate(int x);

int process(void) {
return calculate(21);
}


Sau khi biên dịch riêng:

gcc -c provider.c -o provider.o
gcc -c consumer.c -o consumer.o


Có thể dùng công cụ `nm` để xem ký hiệu:

nm provider.o
nm consumer.o


Kết quả điển hình có thể bao gồm:

provider.o:
T calculate

consumer.o:
U calculate
T process


Trong cách hiển thị thông dụng của `nm`:

`T` biểu thị ký hiệu được định nghĩa trong vùng mã lệnh tương ứng.
`U` biểu thị ký hiệu chưa được định nghĩa trong tệp đối tượng đang xét.

Khi liên kết, bộ liên kết tìm định nghĩa thích hợp của `calculate` và xử lý tham chiếu từ `consumer.o`.

### 4.2. Các nhóm ký hiệu

Tùy định dạng tệp đối tượng, ký hiệu có thể có phạm vi và đặc tính khác nhau.

Trong ELF, một số nhóm quan trọng gồm:

| Phân loại | Ý nghĩa |
|---|---|
| Ký hiệu cục bộ (Local Symbol) | Không được sử dụng để phân giải tham chiếu từ các đơn vị đối tượng khác theo cách của ký hiệu toàn cục |
| Ký hiệu toàn cục (Global Symbol) | Có thể tham gia phân giải giữa các đối tượng |
| Ký hiệu yếu (Weak Symbol) | Có quy tắc phân giải riêng, thường có độ ưu tiên thấp hơn định nghĩa toàn cục thông thường |
| Ký hiệu chưa định nghĩa (Undefined Symbol) | Tham chiếu cần được giải quyết bởi đối tượng hoặc thư viện khác, trừ trường hợp được cho phép để lại |

Các quy tắc chính xác phụ thuộc vào định dạng và loại liên kết.

Không nên xem ký hiệu yếu là một cơ chế hoàn toàn giống nhau trên ELF, Mach-O và COFF.

### 4.3. Lỗi ký hiệu chưa được định nghĩa

Nếu liên kết một chương trình yêu cầu `calculate` nhưng không cung cấp định nghĩa phù hợp, bộ liên kết có thể báo lỗi.

Ví dụ:

undefined reference to `calculate`


Những nguyên nhân thường gặp:

Thiếu tệp đối tượng.
Thiếu thư viện.
Tên ký hiệu không khớp.
Thư viện không tương thích với kiến trúc đích.
Quy tắc tìm kiếm thư viện khiến thành phần cần thiết không được chọn.
Khác biệt ABI hoặc cơ chế biến đổi tên trong ngôn ngữ như C++.

Tuy nhiên, một số loại đầu ra như thư viện dùng chung có thể cho phép giữ lại tham chiếu chưa được giải quyết đến thời điểm nạp hoặc thực thi, tùy nền tảng và tùy chọn liên kết.

### 4.4. Ký hiệu trùng lặp

Nếu hai tệp đối tượng cùng cung cấp định nghĩa mạnh của một ký hiệu toàn cục, bộ liên kết thường phải báo lỗi trùng định nghĩa.

Ví dụ:

**`a.c`:**

int counter = 10;


**`b.c`:**

int counter = 20;


Liên kết cả hai tệp có thể dẫn tới lỗi:

multiple definition of `counter`


Việc xử lý còn phụ thuộc vào quy tắc ngôn ngữ, loại ký hiệu, cách biên dịch và các tùy chọn đặc biệt.



## 5. Tái định vị địa chỉ (Relocation)

### 5.1. Vì sao cần tái định vị?

Khi một tệp nguồn được biên dịch riêng, trình biên dịch thường chưa biết bố cục cuối cùng của toàn bộ chương trình.

Một hàm có thể gọi một hàm khác nằm trong tệp đối tượng riêng.

Một biến có thể được đặt ở vùng dữ liệu có địa chỉ chỉ được xác định sau khi liên kết.

Vì vậy, trình biên dịch và trình hợp dịch tạo ra các thông tin tái định vị để bộ liên kết có thể hoàn thiện mã máy hoặc dữ liệu sau khi xác định vị trí các thành phần.

Tái định vị là quá trình áp dụng những phép tính cần thiết để điều chỉnh các trường chứa địa chỉ hoặc độ lệch trong mã và dữ liệu.

### 5.2. Ví dụ địa chỉ tương đối

Giả sử mã máy có một lệnh nhảy tương đối đến hàm `target`.

Khi tạo tệp đối tượng, vị trí cuối cùng của `target` chưa được xác định.

Bộ liên kết sau đó bố trí:

caller: 0x00001000
target: 0x00001800


Nếu một kiểu tái định vị dùng địa chỉ tương đối, bộ liên kết phải tính độ lệch theo đúng quy tắc của kiến trúc và loại relocation.

Không thể mặc nhiên sử dụng phép trừ đơn giản giữa hai địa chỉ hàm, bởi nhiều loại relocation tính độ lệch từ vị trí của trường được vá hoặc từ địa chỉ lệnh kế tiếp, và có thể yêu cầu xử lý addend, chia tỷ lệ hoặc kiểm tra phạm vi giá trị.

### 5.3. Các thành phần trong phép tái định vị

Trong nhiều đặc tả ABI, phép tái định vị có thể được mô tả bằng những đại lượng:

■: Giá trị hoặc địa chỉ của ký hiệu được tham chiếu.
■: Số hạng bổ sung (Addend).
■: Vị trí áp dụng tái định vị.
■: Địa chỉ cơ sở nạp, đối với các loại relocation cần sử dụng.

Ví dụ, một loại relocation PC-relative có thể sử dụng công thức:

■

Một loại relocation tuyệt đối có thể sử dụng:

■

Đây là những dạng công thức điển hình, không phải công thức chung áp dụng cho tất cả loại relocation.

Đặc tả ABI của từng kiến trúc sẽ quy định cách xác định đại lượng, phép tính, số bit được ghi và điều kiện tràn phạm vi.

### 5.4. Bản ghi tái định vị

Trong ELF, thông tin tái định vị được tổ chức bằng các bản ghi, thường thuộc hai dạng:

`REL`: Addend có thể được lấy từ nội dung tại vị trí tái định vị.
`RELA`: Addend được lưu rõ trong bản ghi.

Một bản ghi tái định vị thường cung cấp thông tin về vị trí cần sửa, ký hiệu liên quan và loại phép tái định vị.

Các kiểu relocation cũng phụ thuộc vào kiến trúc CPU.

Chẳng hạn, x86-64 và AArch64 có tập loại relocation khác nhau, phản ánh sự khác nhau trong cách mã hóa địa chỉ và lệnh máy.

### 5.5. Tái định vị tại thời điểm liên kết và thời điểm nạp

Không phải mọi tái định vị đều được xử lý hoàn toàn khi tạo tệp thực thi.

Có thể phân thành hai trường hợp cơ bản:

**Tái định vị tại thời điểm liên kết (Link-time Relocation):** Bộ liên kết áp dụng phép tái định vị khi tạo sản phẩm đầu ra.

**Tái định vị tại thời điểm nạp (Load-time Relocation):** Bộ nạp hoặc bộ liên kết động xử lý những thông tin tái định vị còn lại khi chương trình được nạp vào bộ nhớ.

Tùy loại nhị phân, hệ điều hành và ABI, một phần công việc cũng có thể được trì hoãn đến thời điểm sử dụng ký hiệu.

Phân chia trách nhiệm này đặc biệt quan trọng trong cơ chế liên kết động và mã độc lập vị trí.



## 6. Liên kết tĩnh (Static Linking)

### 6.1. Khái niệm

Liên kết tĩnh là phương thức trong đó những thành phần mã máy cần thiết từ thư viện tĩnh được đưa vào sản phẩm liên kết tại thời điểm xây dựng.

Trên hệ thống Unix, thư viện tĩnh thường có phần mở rộng `.a`.

Trên Windows, thư viện tĩnh thường sử dụng phần mở rộng `.lib`, nhưng `.lib` cũng có thể là thư viện nhập (Import Library) phục vụ liên kết với DLL.

Vì vậy, không thể chỉ nhìn phần mở rộng `.lib` để kết luận một thư viện chứa mã triển khai tĩnh.

### 6.2. Ví dụ

Biên dịch thư viện:

gcc -c math.c -o math.o
ar rcs libmath.a math.o


Liên kết chương trình:

gcc main.o ./libmath.a -o program


Bộ liên kết có thể lấy những thành phần phù hợp từ thư viện để thỏa mãn tham chiếu trong chương trình.

Một thư viện tĩnh không nhất thiết được sao chép toàn bộ vào tệp thực thi. Nhiều bộ liên kết lựa chọn những thành viên đối tượng cần thiết trong archive.

Các phép tối ưu và cấu hình liên kết còn có thể tiếp tục loại bỏ những section hoặc hàm không được sử dụng.

### 6.3. Đặc điểm

Ưu điểm thường gặp của liên kết tĩnh:

Giảm phụ thuộc vào việc tìm đúng một số thư viện bên ngoài tại thời điểm chạy.
Hỗ trợ việc phân phối chương trình dưới dạng nhị phân tương đối độc lập.
Có thể đơn giản hóa triển khai trong một số môi trường nhúng hoặc hệ thống tối giản.

Giới hạn thường gặp:

Kích thước tệp thực thi có thể tăng.
Nhiều chương trình có thể chứa các bản sao riêng của mã thư viện.
Việc cập nhật một thư viện có thể đòi hỏi liên kết lại và phân phối lại chương trình.
Không bảo đảm chương trình hoàn toàn độc lập với hệ điều hành hoặc mọi thành phần runtime.

### 6.4. Thứ tự thư viện

Trong cách xử lý archive truyền thống của GNU `ld`, thứ tự đối tượng và thư viện trên dòng lệnh có thể ảnh hưởng tới kết quả liên kết.

Ví dụ:

gcc main.o -L. -lmath -o program


Nếu `main.o` chứa tham chiếu chưa được giải quyết tới một hàm trong `libmath.a`, bộ liên kết có thể trích xuất thành viên cung cấp định nghĩa đó.

Ngược lại:

gcc -L. -lmath main.o -o program


có thể không hoạt động như mong muốn trong kiểu tìm kiếm archive tuần tự truyền thống.

GNU `ld` mô tả rằng archive được tìm kiếm tại vị trí nó xuất hiện trên dòng lệnh, thay vì mặc nhiên tìm kiếm lại mỗi khi phát sinh ký hiệu chưa được giải quyết ở những đầu vào phía sau.[^1]

Tùy bộ liên kết và tùy chọn sử dụng, cách xử lý có thể khác. Không nên coi quy tắc trên là hành vi bắt buộc của mọi linker.



## 7. Liên kết động (Dynamic Linking)

### 7.1. Khái niệm

Liên kết động là cơ chế cho phép chương trình sử dụng mã và dữ liệu từ những thư viện được nạp hoặc liên kết theo các quy tắc của hệ thống khi thực thi.

Các thư viện động thường gặp:

| Nền tảng | Dạng thư viện |
|---|---|
| Linux | `.so` |
| macOS | `.dylib` |
| Windows | `.dll` |

Trong mô hình này, bộ liên kết khi xây dựng chương trình thường ghi lại những phụ thuộc và thông tin cần thiết để hệ thống có thể tìm và kết nối các thư viện lúc nạp hoặc thực thi.

### 7.2. Ví dụ trên Linux

Tạo thư viện dùng chung:

gcc -fPIC -shared math.c -o libmath.so


Liên kết chương trình:

gcc main.o -L. -lmath \
-Wl,-rpath,'$ORIGIN' \
-o program


Trong ví dụ này, `$ORIGIN` là cơ chế biểu diễn đường dẫn tương đối với vị trí tệp thực thi được hỗ trợ trong ngữ cảnh ELF của một số bộ nạp động.

Cách hoạt động chính xác còn phụ thuộc vào nền tảng và môi trường thực thi.

### 7.3. Bộ liên kết động và bộ liên kết khi xây dựng

Hai thành phần thường bị gọi chung là linker nhưng thực hiện công việc tại những thời điểm khác nhau.

| Tiêu chí | Bộ liên kết khi xây dựng | Bộ liên kết động |
|---|---|---|
| Thời điểm | Khi tạo nhị phân | Khi nạp hoặc thực thi |
| Đầu vào chính | Tệp đối tượng, thư viện, tùy chọn liên kết | Ảnh nhị phân, thư viện động và siêu dữ liệu nạp |
| Công việc | Kết hợp, bố trí, phân giải và tái định vị | Nạp phụ thuộc, xử lý các tham chiếu và tái định vị còn lại |
| Ví dụ | `ld`, `ld.lld`, `ld64`, `link.exe` | Bộ nạp liên kết động theo nền tảng, như `ld.so` hoặc `dyld` |

Trên Windows, việc nạp DLL và giải quyết các bảng nhập được thực hiện thông qua cơ chế bộ nạp của hệ điều hành; mô hình này không đồng nhất với cách tổ chức `ld.so` trên Linux.

### 7.4. Những cơ chế kỹ thuật thường gặp

Trong các hệ thống ELF, liên kết động thường liên quan đến:

Bảng ký hiệu động.
Thông tin phụ thuộc thư viện.
Những bản ghi tái định vị động.
Bảng địa chỉ toàn cục (Global Offset Table – GOT).
Bảng thủ tục liên kết (Procedure Linkage Table – PLT), khi được sử dụng.
Các cơ chế liên kết sớm hoặc liên kết trì hoãn.

GOT và PLT là những cơ chế quan trọng trong nhiều triển khai ELF, nhưng không phải mọi lời gọi hàm động đều nhất thiết sử dụng đầy đủ cả hai cấu trúc theo cùng một cách.

Cũng không nên áp dụng tên gọi GOT và PLT một cách máy móc cho tất cả cơ chế liên kết động của Mach-O hoặc PE.

### 7.5. Ưu điểm và giới hạn

Liên kết động có thể:

Cho phép nhiều tiến trình sử dụng chung các trang mã thư viện khi hệ điều hành hỗ trợ.
Giảm kích thước nhị phân chính.
Tạo điều kiện thay đổi và cập nhật thư viện độc lập trong phạm vi tương thích ABI.
Hỗ trợ kiến trúc ứng dụng có thể mở rộng bằng thư viện.

Tuy nhiên, nó cũng phát sinh những vấn đề như:

Thiếu thư viện khi triển khai.
Không tương thích phiên bản.
Xung đột tên ký hiệu.
Khác biệt ABI.
Quy tắc tìm kiếm thư viện.
Rủi ro bảo mật liên quan đến đường dẫn hoặc nguồn thư viện không đáng tin cậy.



## 8. Linker Script và cách bố trí bộ nhớ

### 8.1. Linker Script là gì?

Tập lệnh liên kết (Linker Script) là một tệp mô tả hoặc điều khiển cách bộ liên kết bố trí các section, địa chỉ, vùng bộ nhớ và những yếu tố khác trong sản phẩm đầu ra.

Trong GNU `ld`, linker script giữ vai trò đặc biệt quan trọng khi cần tạo những nhị phân có sơ đồ bộ nhớ tùy chỉnh.

GNU mô tả:

Every link is controlled by a linker script.

_(Dịch nghĩa: Mỗi quá trình liên kết đều được điều khiển bởi một tập lệnh liên kết.)_

Theo cơ chế của GNU `ld`, nếu người dùng không cung cấp script riêng, bộ liên kết có thể sử dụng script mặc định phù hợp với chế độ liên kết.[^1]

### 8.2. Ví dụ cơ bản

ENTRY(_start)

SECTIONS
{
. = 0x10000;

.text : {
*(.text)
*(.text.*)
}

.rodata : {
*(.rodata)
*(.rodata.*)
}

.data : {
*(.data)
*(.data.*)
}

.bss : {
*(.bss)
*(.bss.*)
*(COMMON)
}
}


Đây là một ví dụ minh họa cú pháp GNU `ld`, không phải linker script hoàn chỉnh cho mọi hệ điều hành.

Các thành phần chính:

| Thành phần | Chức năng |
|---|---|
| `ENTRY(_start)` | Xác định ký hiệu điểm vào theo cơ chế của linker |
| `SECTIONS` | Mô tả cách bố trí section đầu ra |
| `.` | Bộ đếm vị trí (Location Counter) |
| `.text` | Nhóm các section mã lệnh |
| `.rodata` | Nhóm dữ liệu chỉ đọc |
| `.data` | Nhóm dữ liệu được khởi tạo |
| `.bss` | Nhóm vùng dữ liệu chưa khởi tạo |

Một sản phẩm thực thi thực tế còn cần đáp ứng yêu cầu về segment, quyền truy cập bộ nhớ, căn chỉnh, ABI và bộ nạp.

### 8.3. Địa chỉ thực thi và địa chỉ nạp

Trong các hệ thống nhúng, có thể cần xác định riêng:

Địa chỉ bộ nhớ khi chương trình thực thi (Virtual Memory Address – VMA).
Địa chỉ nơi nội dung được lưu hoặc nạp ban đầu (Load Memory Address – LMA).

Ví dụ, chương trình có thể lưu mã hoặc dữ liệu khởi tạo trong bộ nhớ Flash, nhưng yêu cầu sao chép một số dữ liệu sang RAM khi khởi động.

Linker script có thể xác định những địa chỉ và ký hiệu giúp mã khởi động thực hiện quá trình đó.

Tuy nhiên, **bộ liên kết không tự động sao chép dữ liệu từ Flash sang RAM trong lúc chương trình chạy**. Nó tạo ra bố cục và thông tin cần thiết; phần thực thi thực tế thuộc về mã khởi động hoặc cơ chế nạp.

### 8.4. Vai trò trong hệ điều hành và chương trình nhúng

Linker script thường quan trọng trong:

Hệ điều hành tự phát triển.
Kernel.
Bootloader.
Firmware.
Chương trình bare-metal.
Các hệ thống có bộ nhớ được phân vùng chặt chẽ.

Đối với các môi trường này, quyền kiểm soát địa chỉ và bố cục nhị phân có thể là yêu cầu chức năng chứ không chỉ là tùy chọn tối ưu.



## 9. Cơ chế liên kết trên Linux, macOS và Windows

### 9.1. Linux và định dạng ELF

Trong các hệ thống Linux sử dụng ELF, bộ liên kết tạo đầu ra tuân theo quy định của ELF và ABI kiến trúc đích.

Những nội dung quan trọng thường gồm:

Section và segment.
Bảng ký hiệu.
Tái định vị.
Thông tin liên kết động.
Điểm vào chương trình.
Program header table.

Các bộ liên kết thường gặp gồm GNU `ld` và LLVM `ld.lld`.

### 9.2. macOS và định dạng Mach-O

Trên macOS, định dạng Mach-O được sử dụng cho các tệp đối tượng và ảnh nhị phân thực thi.

Bộ liên kết phải tạo những cấu trúc phù hợp với Mach-O, bao gồm:

Mach header.
Load commands.
Segments và sections.
Thông tin ký hiệu và tái định vị.
Các thông tin phục vụ liên kết động.
Những siêu dữ liệu cần thiết theo yêu cầu nền tảng.

Apple sử dụng bộ liên kết `ld64`; LLVM cũng có phần triển khai Mach-O trong LLD.[^4]

Một điểm cần chú ý là việc tạo ra tệp Mach-O đúng cấu trúc chưa đủ để bảo đảm hệ điều hành sẽ cho phép thực thi. Các yêu cầu như mã ký, chính sách tải mã và nền tảng đích cũng có thể ảnh hưởng đến khả năng chạy chương trình.

### 9.3. Windows và định dạng PE/COFF

Trong hệ sinh thái Windows, COFF thường được sử dụng cho các tệp đối tượng, còn PE được dùng cho những ảnh thực thi như EXE và DLL.

Bộ liên kết phải xử lý:

Ký hiệu COFF.
Bản ghi tái định vị.
Bảng section.
Bảng nhập (Import Table).
Bảng xuất (Export Table).
Thông tin tái định vị cơ sở.
Các cấu trúc đặc thù của PE.

Microsoft quy định những cấu trúc này trong đặc tả PE/COFF.[^5]

Các bộ liên kết phổ biến gồm `link.exe` và chế độ `lld-link` của LLVM.

### 9.4. So sánh ba định dạng

| Tiêu chí | ELF | Mach-O | PE/COFF |
|---|---|---|---|
| Nền tảng tiêu biểu | Linux | macOS và hệ Apple | Windows |
| Đơn vị cấu trúc | Section, segment, header | Segment, section, load command | Header, section, data directory |
| Ký hiệu | Bảng ký hiệu ELF | Bảng ký hiệu và cơ chế liên kết Mach-O | Ký hiệu COFF, import/export |
| Tái định vị | Theo ELF và ABI kiến trúc | Theo Mach-O và kiến trúc đích | COFF relocation và PE base relocation |
| Bộ liên kết tiêu biểu | GNU ld, LLD | ld64, LLD Mach-O | link.exe, lld-link |
| Nạp thư viện động | Cơ chế loader ELF | dyld | Windows loader |

Bảng chỉ mô tả đặc điểm tổng quát. Mỗi định dạng có nhiều phiên bản, biến thể và quy định bổ sung.



## 10. Các tối ưu hóa trong bộ liên kết

### 10.1. Loại bỏ section không sử dụng

Bộ liên kết có thể loại bỏ các section không cần thiết đối với sản phẩm cuối cùng.

Trong GNU `ld` và LLD ở chế độ ELF, các cơ chế như `--gc-sections` hỗ trợ loại bỏ những section không được giữ lại bởi quá trình phân tích tham chiếu và các quy tắc giữ section.

Ví dụ:

gcc -O2 -ffunction-sections -fdata-sections \
-Wl,--gc-sections \
main.c math.c -o program


Tùy chọn `-ffunction-sections` và `-fdata-sections` giúp đặt những hàm hoặc dữ liệu thích hợp vào các section riêng, tạo điều kiện để linker có thể loại bỏ chúng ở mức chi tiết hơn.

Tuy nhiên, không phải mọi section không được tham chiếu trực tiếp đều có thể bị loại bỏ. Các quy tắc giữ lại, điểm vào, metadata và yêu cầu của runtime vẫn phải được xem xét.

### 10.2. Gộp mã hoặc dữ liệu tương đương

Một số bộ liên kết hỗ trợ gộp các section tương đương, thường được gọi là gộp mã giống hệt (Identical Code Folding – ICF) trong những ngữ cảnh cụ thể.

Việc gộp này có thể giảm kích thước tệp thực thi.

Tuy nhiên, việc xem hai hàm là tương đương ở mức byte chưa luôn đồng nghĩa với việc có thể gộp chúng mà không ảnh hưởng đến những đặc tính có thể quan sát của chương trình.

Do đó, bộ liên kết và các tùy chọn tối ưu cần xét đến các ràng buộc về địa chỉ, ngữ nghĩa và ABI.

### 10.3. Tối ưu hóa tại thời điểm liên kết

Tối ưu hóa tại thời điểm liên kết (Link-Time Optimization – LTO) cho phép những phép tối ưu được thực hiện trên phạm vi nhiều đơn vị biên dịch.

Thay vì chỉ liên kết mã máy đã hoàn thiện, hệ thống công cụ có thể truyền biểu diễn trung gian hoặc thông tin phù hợp đến giai đoạn liên kết.

Khi đó, các phép tối ưu có thể bao gồm:

Nội tuyến hàm giữa nhiều đơn vị biên dịch.
Loại bỏ hàm không thể được sử dụng.
Lan truyền hằng số xuyên mô-đun.
Chuyên biệt hóa hoặc đơn giản hóa những đoạn mã có đủ thông tin.

Ví dụ:

gcc -O2 -flto -c math.c -o math.o
gcc -O2 -flto -c main.c -o main.o
gcc -O2 -flto main.o math.o -o program


LTO đòi hỏi sự phối hợp giữa trình biên dịch, bộ liên kết và cơ chế plugin hoặc công cụ hỗ trợ tương ứng.

**Không nên xem LTO là một thuật toán do linker đơn độc thực hiện.** Trong nhiều hệ thống, linker điều phối hoặc kích hoạt các bước tối ưu do thành phần của trình biên dịch triển khai.

### 10.4. Liên kết gia tăng

Liên kết gia tăng (Incremental Linking) hướng đến việc tránh thực hiện lại toàn bộ công việc liên kết khi chỉ một phần đầu vào thay đổi.

Kỹ thuật này đặc biệt hữu ích trong những dự án có thời gian xây dựng lớn.

Tuy nhiên, khả năng liên kết gia tăng, giới hạn của nó và sự tương thích với các phép tối ưu khác phụ thuộc vào từng bộ liên kết.



## 11. Những lỗi liên kết thường gặp

### 11.1. Ký hiệu không được định nghĩa

Ví dụ:

undefined reference to `foo`


Nguyên nhân có thể là thiếu đối tượng, thiếu thư viện hoặc tên ký hiệu không khớp.

Phương pháp kiểm tra:

nm file.o
nm library.a


### 11.2. Trùng định nghĩa

Ví dụ:

multiple definition of `counter`


Cần xem xét những nơi định nghĩa ký hiệu, phạm vi liên kết và cách tổ chức biến hoặc hàm giữa các đơn vị biên dịch.

### 11.3. Không tương thích kiến trúc

Ví dụ, một chương trình ARM64 không thể tùy tiện liên kết với tệp đối tượng x86-64 như thể cả hai chứa mã máy cùng kiến trúc.

Bộ liên kết cần kiểm tra định dạng, loại máy, ABI và những thuộc tính có liên quan.

### 11.4. Relocation vượt phạm vi

Một số loại relocation có giới hạn về phạm vi độ lệch.

Nếu mục tiêu nằm ngoài phạm vi được mã hóa bởi loại lệnh hoặc relocation đang sử dụng, liên kết có thể thất bại.

Trong những hệ thống hỗ trợ, linker có thể tạo đoạn mã trung gian như thunk hoặc veneer để giải quyết một số trường hợp nhảy xa. Nhưng không phải mọi loại relocation vượt phạm vi đều có thể được tự động sửa.

### 11.5. Thư viện động không được tìm thấy

Đây thường là vấn đề xảy ra khi nạp hoặc thực thi, không nhất thiết ở thời điểm liên kết xây dựng.

Chẳng hạn, chương trình đã được liên kết thành công nhưng hệ thống không tìm thấy thư viện động cần thiết.

Vì vậy, cần xác định lỗi phát sinh từ:

Bộ liên kết khi xây dựng.
Bộ nạp hoặc bộ liên kết động.
Cơ chế tìm kiếm thư viện.
Tính tương thích nhị phân của thư viện.



## 12. Thử nghiệm thực tế

### 12.1. Tạo hai tệp đối tượng

**`math.c`:**

int add(int a, int b) {
return a + b;
}


**`main.c`:**

int add(int a, int b);

int main(void) {
return add(20, 22) != 42;
}


Biên dịch riêng:

gcc -O0 -c math.c -o math.o
gcc -O0 -c main.c -o main.o


### 12.2. Kiểm tra ký hiệu

nm math.o
nm main.o


Tìm ký hiệu `add` được định nghĩa trong `math.o` và tham chiếu chưa được giải quyết trong `main.o`.

### 12.3. Kiểm tra relocation

Trên nền tảng ELF:

readelf -r main.o


Kết quả thường chứa bản ghi tái định vị liên quan đến lời gọi `add`, nếu trình biên dịch đã tạo lời gọi cần được giải quyết tại thời điểm liên kết.

Loại relocation cụ thể phụ thuộc vào kiến trúc và cách sinh mã.

### 12.4. Liên kết và quan sát

gcc main.o math.o -o program


Kiểm tra:

nm program
objdump -d program


Đối chiếu cách tham chiếu `add` trong tệp đối tượng trước và sau khi liên kết.

Trong trường hợp mục tiêu là Mach-O hoặc PE/COFF, cần sử dụng các công cụ và tùy chọn phù hợp với định dạng tương ứng, thay vì mặc nhiên sử dụng `readelf`.

### 12.5. Tạo bản đồ liên kết

Trên môi trường GNU ELF có hỗ trợ tùy chọn tương ứng:

gcc main.o math.o \
-Wl,-Map=program.map \
-o program


Tệp `program.map` có thể cung cấp thông tin về bố cục liên kết, những section đầu vào, ký hiệu và vị trí của chúng.

Đây là một nguồn dữ liệu hữu ích khi nghiên cứu linker, tối ưu bố cục bộ nhớ và phân tích firmware.



## 13. Bộ liên kết có bắt buộc trong mọi trình biên dịch không?

Không.

Về mặt kiến trúc, một trình biên dịch có thể trực tiếp tạo tệp thực thi hoàn chỉnh nếu nó tự đảm nhiệm các công việc vốn thường thuộc về bộ liên kết.

Chẳng hạn, trình biên dịch có thể:

Phân tích toàn bộ đơn vị chương trình.
Quản lý ký hiệu trên phạm vi toàn chương trình.
Sinh mã máy cho các hàm.
Bố trí mã và dữ liệu.
Tính toán, vá các tham chiếu địa chỉ.
Tạo header và cấu trúc tệp thực thi.
Xuất nhị phân phù hợp với hệ điều hành.

Trong trường hợp đó, hệ thống vẫn thực hiện **các chức năng liên kết**, nhưng không cần gọi một chương trình linker độc lập.

Cũng có thể tồn tại mô hình trung gian: trình biên dịch tự xử lý một số nhiệm vụ liên kết, sau đó sử dụng linker bên ngoài cho thư viện hoặc những cấu trúc nhị phân đặc thù.

Điều này cho thấy cần hiểu linker vừa là một loại công cụ, vừa là một tập hợp chức năng kỹ thuật có thể được tổ chức theo nhiều kiến trúc triển khai khác nhau.



## 14. Kết luận

Bộ liên kết là một thành phần quan trọng trong quá trình xây dựng phần mềm, giúp các tệp đối tượng và thư viện được biên dịch riêng biệt kết hợp thành sản phẩm nhị phân có thể sử dụng.

Hai nhiệm vụ nền tảng của bộ liên kết là phân giải ký hiệu và tái định vị các tham chiếu địa chỉ. Bên cạnh đó, bộ liên kết còn đảm nhiệm việc bố trí mã và dữ liệu, quản lý phụ thuộc, xác định điểm vào, tạo cấu trúc định dạng tệp và phối hợp các cơ chế tối ưu hóa.

Sự phát triển của bộ liên kết cũng phản ánh quá trình mở rộng quy mô và tính mô-đun của phần mềm. Từ các cơ chế liên kết và nạp chương trình ban đầu, những bộ liên kết hiện đại đã trở thành hệ thống xử lý nhị phân phức tạp, hỗ trợ nhiều kiến trúc, hệ điều hành và mô hình triển khai khác nhau.

Việc nghiên cứu bộ liên kết không thể tách rời các khái niệm về mã đối tượng, ABI, relocation và định dạng tệp thực thi.

Đối với thiết kế trình biên dịch, hiểu cơ chế liên kết giúp giải thích cách mã máy từ nhiều mô-đun được tổ chức thành chương trình thống nhất, đồng thời làm rõ ranh giới giữa công việc của trình biên dịch, linker và bộ nạp.



## Phụ lục A. Thuật ngữ kỹ thuật

| Thuật ngữ tiếng Việt | Thuật ngữ tiếng Anh | Ý nghĩa |
|---|---|---|
| Bộ liên kết | Linker | Công cụ kết hợp và xử lý các tệp đối tượng để tạo sản phẩm nhị phân |
| Tệp đối tượng | Object File | Tệp chứa mã, dữ liệu và thông tin phục vụ liên kết |
| Phân giải ký hiệu | Symbol Resolution | Xác định định nghĩa tương ứng với những tham chiếu ký hiệu |
| Tái định vị | Relocation | Tính toán và điều chỉnh những tham chiếu địa chỉ |
| Bảng ký hiệu | Symbol Table | Cấu trúc mô tả ký hiệu trong tệp nhị phân |
| Liên kết tĩnh | Static Linking | Đưa những thành phần mã cần thiết vào sản phẩm liên kết |
| Liên kết động | Dynamic Linking | Sử dụng những thư viện được kết nối theo cơ chế nạp hoặc thực thi |
| Bộ nạp | Loader | Thiết lập chương trình trong bộ nhớ để thực thi |
| Bộ liên kết động | Dynamic Linker | Xử lý liên kết thư viện động trong môi trường thực thi |
| Tập lệnh liên kết | Linker Script | Tệp chỉ định những quy tắc bố trí và xử lý khi liên kết |
| Điểm vào | Entry Point | Vị trí bắt đầu thực thi theo cấu trúc nhị phân |
| Liên kết gia tăng | Incremental Linking | Tái sử dụng kết quả liên kết trước để giảm công việc khi đầu vào thay đổi |
| Tối ưu tại thời điểm liên kết | Link-Time Optimization | Tối ưu chương trình thông qua thông tin từ nhiều đơn vị biên dịch |
| Giao diện nhị phân ứng dụng | Application Binary Interface | Các quy tắc về tương tác nhị phân trên nền tảng đích |
| Thư viện tĩnh | Static Library | Tập hợp mã đối tượng để liên kết tại thời điểm xây dựng |
| Thư viện dùng chung | Shared Library | Thư viện có thể được sử dụng theo cơ chế liên kết động |
| Mã độc lập vị trí | Position-Independent Code | Mã phù hợp để sử dụng tại nhiều vị trí nạp theo cơ chế tương ứng |



## Phụ lục B. Tài liệu tham khảo

### B.1. Tài liệu về bộ liên kết

**[1] GNU Binutils, *LD: The GNU Linker*.**

Tài liệu chính thức về GNU `ld`, bao gồm phân giải ký hiệu, tệp lưu trữ, relocation, linker script và tùy chọn liên kết.

https://sourceware.org/binutils/docs/ld.html

**[2] LLVM Project, *LLD – The LLVM Linker*.**

Tài liệu về kiến trúc, chức năng và khả năng hỗ trợ định dạng ELF, COFF và Mach-O của LLD.

https://lld.llvm.org/

**[3] LLVM Project, *The ELF, COFF and Wasm Linkers*.**

Tài liệu về các thành phần và lựa chọn thiết kế của bộ liên kết LLD.

https://releases.llvm.org/9.0.0/tools/lld/docs/NewLLD.html

**[4] LLVM Project, *Linker Script Implementation Notes and Policy*.**

Mô tả việc triển khai cú pháp linker script GNU trong LLD và những khác biệt được ghi nhận.

https://lld.llvm.org/ELF/linker_script.html

### B.2. Đặc tả định dạng tệp và ABI

**[5] System V Application Binary Interface, *Executable and Linking Format*.**

Tài liệu mô tả cấu trúc ELF, section, program header, symbol table và relocation.

https://refspecs.linuxfoundation.org/elf/elf.pdf

**[6] Microsoft, *Portable Executable and Common Object File Format Specification*.**

Đặc tả về PE/COFF, bảng ký hiệu, relocation, import/export, header và dữ liệu phục vụ bộ nạp Windows.

https://learn.microsoft.com/en-us/windows/win32/debug/pe-format

**[7] Apple, *Mach-O Documentation*.**

Tài liệu về cấu trúc Mach-O và các thành phần hỗ trợ cơ chế nạp, liên kết trên hệ sinh thái Apple.

https://developer.apple.com/documentation/kernel/mach-o

**[8] LLVM Project, *Mach-O LLD Port*.**

Tài liệu về phần triển khai linker dành cho Mach-O trong dự án LLD.

https://lld.llvm.org/MachO/index.html

### B.3. Sách nghiên cứu

**[9] John R. Levine, *Linkers and Loaders*, Morgan Kaufmann, 1999.**

Công trình chuyên khảo về bộ liên kết, bộ nạp, bảng ký hiệu, relocation, thư viện và các định dạng đối tượng.

**[10] David A. Wheeler, *Program Library HOWTO*.**

Tài liệu hướng dẫn về thư viện tĩnh, thư viện dùng chung và cách sử dụng thư viện trong các hệ thống Unix.

**[11] Alfred V. Aho, Monica S. Lam, Ravi Sethi và Jeffrey D. Ullman, *Compilers: Principles, Techniques, and Tools*, ấn bản thứ hai, Pearson, 2006.**

Tài liệu nền tảng về kiến trúc trình biên dịch, sinh mã và các vấn đề liên quan đến quá trình dịch chương trình.



## Chú thích

[^1]: GNU Binutils, _LD_, mục _Overview_, _Linker Scripts_ và phần tùy chọn thư viện. https://sourceware.org/binutils/docs/ld.html

[^2]: LLVM Project, _LLD – The LLVM Linker_. https://lld.llvm.org/

[^3]: System V ABI, _Executable and Linking Format_, phần mô tả cách nhìn liên kết và thực thi, bảng section và program header. https://refspecs.linuxfoundation.org/elf/elf.pdf

[^4]: LLVM Project, _Mach-O LLD Port_. https://lld.llvm.org/MachO/index.html

[^5]: Microsoft, _PE Format_, các phần về COFF Relocations, Import Table, Export Table và Base Relocations. https://learn.microsoft.com/en-us/windows/win32/debug/pe-format