Trình biên dịch clang++
clang++
là trình biên dịch C++ nằm trong hệ sinh thái LLVM, sử dụng frontend Clang, liên kết với backend của LLVM. Đây là một trong những lựa chọn hiện đại thay thế g++
, có thể hoạt động tốt trên Windows, Linux và macOS. Nó tuân thủ chặt chẽ chuẩn C++, tốc độ biên dịch nhanh, cung cấp thông báo lỗi dễ đọc, và hỗ trợ tốt cho các công cụ phân tích mã như clangd
, clang-tidy
, clang-format
.
Tương tự g++
, khi chạy clang++
với file nguồn .cpp
, trình biên dịch thực hiện các giai đoạn:
- Biên dịch
.cpp
thành file đối tượng.o
(object file) - Gọi linker mặc định (
ld.lld
,ld
, hoặclink.exe
tùy nền tảng) để tạo file thực thi.exe
(Windows) hoặc.out
(Linux)
Cú pháp tổng quát
clang++ [file nguồn] [cờ tiền xử lý] [cờ cấu hình biên dịch] [cờ cảnh báo] [cờ tối ưu hóa] [cờ include] [cờ liên kết] [cờ đầu ra]
[file nguồn]
đặt ở đầu để dễ đọc- Nhóm cờ được nhóm theo chức năng, giúp dễ debug và mở rộng
- Có thể thay đổi thứ tự, nhưng khuyến nghị theo nhóm rõ ràng
Các nhóm cờ và giải thích
Dưới đây sẽ liệt kê các cờ cơ bản và phổ biến đại diện cho mỗi nhóm cờ trong cú pháp tổng quát.
- Nhóm cờ tiền xử lý (Preprocessing)
-D<macro>
: Định nghĩa macro trước khi biên dịch (giống như#define
). Ví dụ:-DDEBUG
.-U<macro>
: Gỡ bỏ macro đã định nghĩa.-include file.h
: Ép buộc include một file header từ đầu, trước cả các include khác.-E
: Thực hiện tiền xử lý và in kết quả rastdout
. Không biên dịch.-H
: Hiển thị danh sách các file header được include, giúp debug tiền xử lý.
- Nhóm cờ cấu hình biên dịch
-std=c++20
: Chỉ định phiên bản chuẩn C++ để biên dịch. Thường dùng c++11, c++17, c++20,...-g
: Thêm thông tin debug vào file thực thi, tương thích với trình gỡ lỗi gdb, lldb (trình debug của LLVM, được khuyến nghị).-fno-exceptions
: Tắt hệ thốngtry-catch
, giảm kích thước chương trình nếu không dùng exception.-fno-rtti
: Tắt RTTI (Run-time Type Information), giúp giảm kích thước chương trình nếu không dùngdynamic_cast
haytypeid
.-nostdlib
: Không tự động liên kết với thư viện chuẩn nhưlibstdc++
,libc
. Dùng khi bạn muốn kiểm soát hoàn toàn liên kết.-nostdinc
: Không thêm đường dẫn thư viện tiêu chuẩn như/usr/include
.-m32
/-m64
: Chỉ định kiến trúc 32-bit hoặc 64-bit khi biên dịch-nodefaultlibs
: Không liên kết với bất kỳ thư viện mặc định nào (nhưlibgcc
,libstdc++
,libc
,...).-fcoroutines
: Bật hỗ trợ coroutines (C++20), thường được tối ưu tốt hơn trongclang++
.-fmodules
: Bật hỗ trợ C++20 modules, cải thiện tốc độ biên dịch cho dự án lớn.
- Nhóm cờ cảnh báo
-Wall
: Bật tập hợp cảnh báo phổ biến. Đây là cờ nên luôn bật.-Wextra
: Bật các cảnh báo bổ sung mà-Wall
chưa bao gồm. Nên bật để phát hiện lỗi tiềm ẩn.-pedantic
: Tuân thủ nghiêm ngặt tiêu chuẩn C++ (báo lỗi những gì không chuẩn).-Wconversion
: Cảnh báo khi có chuyển kiểu ngầm định có thể gây mất dữ liệu.-Wshadow
: Cảnh báo khi biến khai báo mới che khuất (shadow) biến cũ.-Wsign-conversion
: Cảnh báo khi có chuyển đổi giữa số có dấu và không dấu.-Werror
: Chuyển tất cả cảnh báo thành lỗi, dừng biên dịch nếu có cảnh báo.-fsanitize=address
,-fsanitize=undefined
: Kích hoạt AddressSanitizer hoặc UndefinedBehaviorSanitizer để phát hiện lỗi bộ nhớ (như truy cập ngoài mảng, rò rỉ bộ nhớ) hoặc hành vi không xác định.-Weverything
: Bật tất cả cảnh báo củaclang++
, kể cả những cảnh báo không có trong-Wall
hay-Wextra
. Thường dùng để kiểm tra mã rất nghiêm ngặt, nhưng có thể gây nhiễu.-Wno-<warning>
: Tắt cảnh báo cụ thể, ví dụ-Wno-unused-variable
để bỏ qua cảnh báo biến không dùng.-fsanitize=thread
: Kích hoạt ThreadSanitizer để phát hiện lỗi đa luồng (data race, deadlock).-fsanitize=memory
: Kích hoạt MemorySanitizer để phát hiện truy cập bộ nhớ không khởi tạo.
- Nhóm cờ tối ưu hóa
-O0
/-O1
/-O2
/-O3
: Mức tối ưu từ không (-O0
) đến tối ưu mạnh (-O3
), tương tựg++
.-Oz
: Tối ưu hóa kích thước mã, nhỏ hơn cả-Os
(tương tựg++
), phù hợp cho các thiết bị nhúng.-flto
: Bật Link-Time Optimization, cải thiện hiệu năng.
- Nhóm cờ include (Header search path)
-I<dir>
: Thêm thư mục vào danh sách tìm kiếm file header. Áp dụng cho cả#include "..."
và<...>
.-iquote <dir>
: Chỉ thêm thư mục này cho#include "..."
, không áp dụng cho<...>
.-isystem <dir>
: Thêm thư mục hệ thống (thường là SDK, thư viện chuẩn); cảnh báo trong các file header ở đây sẽ bị bỏ qua.-idirafter <dir>
: thêm thư mục tìm kiếm header nhưng ưu tiên thấp hơn-I
.-isysroot <dir>
: Chỉ định thư mục gốc cho các header và thư viện hệ thống (thường dùng trên macOS khi cross-compile).-F<dir>
: Thêm thư mục tìm kiếm framework (chủ yếu trên macOS).
- Nhóm cờ liên kết (Linker flags)
-L<dir>
: Thêm thư mục vào đường dẫn tìm thư viện.a
hoặc.so
/.dll
.-l<name>
: Link với thư việnlib<name>.a
hoặclib<name>.so.
Ví dụ-lm
→libm.a
.-pthread
: Thêm cả cờ biên dịch và liên kết để hỗ trợ đa luồng POSIX.-static
: Yêu cầu liên kết thư viện tĩnh thay vì thư viện động.-fuse-ld=lld
: Sử dụng linkerlld
của LLVM (mặc định trong nhiều bản càiclang++
). Hoặc-fuse-ld=mold
chỉ định linkermold
(nếu có), nhanh hơnlld
trong các dự án lớn.-Wl,<option>
: Truyền trực tiếp tùy chọn cho linkerld
. Ví dụ:-Wl,--as-needed
để giảm liên kết thư viện không cần thiết.-mwindows
: Chỉ định rằng chương trình là GUI (Windows subsystem), không hiện cửa sổ console khi chạy. Tương đương-Wl,--subsystem,windows
.-stdlib=libc++
: Sử dụng thư viện chuẩn C++ của LLVM (libc++
) thay vìlibstdc++
. Đây là lựa chọn mặc định trên macOS, nhưng cần chỉ định trên Linux/Windows nếu muốn dùnglibc++
. Hoặc-stdlib=libstdc++
để sử dụng thư viện chuẩn của GCC (libstdc++
) để đảm bảo tương thích vớig++
.
- Nhóm cờ đầu ra
-o <file>
: Đặt tên file đầu ra. Nếu không chỉ định, mặc định làa.out
(Linux) hoặca.exe
(Windows).-c
: Chỉ biên dịch, không liên kết. Tạo.o
thay vì.exe
.-S
: Sinh mã hợp ngữ (.s
) thay vì mã máy.-MMD
: Tạo file.d
chứa dependency để hỗ trợ build tự động (dùng cho makefile).-shared
: Biên dịch và liên kết để sinh thư viện động (.so
trên Linux hoặc.dylib
trên macOS).- Khi sử dụng cờ
-shared
,clang++
sẽ tạo thư viện động với phần mở rộng.so
trên Linux hoặc.dylib
trên macOS. Trên Windows, cần cấu hình thêm (ví dụ: dùngclang-cl
hoặc-target
) để tạo.dll
. - Nếu không có
-shared
, định dạng đầu ra mặc định là.out
hoặc.exe
.
- Khi sử dụng cờ
-M
: Tạo danh sách phụ thuộc (dependencies) mà không tạo file.d
như-MMD
.-fPIC
: Tạo mã máy độc lập vị trí (Position-Independent Code)-fPIC
bắt buộc khi tạo thư viện động (.so
hoặc.dylib
) trên Linux/macOS để đảm bảo mã độc lập vị trí. Trên Windows,-fPIC
thường không cần khi tạo.dll
vớiclang-cl
hoặc MinGW.- Không cần dùng khi build file thực thi hoặc thư viện tĩnh
.a
.
Trình liên kết của clang++
- Mặc định,
clang++
sử dụnglld
(ld.lld.exe
) (linker của LLVM), nhanh hơnld
và tích hợp tốt với hệ sinh thái LLVM, nếu có, hoặcld
hệ thống. - Có thể thay bằng
ld
,gold
, hoặcmold
qua cờ-fuse-ld=<linker>
. lld
hỗ trợ các tính năng như LTO nhanh hơn và báo lỗi tốt hơn so vớild
.- Khi biên dịch nhiều file
.o
,clang++
tự động gọi linker để tạo file thực thi hoặc thư viện động (-shared
). clang++
hỗ trợ-stdlib=libc++
(thư viện chuẩn C++ của LLVM) hoặc-stdlib=libstdc++
(thư viện chuẩn của GCC), tùy thuộc vào cấu hình và nền tảng.- Có thể cần cài thêm
libc++-dev
để liên kết thành công nếu không dùnglibstdc++
.
Ví dụ
Biên dịch file thực thi .exe
:
clang++ src/main.cpp src/lib.cpp -std=c++20 -Wall -Wextra -O2 -Iinclude/ -Llib/ -lmylib -o build/myapp.exe
Giải thích:
- File nguồn:
src/main.cpp
,src/lib.cpp
. - Cờ cấu hình:
-std=c++20
. - Cờ cảnh báo:
-Wall
,-Wextra
,-Wconversion
. - Cờ tối ưu:
-O2
. - Cờ include:
-Iinclude/
. - Cờ liên kết:
-Llib/
,-lmylib
. - Cờ đầu ra:
-o build/myapp.exe
.
Biên dịch và sử dụng thư viện tĩnh (.a
)
1. Biên dịch mã nguồn thành file đối tượng (.o
)
-
Mục đích: Biên dịch các file mã nguồn (
.cpp
) thành file đối tượng (.o
) mà không liên kết, để chuẩn bị cho việc tạo thư viện tĩnh. -
Cú pháp tổng quát:
clang++ -c [-fPIC] <file.cpp> -o <file.o> [cờ biên dịch khác]
-
Ví dụ áp dụng cơ bản:
clang++ -c mylib.cpp -o mylib.o
-c
: Chỉ biên dịch, không liên kết. Tạo file.o
từ.cpp
.-o mylib.o
: Chỉ định tên file đối tượng đầu ra.
lưu ý- Nếu có nhiều file mã nguồn (ví dụ:
file1.cpp
,file2.cpp
), lặp lại lệnh cho từng file. - Đảm bảo file
.cpp
đã include.h
tương ứng để kiểm tra tính nhất quán của khai báo prototype và định nghĩa.
2. Đóng gói file đối tượng thành thư viện tĩnh (.a
)
- Mục đích: Gộp một hoặc nhiều file đối tượng (
.o
) thành một thư viện tĩnh (.a
) để tái sử dụng và liên kết với các chương trình khác mà không cần biên dịch lại mã nguồn gốc. - Cú pháp tổng quát:
llvm-ar rcs <libname.a> <file1.o> [file2.o ...]
- Ví dụ áp dụng cơ bản:
llvm-ar rcs libmylib.a mylib.o
llvm-ar
: Công cụ tương đươngar
nhưng thuộc hệ LLVM.r
: Thêm hoặc thay thế file.o
vào thư viện.c
: Tạo thư viện mới nếu chưa tồn tại.s
: Tạo chỉ mục (symbol table) để linker tìm kiếm nhanh (tương đươngranlib
/llvm-ranlib
).libmylib.a
: Tên thư viện đầu ra. Khai báo tên thư viện.a
ngay saurcs
là bắt buộc.mylib.o
: File đối tượng được đóng gói.
lưu ý- Tên thư viện nên có tiền tố
lib
và đuôi.a
, ví dụlibmylib.a
, để tương thích với cú pháp-l<name>
khi liên kết sau này. - Có thể đóng gói nhiều file
.o
cùng lúc nếu thư viện gồm nhiều thành phần. llvm-ar
là phần của hệ sinh thái LLVM, có cú pháp tương thíchar
, nhưng nên dùng thay vìar
để đảm bảo không chéo công cụ giữa các hệ thống build.
- Kiểm tra thư viện sau khi đóng gói:
- Liệt kê các file
.o
trong.a
:llvm-ar -t libmylib.a
- Xem symbol table (các ký hiệu như hàm/biến):
llvm-nm -s libmylib.a
- Trích xuất file
.o
từ.a
(nếu cần):- Trích xuất tất cả file
llvm-ar -x libmylib.a
- Hoặc trích xuất file cụ thể
llvm-ar -x libmylib.a mylib.o
- Trích xuất tất cả file
- Liệt kê các file
3. Liên kết thư viện .a
với chương trình chính
- Mục đích: Biên dịch chương trình chính (
.cpp
) và liên kết vớilibmylib.a
để tạo file thực thi. - Cú pháp tổng quát:
clang++ <main.cpp> -I<dir_include> -L<dir_lib> -l<libname> -o <output[.exe]> [cờ biên dịch khác]
- Ví dụ áp dụng cơ bản:
clang++ main.cpp -Iinclude -Llib -lmylib -o myprogram
main.cpp
: File mã nguồn chính, phải includemylib.h
để sử dụng các hàm/biến từ thư viện.-Iinclude
: Chỉ định thư mục chứamylib.h
(nếu không ở cùng thư mục vớimain.cpp
).-Llib
: Chỉ định thư mục chứalibmylib.a
.-lmylib
: Liên kết vớilibmylib.a
(bỏlib
và.a
theo quy ước).-o myprogram
: Tên file thực thi đầu ra.
lưu ý- File
libmylib.a
cần được tạo từllvm-ar
để đảm bảo nhất quán trong hệ sinh thái LLVM. - Cờ
-I
,-L
,-l
hoạt động tương tự như vớig++
, nhưng build bằngclang++
sẽ sử dụng linkerlld
(nếu không thay đổi). - Nếu có nhiều file
.cpp
trong chương trình chính, hãy thêm tất cả vào lệnh biên dịch hoặc biên dịch riêng rồi liên kết lại sau.
Biên dịch và sử dụng thư viện động (.so
)
- Nội dung bên dưới áp dụng cho Linux/macOS khi sử dụng
clang++
. - Thư viện động có phần mở rộng
.so
(Linux) hoặc.dylib
(macOS). - Nếu bạn đang sử dụng
clang++
trên Windows và muốn tạo.dll
, nên chuyển sangclang-cl
hoặcg++
(MinGW).
Thư viện động .so
(shared object) trong hệ sinh thái LLVM/clang++
dùng để chia sẻ mã thực thi giữa nhiều chương trình, giúp giảm kích thước chương trình và hỗ trợ tải động tại runtime. Có hai phương pháp sử dụng thư viện .so
:
- Liên kết động ngầm định (Implicit dynamic linking): Liên kết với
.so
tại thời điểm biên dịch. Trình liên kết thiết lập lời gọi gián tiếp, và hệ thống sẽ tự động nạp.so
khi chạy chương trình. - Tải động (explicit linking): Tự nạp
.so
tại runtime bằng API hệ thống (dlopen
/dlsym
).
1. Biên dịch mã nguồn thành file đối tượng (.o
)
- Mục đích: Biên dịch các file mã nguồn (
.cpp
) thành file đối tượng (.o
) mà không liên kết, chuẩn bị cho việc tạo thư viện động. Và cho mục đích khác là tái sử dụng.o
. - Cú pháp tổng quát:
clang++ -c [-fPIC] <file.cpp> -o <file.o> [cờ biên dịch khác]
- Ví dụ áp dụng cơ bản:
clang++ -c -fPIC mylib.cpp -o mylib.o
-c
: Chỉ biên dịch, không liên kết.-fPIC
: Tạo mã độc lập vị trí (Position-Independent Code), bắt buộc nếu build thư viện động trên Linux/macOS.-o mylib.o
: Tên file đối tượng đầu ra.
- File header:
- Cần tạo file header (
.h
) chứa khai báo hàm/biến với chỉ thị__attribute__((visibility("default")))
để export symbol trong.so
. Dùngextern "C"
để tránh name mangling nếu muốn tương thích ngôn ngữ khác. - Ví dụ với
include/mylib.h
#pragma once
#ifdef __GNUC__
#define MYLIB_API __attribute__((visibility("default")))
#else
#define MYLIB_API
#endif
#ifdef __cplusplus
extern "C" {
#endif
MYLIB_API int add(int a, int b);
#ifdef __cplusplus
}
#endif - Lưu ý:
- Không nên sử dụng
extern "C"
với các thành phần đặc trưng của C++ nhưstd::string
,std::vector
, hàm overload, hàm template, class hoặc namespace. Những thành phần này không tương thích với ngữ cảnhextern "C"
vì không có định dạng ABI (Application Binary Interface) theo chuẩn C, có thể dẫn đến lỗi biên dịch hoặc lỗi liên kết (linker error).
- Không nên sử dụng
- Ví dụ mã nguồn
src/mylib.cpp
#include "mylib.h"
int add(int a, int b) {
return a + b;
}
- Cần tạo file header (
Calling convention là gì?
-
Calling convention là gì?
Calling convention (quy ước gọi hàm) là quy tắc quy định:- Thứ tự truyền tham số
- Ai chịu trách nhiệm thu dọn stack (caller hay callee)
- Cách đặt tên hàm (name mangling) (nếu có)
-
Các loại phổ biến trong Windows (x86)
Tên gọi Từ khóa Truyền tham số Thu dọn stack Dùng phổ biến ở đâu Cdecl __cdecl
Từ phải sang trái Caller Mặc định trong C/C++ Stdcall __stdcall
Từ phải sang trái Callee Windows API, AutoIt, VB6 Fastcall __fastcall
Một số dùng thanh ghi Callee Hiệu năng cao (ít dùng) Thiscall __thiscall
this
trongecx
, còn lại trên stackCallee Mặc định với hàm thành viên C++ a.
__cdecl
- Caller (người gọi) phải clean stack sau khi gọi hàm.
- Cho phép overload số lượng tham số (
printf
,scanf
) - Thường dùng mặc định trong C/C++ compile
__cdecl int add(int a, int b);
b.
__stdcall
- Callee (hàm được gọi) sẽ tự động dọn stack.
- Thường dùng cho Windows API, DLL để gọi từ ngôn ngữ khác
- Gọi từ AutoIt, VB6, Pascal, C# (P/Invoke) → nên dùng
__stdcall
__stdcall int add(int a, int b); // gọn hơn cho người gọi
c.
__fastcall
- Truyền 2 tham số đầu tiên qua thanh ghi (
ecx
,edx
) → nhanh hơn. - Stack dọn bởi callee.
- Thường không cần thiết trừ khi tối ưu hóa chuyên sâu.
__fastcall int add(int a, int b);
d.
__thiscall
(mặc định cho C++ member function)this
nằm trong thanh ghiecx
- Các tham số còn lại trên stack
- Không dùng được trong
extern "C"
(vì C không cóthis
)
g++
và clang++
Trong môi trường MinGW (Windows), các quy tắc calling convention tương tự MSVC, nhưng cú pháp khai báo có thể dùng __attribute__((stdcall))
thay cho __stdcall
. Trên Linux/POSIX, các khái niệm __stdcall
và __cdecl
không áp dụng; thay vào đó, System V ABI được sử dụng.
Nếu không khai báo rõ calling convention, hàm export từ DLL sẽ dùng calling convention mặc định, tùy thuộc vào trình biên dịch và nền tảng:
- Trên Windows x86 (32-bit) (dùng
clang-cl
,g++
, hoặcclang++
trong MinGW): Mặc định là__cdecl
. Nếu DLL được gọi bởi các ứng dụng Windows khác (như MSVC, AutoIt, VB6), cần khai báo__stdcall
(hoặc__attribute__((stdcall))
trong g++/clang++) để tránh lỗi stack hoặc treo chương trình. - Trên Windows x64: Tất cả dùng Microsoft x64 ABI, không cần khai báo
__stdcall
hay__cdecl
. - Trên Linux/POSIX (dùng
g++
hoặcclang++
): System V ABI được áp dụng, không liên quan đến__stdcall
hay__cdecl
. Đảm bảo dùngextern "C"
để tránh name mangling khi export hàm.
TÌNH HUỐNG:
- Windows 64-bit (dùng clang-cl, g++, hoặc clang++ trong MinGW):
- Không cần phân biệt
__stdcall
hay__cdecl
. Tất cả hàm gọi giữa module đều theo Microsoft x64 ABI, đảm bảo tương thích mà không cần khai báo thêm.
- Không cần phân biệt
- Windows 32-bit (dùng clang-cl, g++, hoặc clang++ trong MinGW):
- Phải khai báo rõ
__stdcall
(hoặc__attribute__((stdcall))
trong g++/clang++) khi viết DLL để tương tác với các ứng dụng Windows khác. Nếu không, sẽ xảy ra:- Lỗi stack khi trả về từ hàm.
- Treo chương trình hoặc kết quả sai.
- Phải khai báo rõ
- Linux/POSIX (dùng g++ hoặc clang++):
- Không cần khai báo calling convention cụ thể. Hàm export từ thư viện chia sẻ (
.so
) tuân theo System V ABI. Hãy đảm bảo dùngextern "C"
để tránh name mangling khi cần gọi hàm từ các ngôn ngữ khác.
- Không cần khai báo calling convention cụ thể. Hàm export từ thư viện chia sẻ (
2. Tạo thư viện động (.so
) và import library (.a
)
- Mục đích: Liên kết các file
.o
thành thư viện động.so
và (tuỳ chọn) tạo import library.a
để liên kết động ngầm định. - Cú pháp tổng quát:
clang++ -shared <file.o> -o <libname.so> [-Wl,--out-implib,<libname.a>] [-D<macro>]
- Ví dụ áp dụng cơ bản:
clang++ -shared mylib.o -o lib/libmylib.so -Wl,--out-implib,lib/libmylib.a -DBUILD_MYLIB
-shared
: Tạo thư viện động.so
.-o lib/libmylib.so
: Tên file thư viện động đầu ra.-Wl,--out-implib,lib/libmylib.a
: (Tùy chọn) tạo import library.a
để sử dụng cho implicit linking.-DBUILD_MYLIB
: Định nghĩa macro để sử dụng__attribute__((visibility("default")))
khi biên dịch.- Nếu có nhiều file
.o
:clang++ -shared file1.o file2.o -o lib/libmylib.so -Wl,--out-implib,lib/libmylib.a -DBUILD_MYLIB
- Quy tắc đặt tên:
- Thư viện động:
lib<ten>.so
(ví dụ:libmylib.so
), theo quy ước hệ UNIX. - Import library:
lib<ten>.a
(ví dụ:libmylib.a
), dùng với-l<ten>
(như-lmylib
) khi liên kết. - Đặt
.so
và.a
trong thư mụclib/
:project/
├── include/mylib.h
├── lib/libmylib.a
├── lib/mylib.so
- Thư viện động:
- Kiểm tra sau khi tạo:
- Xem ký hiệu xuất trong
.so
llvm-nm --defined-only lib/libmylib.so
- Xem thông tin chi tiết
.so
llvm-objdump -p lib/libmylib.so
- Kiểm tra import library
llvm-nm -s lib/libmylib.a
- Trích xuất
.o
từ.a
(nếu cần)llvm-ar -x lib/libmylib.a
- Xem ký hiệu xuất trong
- Lưu ý:
- File
.a
chỉ cần nếu muốn hỗ trợ liên kết ngầm định (giống Windows). Với Linux/macOS, có thể liên kết trực tiếp với.so
nếu dùng-l
và thư viện được đặt đúng nơi. - Có thể tạo file
.def
hoặc sử dụng-fvisibility=hidden
và gắn__attribute__((visibility("default")))
cho hàm cần export. - Nếu chỉ cần tải động (explicit), có thể bỏ
--out-implib
:clang++ -shared -o lib/libmylib.so mylib.o -DBUILD_MYLIB
- File
3. Sử dụng thư viện động .so
- Liên kết động ngầm định (Implicit dynamic linking)
- Mục đích: Biên dịch chương trình chính, liên kết với
libmylib.so
(hoặc.a
, nếu dùng import lib) để gọi hàm từ thư viện động như gọi hàm thông thường. - Yêu cầu:
libmylib.so
: Thư viện động có mặt tại thời điểm biên dịch và runtime.mylib.h
: Khai báo hàm với__attribute__((visibility("default")))
(hoặc macro điều kiện nếu dùng chung với export).
- Cú pháp tổng quát:
clang++ <main.cpp> -I<dir_include> -L<dir_lib> -l<libname> -o <output> [cờ khác]
- Ví dụ áp dụng cơ bản:
clang++ main.cpp -Iinclude -Llib -lmylib -o myapp
main.cpp
: Gọi các hàm đã khai báo trongmylib.h
.-Iinclude
: Thư mục chứamylib.h
.-Llib
: Thư mục chứalibmylib.so
.-lmylib
: Liên kết vớilibmylib.so
.-o myapp
: File thực thi đầu ra.
- Lưu ý quan trọng:
mylib.h
cần khai báo với__attribute__((visibility("default")))
để cho phép export symbol.- Tại runtime, file
libmylib.so
phải nằm cùng thư mục với myapp, hoặc:- Được thêm vào biến môi trường
LD_LIBRARY_PATH
- Hoặc chỉ định đường dẫn runtime bằng linker:
-Wl,-rpath,<dir>
- Cấu trúc thư mục thường dùng:
bin/myapp
bin/libmylib.so
lib/libmylib.a (nếu có)
include/mylib.h
- Được thêm vào biến môi trường
- Mục đích: Biên dịch chương trình chính, liên kết với
- Tải động (Explicit linking)
- Mục đích: Tải
.so
tại runtime, không cầnlibmylib.a
, phù hợp cho plugin, runtime module, hoặc dùng từ ngôn ngữ khác. - Ưu điểm:
- Kiểm soát lỗi khi thư viện không tồn tại.
- Cho phép thay thế module khi chương trình đang chạy (hot plugin).
- Có thể sử dụng
.so
từ ngôn ngữ khác nếu tuân thủextern "C"
và chuẩn gọi hàm rõ ràng.
- Yêu cầu:
- Biết tên hàm đúng như đã export (ví dụ: add).
- Trong thư viện, hàm phải khai báo với
extern "C"
để tránh name mangling. - Cần include
<dlfcn.h>
trên Linux/macOS.
- Ví dụ minh hoạ:
#include <dlfcn.h>
#include <iostream>
int main() {
void* handle = dlopen("libmylib.so", RTLD_LAZY);
if (!handle) {
std::cerr << dlerror() << "\n";
return 1;
}
typedef int (*AddFunc)(int, int);
AddFunc add = (AddFunc)dlsym(handle, "add");
if (!add) {
std::cerr << dlerror() << "\n";
return 1;
}
std::cout << "2 + 3 = " << add(2, 3) << "\n";
dlclose(handle);
} - Biên dịch chương trình:
clang++ src/main.cpp -ldl -o myapp
- Mục đích: Tải