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

Thư viện tĩnh chỉ là một kho lưu trữ

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

  • Đóng gói nhiều tệp .o thành một tệp .a bằng ar
  • Liệt kê và giải nén nội dung một thư viện tĩnh
  • Đọc bảng ký hiệu bằng nm và phân biệt T với t
  • Giải thích vì sao .a không phải là mã đã liên kết

Một tệp .a không phải mã đã liên kết, không phải mã đã tối ưu, không phải thứ gì đặc biệt cả. Nó là một cái túi đựng các tệp .o, đúng nghĩa như một tệp .tar, cộng thêm một mục lục ký hiệu.

#Nó thật sự là gì

Thư viện tĩnh (static library)
Một kho lưu trữ chứa nhiều tệp đối tượng. Khi liên kết, trình liên kết lấy ra những tệp .o mà chương trình cần rồi nhét thẳng vào tệp nhị phân. Sau đó thư viện không còn vai trò gì nữa.
Nhánh trên là tĩnh, nhánh dưới là động. Bài này lo nhánh trên.
Linux và macOSWindows
Phần mở rộng.a.lib, hoặc .a với MinGW
Công cụ tạoarlib.exe, hoặc ar với MinGW
Tên quy ướclibmath.amath.lib, hoặc libmath.a
Cách gọi khi liên kết-lmathmath.lib, hoặc -lmath
Định dạng bên trongar archiveGiống, khi dùng MinGW

#Ba bước tạo

Cây thư mục
mathlib/
  include/
    mathlib.h
  src/
    hinhhoc.c
    sohoc.c
  build/
  main.c
include/mathlib.h
#ifndef MATHLIB_H
#define MATHLIB_H

#define PI 3.14159

double dt_hinh_tron(double r);
double cv_hinh_tron(double r);
long   giai_thua(int n);
int    uoc_chung_lon_nhat(int a, int b);

#endif
src/sohoc.c
#include "mathlib.h"

static int tru(int a, int b) { return a - b; }   /* noi bo, khong xuat ra */

long giai_thua(int n) {
    long t = 1;
    for (int i = 2; i <= n; ++i) t *= i;
    return t;
}

int uoc_chung_lon_nhat(int a, int b) { ... }
terminal
# Bước 1: dịch từng tệp nguồn thành tệp đối tượng
gcc -c -Iinclude src/hinhhoc.c -o build/hinhhoc.o
gcc -c -Iinclude src/sohoc.c -o build/sohoc.o
# Bước 2: đóng gói
ar rcs build/libmath.a build/hinhhoc.o build/sohoc.o
# Bước 3: xem kết quả
ls -l build/
-rw-r--r-- 1 DELL 197609  990 hinhhoc.o
-rw-r--r-- 1 DELL 197609 2276 libmath.a
-rw-r--r-- 1 DELL 197609 1021 sohoc.o

Makefile

Makefile
CC      = gcc
CFLAGS  = -std=c11 -Iinclude -Wall -Wextra -Wpedantic -O2

NGUON   = $(wildcard src/*.c)
DOI_TUONG = $(patsubst src/%.c,build/%.o,$(NGUON))

build/libmath.a: $(DOI_TUONG)
	ar rcs $@ $^

build/%.o: src/%.c include/mathlib.h | build
	$(CC) $(CFLAGS) -c -o $@ $<

build:
	mkdir -p build

sach:
	rm -rf build

.PHONY: sach

# Chu y "| build": day la phu thuoc thu tu.
# No bao dam thu muc build ton tai truoc khi dich, nhung KHONG
# lam .o phai dich lai moi khi thu muc doi thoi diem sua.

#Nhìn vào bên trong

terminal
# Liệt kê các tệp .o bên trong
ar t build/libmath.a
hinhhoc.o
sohoc.o
# Xem bảng ký hiệu
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
ar t libmath.a          # liet ke ten tep .o
ar tv libmath.a         # kem kich thuoc va thoi diem
ar x libmath.a          # GIAI NEN: lay lai cac tep .o ra thu muc hien tai
ar x libmath.a sohoc.o  # chi lay mot tep
ar d libmath.a sohoc.o  # xoa mot tep khoi thu vien
ar r libmath.a moi.o    # them hoac thay the mot tep

nm libmath.a            # ky hieu cua tung tep .o
nm --defined-only libmath.a   # bo cac ky hieu chua dinh nghia
objdump -a libmath.a    # dau muc cua tung thanh vien

#Dùng thư viện

terminal
gcc -Iinclude main.c -Lbuild -lmath -o app.exe && ./app.exe
dt(2)  = 12.56636
cv(2)  = 12.56636
5!     = 120
CờNói với trình biên dịch
-IincludeTìm tệp header trong thư mục include
-LbuildTìm tệp thư viện trong thư mục build
-lmathLiên kết với libmath.a hoặc libmath.so
-l:libmath.aLiên kết đúng tệp này, không đoán tên
build/libmath.aChỉ tên tệp thẳng, không cần -L và -l

#Trình liên kết lấy gì từ thư viện

# Cach do: dem so ky hieu that su vao trong chuong trinh

nm app.exe | grep -E 'dt_hinh_tron|cv_hinh_tron|giai_thua'

# Hoac de trinh bien dich chia nho gium:

gcc -ffunction-sections -fdata-sections -c src/*.c
gcc main.o -Wl,--gc-sections -Lbuild -lmath -o app

#  -ffunction-sections   moi ham vao mot phan doan rieng
#  -fdata-sections       moi bien vao mot phan doan rieng
#  --gc-sections         trinh lien ket xoa phan doan khong ai dung
#
# Ba co nay lam viec chia .o thanh nho tu dong. Rat dang bat
# trong ma nhung, noi tung KB deu quy. Bai 46.3 da noi.

Tự làm thử

  1. Đóng gói hai tệp nguồn thành một thư viện tĩnh và chạy ar t lên nó.
  2. Chạy nm và xác định hàm nào là T hàm nào là t.
  3. Bỏ chữ s trong ar rcs và xem trình liên kết nói gì.
  4. Giải nén thư viện bằng ar x trong một thư mục rỗng.
  5. Chứng minh một hàm không ai gọi vẫn vào tệp nhị phân nếu nó cùng .o với hàm được gọi.
  6. Bật -ffunction-sections và --gc-sections, đo lại kích thước.

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

  • Tệp .a là một kho lưu trữ chứa nguyên các tệp .o, cộng một mục lục ký hiệu.
  • ar rcs: r thêm hoặc thay, c tạo mới im lặng, s sinh mục lục.
  • nm hiện T cho hàm công khai và t cho hàm static. Đó là ranh giới API của thư viện.
  • Trình liên kết lấy cả tệp .o chứ không lấy từng hàm, nên hãy chia thư viện thành nhiều tệp nguồn nhỏ.
  • -lmath tìm libmath.a, nên tên tệp bắt buộc phải có tiền tố lib.