Trình điều khiển g++
g++ là trình điều khiển (driver) ngôn ngữ C++ nằm trong bộ công cụ GCC (GNU Compiler Collection). Đây là một trong những trình điều khiển phổ biến nhất, được dùng nhiều trên các hệ điều hành dựa trên Unix (Linux, BSD, macOS), và có thể dùng trên Windows thông qua các hệ thống như MinGW, MSYS2, hoặc WSL.
Khi bạn chạy g++ với một file nguồn .cpp, trình điều khiển này sẽ tự động thực hiện cả hai giai đoạn:
- Biên dịch file
.cppthành file.o(object) - Gọi trình liên kết
ldđể tạo file thực thi.exe(Windows) hoặc.out(Linux)
Cú pháp tổng quát
g++ [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.
- 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 (sử dụng với gdb).-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_casthaytypeid.-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,...).
- 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à-Wallchư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ớ hoặc hành vi không xác định.
- Nhóm cờ tối ưu hóa
-O0/-O1/-O2/-O3: Tối ưu mã ở các mức độ khác nhau.-O0là không tối ưu,-O2là tối ưu thông thường,-O3tối ưu mạnh.-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.
- 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.ahoặc.so.-l<name>: Link với thư việnlib<name>.ahoặ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: Dùng linkerlldthay thế chold. Cần cài thêmlld.-Wl,<options>: 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.-shared-libgcc: Liên kết với thư viện GCC động thay vì tĩnh.
- 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.othay vì.exe.-S: Sinh mã hợp ngữ (.s) thay vì mã máy.-MMD: Tạo file.dchứ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 (.sotrên Linux hoặc.dlltrên Windows).- Khi sử dụng cờ
-shared,g++sẽ hiểu rằng bạn đang tạo một thư viện động và tự động chọn phần mở rộng đầu ra phù hợp, chẳng hạn.sotrên Linux hoặc.dlltrên Windows (với MinGW). - Nếu không có
-shared, định dạng đầu ra mặc định là.outhoặc.exe.
- Khi sử dụng cờ
-M: Tạo danh sách phụ thuộc (dependencies) mà không tạo file.dnhư-MMD.-fPIC: Tạo mã máy độc lập vị trí (Position-Independent Code)- Bắt buộc khi build thư viện động (
.so) trên Linux. Trên Windows (MinGW), có thể không cần, nhưng vẫn nên dùng để tăng tính tương thích và ổn định khi biên dịch thư viện.dll. - Không cần dùng khi build file thực thi
.exehoặc thư viện tĩnh.a.
- Bắt buộc khi build thư viện động (
Trình liên kết của g++
g++sử dụngld(GNU linker) làm trình liên kết mặc định.- Thông thường, người dùng không cần quan tâm đến
ldvìg++sẽ gọi nó tự động. - Có thể thay thế bằng linker khác như
goldhoặclldbằng cờ-fuse-ld=goldhoặc-fuse-ld=lld. lldthường nhanh hơnldvà được ưa chuộng trong các dự án lớn, nhưng cần cài đặt thêm.- Khi biên dịch có nhiều file
.o,g++sẽ tự gọildđể tạo file thực thi hoặc thư viện (nếu có cờ-shared).
Ví dụ
Biên dịch file thực thi .exe:
g++ src/main.cpp src/lib.cpp -std=c++20 -Wall -Wextra -Wconversion -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 biên dịch:
-std=c++20 - Cờ cảnh báo:
-Wall,-Wextra,-Wconversion - Cờ tối ưu hóa:
-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:
g++ -c [-fPIC] <file.cpp> -o <file.o> [cờ biên dịch khác] -
Ví dụ áp dụng cơ bản:
g++ -c mylib.cpp -o mylib.o-c: Chỉ biên dịch, không liên kết. Tạo file.otừ.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.htươ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: Dùng
arđể đóng gói các file.othành thư viện tĩnh.a, tạo điều kiện cho việc liên kết với chương trình chính. - Cú pháp tổng quát:
ar rcs <libname.a> <file1.o> [file2.o ...] - Ví dụ áp dụng cơ bản:
ar rcs libmylib.a mylib.oar: Công cụ GNU Binutils để tạo/quản lý file lưu trữ.r: Thêm hoặc thay thế file.ovà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).libmylib.a: Tên thư viện tĩnh theo quy ước GNU. Khai báo tên thư viện.angay saurcslà bắt buộc.mylib.o: File đối tượng được đóng gói.- Nếu có nhiều file
.o:ar rcs libmylib.a file1.o file2.o
Quy tắc đặt tên thư viện (GNU)- Tên thư viện tĩnh phải bắt đầu bằng
lib(ví dụ:libmylib.a). - Khi liên kết, chỉ cần dùng
-lmylib(bỏlibvà.a). - Đặt thư viện trong thư mục chuẩn như
lib/để dễ quản lý.
- Kiểm tra thư viện sau khi đóng gói:
- Liệt kê các file
.otrong.a:ar -t libmylib.a - Xem symbol table (các ký hiệu như hàm/biến):
nm -s libmylib.a - Trích xuất file
.otừ.a(nếu cần):- Trích xuất tất cả file
ar -x libmylib.a - Hoặc trích xuất file cụ thể
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:
g++ <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:
g++ main.cpp -Iinclude -Llib -lmylib -o myprogrammain.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ỏlibvà.atheo quy ước GNU).-o myprogram: Tên file thực thi đầu ra.
Biên dịch và sử dụng thư viện động (.so/.dll)
Thư viện động .so/.dll (Shared Object/Dynamic-Link Library) trong hệ sinh thái GNU (dùng g++ với MinGW/MSYS2 trên Windows) cho phép chia sẻ mã thực thi giữa nhiều chương trình, giảm kích thước file thực thi (.exe) và hỗ trợ tải động tại runtime. Quy trình dưới đây mô tả cách biên dịch và sử dụng .so/.dll theo hai phương pháp:
-
Liên kết tĩnh (implicit linking): Gọi hàm trực tiếp qua file
.a(import library). -
Tải động (explicit linking): Tự nạp
.so/.dlltại runtime bằng API hệ thống (dlopen/dlsym,LoadLibrary/GetProcAddress).thông tinCả hai cách đều chỉ yêu cầu phân phối file thực thi và
.so/.dllkhi triển khai.
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:
g++ -c [-fPIC] <file.cpp> -o <file.o> [cờ biên dịch khác] -
Ví dụ áp dụng cơ bản:
g++ -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.-o mylib.o: Tên file đối tượng đầu ra.
Các cờ bắt buộc và cần thiết-clà bắt buộc để chỉ biên dịch, không liên kết.-olà cần thiết để đặt tên file đầu ra rõ ràng, nhất là khi có nhiều file, tránh dùng mặc địnha.o.-fPIC:- Bắt buộc nếu build
.sotrên Linux, đặc biệt khi dùng nhiều.ohoặc xuất symbol. - Không bắt buộc trên Windows, vì định dạng
.dlldùng PE/COFF, cơ chế khác với ELF. - Dù
g++sẽ tự bỏ qua-fPICtrên Windows, vẫn nên thêm để đảm bảo tính tương thích nếu chuyển thư viện sang Linux.
- Bắt buộc nếu build
-
File header:
- Cần tạo file header (
.h) chứa khai báo hàm/biến với chỉ thị__declspec(dllexport)/__declspec(dllimport)để xuất/nhập ký hiệu. Để tương thích với ngôn ngữ khác, dùngextern "C"để tránh name mangling. - Ví dụ với
include/mylib.h#pragma once
// Nhận diện môi trường build static
#if defined(STATIC_MYLIB)
#define MYLIB_API
// Windows (MSVC, clang-cl, mingw)
#elif defined(_WIN32) || defined(__CYGWIN__)
#if defined(BUILD_MYLIB)
#define MYLIB_API __declspec(dllexport)
#else
#define MYLIB_API __declspec(dllimport)
#endif
// Unix-like (Linux, macOS, BSD...) – dùng GCC hoặc Clang
#elif defined(__GNUC__) || defined(__clang__)
#if defined(BUILD_MYLIB)
#define MYLIB_API __attribute__((visibility("default")))
#else
#define MYLIB_API
#endif
// Fallback: Không xác định được platform/compiler
#else
#define MYLIB_API
#endif
#ifdef __cplusplus
extern "C" {
#endif
MYLIB_API int add(int a, int b);
#ifdef __cplusplus
}
#endif - Ví dụ mã nguồn
src/mylib.cpp#include "mylib.h"
int add(int a, int b) {
return a + b;
}
lưu ýextern "C"đảm bảo tên hàm không bị name mangling (ví dụ:addthay vì_Z3addii), cần cho tải động.- Đảm bảo
mylib.cppcó includemylib.hđể kiểm tra tính nhất quán.
- 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 __cdeclTừ phải sang trái Caller Mặc định trong C/C++ Stdcall __stdcallTừ phải sang trái Callee Windows API, AutoIt, VB6 Fastcall __fastcallMột số dùng thanh ghi Callee Hiệu năng cao (ít dùng) Thiscall __thiscallthistrongecx, 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)thisnằ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
__stdcallhay__cdecl. - Trên Linux/POSIX (dùng
g++hoặcclang++): System V ABI được áp dụng, không liên quan đến__stdcallhay__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
__stdcallhay__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 (.dll) và import library (.a)
-
Mục đích: Liên kết các file
.othành.dllvà tạo import library.a(dùng cho liên kết tĩnh). -
Cú pháp tổng quát:
g++ -shared <file.o> -o <file.dll> [-Wl,--out-implib,<file.a>] [-D<macro>] -
Ví dụ áp dụng cơ bản:
g++ -shared mylib.o -o lib/mylib.dll -Wl,--out-implib,lib/libmylib.a -DBUILD_MYLIB-shared: Tạo thư viện động.dllthay vì file thực thi.-o lib/mylib.dll: Tên file thư viện động đầu ra.-Wl,--out-implib,lib/libmylib.a: Tạo import librarylibmylib.acho liên kết tĩnh.-DBUILD_MYLIB: Định nghĩa macro để sử dụng__declspec(dllexport)khi biên dịch.- Nếu có nhiều file
.o:g++ -shared file1.o file2.o -o lib/mylib.dll -Wl,--out-implib,lib/libmylib.a -DBUILD_MYLIB
lưu ý-Wl,--out-implib,lib/libmylib.a: Là một cờ duy nhất, dùng để yêu cầu linker (ld) tạo import library.aphục vụ liên kết tĩnh (implicit linking).-Wl,: Chuyển tiếp toàn bộ phần phía sau dấu phẩy,xuống cho linker (ld).--out-implib,lib/libmylib.a: Là một tùy chọn đơn lẻ của linker, yêu cầu linker xuất file.a(lib/libmylib.a) chứa thông tin liên kết tới.dll.
-
Quy tắc đặt tên:
- Thư viện động:
<ten>.dll(ví dụ:mylib.dll), không bắt buộc tiền tố lib. - Import library:
lib<ten>.a(ví dụ:libmylib.a), dùng với-l<ten>(như-lmylib) khi liên kết. - Đặt
.dllvà.atrong thư mụclib/:project/
├── include/mylib.h
├── lib/libmylib.a
├── lib/mylib.dll
- Thư viện động:
-
Kiểm tra sau khi tạo:
- Xem ký hiệu xuất trong
.dllKết quả: Hiển thị tên hàm nhưnm --defined-only lib/mylib.dlladd(nếu dùngextern "C"). - Xem thông tin chi tiết
.dllobjdump -p lib/mylib.dll - Kiểm tra import library
nm -s lib/libmylib.a - Trích xuất
.otừ.a(nếu cần)ar -x lib/libmylib.a
- Xem ký hiệu xuất trong
-
Lưu ý:
- File
.achỉ cần cho liên kết tĩnh, không cần cho tải động. - Có thể tạo
.defđể:- Kiểm soát ký hiệu xuất
- Kiểm soát chính xác tên hàm export
- Tránh bị name mangling hoặc export hàm không mong muốn
- Hữu ích khi không dùng
__declspec(dllexport)hoặc muốn giảm phụ thuộc - Nội dung
.def:LIBRARY "mylib"
EXPORTS
addLIBRARYlà tên DLL (không cần đuôi.dll)EXPORTSliệt kê các hàm public
- Biên dịch với
.def:g++ -shared -o lib/mylib.dll mylib.o -Wl,--output-def,lib/mylib.def -DBUILD_MYLIB
- Nếu chỉ cần tải động, có thể bỏ
--out-implib:g++ -shared -o lib/mylib.dll mylib.o -DBUILD_MYLIB
- File
3. Sử dụng thư viện động .dll
- Liên kết tĩnh (Implicit linking)
- Mục đích: Biên dịch chương trình chính, liên kết với
libmylib.ađể gọi hàm từ.dllnhư các hàm thông thường. - Yêu cầu:
libmylib.a: Import library chứa symbol để linker tạo lời gọi tới.dll.mylib.h: Khai báo hàm với__declspec(dllimport)(qua macro nếu dùng header dùng chung export/import).mylib.dll: Cần có mặt khi chạy chương trình.
- Cú pháp tổng quát:
g++ <main.cpp> -I<dir_include> -L<dir_lib> -l<libname> -o <output[.exe]> [cờ khác] - Ví dụ áp dụng cơ bản:
g++ main.cpp -Iinclude -Llib -lmylib -o myapp.exemain.cpp: Gọi các hàm được khai báo trongmylib.h.-Iinclude: Thư mục chứamylib.h.-Llib: Thư mục chứalibmylib.a.-lmylib: Liên kết vớilibmylib.a.-o myapp.exe: File thực thi đầu ra.
- Lưu ý quan trọng:
- Cần
mylib.hvới__declspec(dllimport)để chương trình biết khai báo các hàm từ.dll. - Nhiều chương trình
.exekhác nhau có thể dùng chungmylib.dll, miễn là chúng đều được biên dịch vớilibmylib.a. - Tại runtime, file
mylib.dllphải nằm cùng thư mục vớimyapp.exe, hoặc:- Được thêm vào biến môi trường
PATH - Chỉ định rõ đường dẫn thư mục chứa
.dllbằng linker:-Wl,-rpath,<dir>(trên Linux) hoặc sử dụng công cụ riêng cho Windows (như. manifesthoặcSetDllDirectory).
- Được thêm vào biến môi trường
- Cấu trúc thư mục thường dùng:
bin/myapp.exe
bin/mylib.dll
lib/libmylib.a
include/mylib.h
- Cần
- 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
.dlltại runtime, không cầnlibmylib.a, phù hợp cho plugin, tình huống cần linh hoạt, hoặc sử dụng từ ngôn ngữ khác như Python, C#,... - Ưu điểm:
- Cho phép kiểm soát lỗi khi không tìm thấy
.dll, linh hoạt (plugin, module). - Nếu
.dllđược xây dựng đúng tiêu chuẩn (sử dụngextern "C"và chuẩn gọi hàm rõ ràng như__cdeclhoặc__stdcall), nó có thể được gọi từ nhiều ngôn ngữ khác.
- Cho phép kiểm soát lỗi khi không tìm thấy
- Yêu cầu:
- Tên hàm (như hàm "add" trong ví dụ bên dưới) phải khớp tuyệt đối với tên xuất trong
.dll. - Trong thư viện, hàm phải khai báo với
extern "C"để tránh name mangling. - Cần include
<windows.h>trên Windows,<dlfcn.h>trên Linux/macOS.
- Tên hàm (như hàm "add" trong ví dụ bên dưới) phải khớp tuyệt đối với tên xuất trong
- Ví dụ minh hoạ:
src/main.cpp (Windows)
#include <windows.h>
#include <iostream>
typedef int (*AddFunc)(int, int);
int main() {
HINSTANCE hDLL = LoadLibrary("mylib.dll");
if (!hDLL) {
std::cerr << "Không thể tải mylib.dll\n";
return 1;
}
AddFunc add = (AddFunc)GetProcAddress(hDLL, "add");
if (!add) {
std::cerr << "Không tìm thấy hàm add\n";
return 1;
}
std::cout << "2 + 3 = " << add(2, 3) << "\n";
FreeLibrary(hDLL);
return 0;
}src/main.cpp (Linux/macOS)#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:
g++ src/main.cpp -o myapp.exe
lưu ý- Không cần
libmylib.ahoặcmylib.h, nhưng bạn cần:- Biết chính xác tên hàm export ("add") và prototype của nó.
- Biết chuẩn gọi hàm (thường là
__cdeclmặc định trên Windows).
- Có thể dùng
nm --defined-only lib/mylib.dllđể liệt kê symbol nếu.dllkhông bị strip. - Nên cung cấp tài liệu
.dll(header hoặc PDF) cho người dùng thư viện: tên hàm, kiểu dữ liệu, calling convention,...
- Mục đích: Tải
4. Kiểm tra DLL phụ thuộc trước khi phân phối
- Mục đích:
Kiểm tra các thư viện động (.dll) mà file thực thi (.exe) hoặc thư viện động (.dll) phụ thuộc, nhằm đảm bảo chương trình chạy được trên máy đích mà không gặp lỗi "missing DLL". - Công cụ:
dumpbin.exe, một tiện ích trong bộ công cụ Visual Studio (hoặc Visual Studio Build Tools). Nếu dùng MinGW/MSYS2, bạn cần cài Visual Studio Build Tools hoặc chạy từ Developer Command Prompt for Visual Studio.
"Một bộ" đủ dùng chodumpbin.exebao gồm các file: (tổng dung lượng ~1.5MB)dumpbin.exelink.exelink.exe.configmspdbcore.dlltbbmalloc.dll
objdump.exe, có sẵn trong MinGW (hệ sinh thái GNU).llvm-objdump.exe, có sẵn trong hệ sinh thái LLVM.
- Cú pháp:
dumpbin /dependents <file.exe|file.dll> [| findstr ".dll"]
# Hoặc lệnh tương đương dùng objdump
objdump -p <file.exe> [| findstr "DLL Name:"]
# Hoặc lệnh tương đương dùng llvm-objdump
llvm-objdump -p <file.exe|file.dll> [| findstr "DLL Name:"]/dependents: Liệt kê tất cả các DLL mà file đầu vào phụ thuộc.<file.exe|file.dll>: File thực thi hoặc thư viện động cần kiểm tra.
- Ví dụ:
Kiểm tra DLL phụ thuộc củamyapp.exeKết quả mẫu (trên Windows, MinGW)dumpbin /dependents bin/myapp.exeDump of file bin/myapp.exe
File Type: EXECUTABLE IMAGE
Image has the following dependencies:
mylib.dll
libstdc++-6.dll
libgcc_s_seh-1.dll
KERNEL32.dll
msvcrt.dll
Summary
...- Giải thích:
- Các DLL liệt kê:
mylib.dll: Thư viện động bạn tạo (nếu có liên kết vớilibmylib.a).libstdc++-6.dll,libgcc_s_seh-1.dll: Thư viện runtime của MinGW, cần phân phối cùng.exenếu máy đích không có MinGW/MSYS2.KERNEL32.dll,msvcrt.dll: Thư viện hệ thống Windows, thường có sẵn trên mọi máy Windows.
- Cách sử dụng kết quả:
- Đảm bảo tất cả các DLL không thuộc hệ thống (như
mylib.dll,libstdc++-6.dll,libgcc_s_seh-1.dll) được:- Đặt cùng thư mục với
myapp.exe. - Hoặc thêm vào biến môi trường
PATH.
- Đặt cùng thư mục với
- Kiểm tra phiên bản DLL (nếu cần) bằng công cụ như
strings.exe(trong MinGW/MSYS2 (hoặc GNU Binutils)) hoặcobjdump.exeđể đảm bảo tương thích.
- Đảm bảo tất cả các DLL không thuộc hệ thống (như
- Các DLL liệt kê:
- Giải thích:
- Tối ưu phân phối:
- Để giảm phụ thuộc, cân nhắc dùng
-static-libstdc++và-static-libgcckhi biên dịchmyapp.exe:Điều này loại bỏ nhu cầu phân phốig++ main.cpp -Iinclude -Llib -lmylib -static-libstdc++ -static-libgcc -o myapp.exelibstdc++-6.dllvàlibgcc_s_seh-1.dll. - Nếu dùng
-static, tất cả thư viện (bao gồm cảlibmylib.a) sẽ được nhúng vào.exe, loại bỏ phụ thuộc vàomylib.dll.
- Để giảm phụ thuộc, cân nhắc dùng
- Cấu trúc thư mục dễ hình dung:
bin/
├── myapp.exe
├── mylib.dll
├── libstdc++-6.dll (nếu không dùng -static-libstdc++)
├── libgcc_s_seh-1.dll (nếu không dùng -static-libgcc)
include/
├── mylib.h
lib/
├── libmylib.a