本机环境 mingw64
C:\mingw64\bin
卷 Windows 的文件夹 PATH 列表 卷序列号为 F023-2216 C:\MINGW64\BINaddr2line.exear.exeas.exec++.exec++filt.execc.execpp.exedlltool.exedllwrap.exeelfedit.exeg++.exegcc-ar.exegcc-nm.exegcc-ranlib.exegcc.exegcov-dump.exegcov-tool.exegcov.exegdb-add-indexgdb.exegdborig.exegdbserver.exegendef.exegenidl.exegenpeimg.exegfortran.exegprof.exegstackld.bfd.exeld.exelibatomic-1.dlllibgcc_s_seh-1.dlllibgfortran-5.dlllibgomp-1.dlllibquadmath-0.dlllibstdc++-6.dlllibwinpthread-1.dlllto-dump.exemingw32-make.exenm.exeobjcopy.exeobjdump.exeranlib.exereadelf.exesize.exestrings.exestrip.exewidl.exewindmc.exewindres.exex86_64-w64-mingw32-c++.exex86_64-w64-mingw32-g++.exex86_64-w64-mingw32-gcc-15.2.0.exex86_64-w64-mingw32-gcc-ar.exex86_64-w64-mingw32-gcc-nm.exex86_64-w64-mingw32-gcc-ranlib.exex86_64-w64-mingw32-gcc.exex86_64-w64-mingw32-gfortran.exe
他们的作用
| GNU Binutils 是一系列的二进制工具的集合 | |
| 主要 | |
| ldI(Gld) | GNU 链接器,有关链接的详细介绍请参见后文 |
| as(Gas) | GNU Assembler,通常称为gas或as,是由GNU项目开发的汇编程序。 GAS第一个版本的 在1986-1987年发布,由Dean Elsner编写,当时支持的是VAX架构 有关汇编的详细介绍 https://en.wikipedia.org/wiki/GNU_Assembler https://ee209-2019-spring.github.io/references/gnu-assembler.pdf (2.14版本) https://astro.uni-bonn.de/~sysstw/CompMan/gnu/as.html (1994年,你可以看到很多支持的其他架构) http://web.mit.edu/gnu/doc/html/as_1.html ( 这个手册是针对GNU assembler as的指南.)注意: 1)汇编器(Assembler)是将汇编语言翻译为机器语言的程序。一般而言,汇编生成的是目标代码,需要经gnu 链接器(ldl)生成可执行代码才可以执行 2) GAS,即 GNU 汇编器,是 GNU 操作系统的默认的汇编器。它不仅仅是支持x86架构,它适用于许多不同的体系结构并支持多种汇编语言语法。 3)默认是作为gcc后端工具来使用,也就是不是直接调用 as 命令来执行编译,是由其他工具来调用 4)默认语法是AT&T语法 5)支持两种基本注解 /**/ 和 # arm架构用 @作为注解 i386架构,x86-64架构,mips架构,risc-v架构 等都采用 # AArch64架构采用 // |
| gold | a new, faster, ELF only linker. |
| 其他 | |
| addr2line |
用 来将程序 地址转 换成其所 对应的程 序源文 件及所对 应的代 码行,也可以得到所对应的函数。 |
| ar |
可以对静态库做创建、修改和提取的操作 1) windows static lib 以 .lib 为 后缀 的文 件 ,share lib 以 .dll 为 后缀 |
| c++filt | 反编译(反混淆,demangle)C++符号的工具 |
| dlltool | 创建创建Windows动态库 |
| elfedit | |
| gprof | 性能分析(profiling)工具程序 |
| gprofng | |
| nlmconv | 可以转换成NetWare Loadable Module(NLM)目标文件格式 |
| nm | 显示目标文件内的符号信息 |
| objcopy |
将一种对象文件翻译成另一种格式,譬如将.bin 转换成.elf、或者将.elf 转换成.bin 等。 |
| objdump | 主要的作用是反汇编。显示目标文件的相关信息 |
| ranlib | 产生静态库的索引 |
| readelf | 显示有关 ELF 文件的信息 |
| size | 列出可执行文件每个部分的尺寸和总尺寸,代码段、数据段、总大小等,请参见后文了解使用 size 的具体使用实例 |
| strings | 列出文件中可打印的字符串信息 |
| strip | 从目标文件中移除符号信息 |
| windmc | Windows消息资源编译器 |
| windres | Windows资源文件编译器 |
| ldd | 可以用于查看一个可执行程序依赖的share lib。 |
| ld | 它用于连接目标文件和库文件生成可执行文件或共享库文件。 |
一、简单的测试
1.g++ Test.cpp
生成默认为a.exe的文件,这个过程包含了编译和链接。但是直接会忽略
2. -o <输出的程序名称>
例如: D:\dlltest\cjiajia\test>g++ -o t.exe test.cpp
-o命令表示指定可执行文件的名称 ,gcc/g++命令是非常灵活的,你不指定输出的文件名的时候,默认生成的是a.exe文件。
二、c++四部曲
编写测试文件
#include <iostream> using namespace std; static int t = 1; #define T 888 typedef int s;int main() {s i = 1;cout << T * i << endl; return 0; }
步骤一、预处理生成 .i 的文件
D:\dlltest\cjiajia\test>g++ -E test.cpp > test.i (或 g++ -E test.c -o test.i)
(-E 表示预处理阶段结束后停止,以便我们更好的分析 生成的test.i文件)
D:\dlltest\cjiajia\test 的目录2025/12/07 17:33 <DIR> .
2025/12/07 15:53 <DIR> ..
2025/12/07 15:47 164 test.cpp
2025/12/07 15:50 1,049,399 test.i
2025/12/07 15:39 440 test2.cpp
2025/12/07 17:33 1,847 说明.txt5 个文件 1,053,226 字节2 个目录 601,594,114,048 可用字节
test.i如下:
# 0 "test.cpp" # 0 "<built-in>" # 0 "<command-line>" # 1 "test.cpp" # 1 "C:/mingw64/lib/gcc/x86_64-w64-mingw32/15.2.0/include/c++/iostream" 1 3 # 40 "C:/mingw64/lib/gcc/x86_64-w64-mingw32/15.2.0/include/c++/iostream" 3 # 1 "C:/mingw64/lib/gcc/x86_64-w64-mingw32/15.2.0/include/c++/bits/requires_hosted.h" 1 3 # 31 "C:/mingw64/lib/gcc/x86_64-w64-mingw32/15.2.0/include/c++/bits/requires_hosted.h" 3 # 1 "C:/mingw64/lib/gcc/x86_64-w64-mingw32/15.2.0/include/c++/x86_64-w64-mingw32/bits/c++config.h" 1 3 # 37 "C:/mingw64/lib/gcc/x86_64-w64-mingw32/15.2.0/include/c++/x86_64-w64-mingw32/bits/c++config.h" 3 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wvariadic-macros"
...略...
步骤二、生成.s文件
预处理后的代码.i文件 被编译成汇编语言,生成 .s 文件(汇编代码)
使用的是as程序
D:\dlltest\cjiajia\test>g++ -S test.cpp 根据.i 文件, 生成.s文件D:\dlltest\cjiajia\test>dir驱动器 D 中的卷没有标签。卷的序列号是 7C23-817ED:\dlltest\cjiajia\test 的目录2025/12/07 17:33 <DIR> . 2025/12/07 15:53 <DIR> .. 2025/12/07 15:47 164 test.cpp 2025/12/07 15:50 1,049,399 test.i 2025/12/07 17:33 1,376 test.s 2025/12/07 15:39 440 test2.cpp 2025/12/07 17:33 1,847 说明.txt5 个文件 1,053,226 字节2 个目录 601,594,114,048 可用字节
test.s文件如下
.file "test.cpp"
.text
.data
.align 4
_ZL1t:
.long 1
.text
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
....略.....
步骤 3: 汇编 (Assembly)
汇编器将汇编代码转换为机器码,生成 .o 文件(目标文件)。
D:\dlltest\cjiajia\test>g++ -c test.cppD:\dlltest\cjiajia\test>dir驱动器 D 中的卷没有标签。卷的序列号是 7C23-817ED:\dlltest\cjiajia\test 的目录2025/12/07 17:38 <DIR> . 2025/12/07 15:53 <DIR> .. 2025/12/07 15:47 164 test.cpp 2025/12/07 15:50 1,049,399 test.i 2025/12/07 17:38 1,581 test.o 2025/12/07 17:37 1,376 test.s 2025/12/07 15:39 440 test2.cpp 2025/12/07 17:33 1,847 说明.txt6 个文件 1,054,807 字节2 个目录 601,594,109,952 可用字节
步骤 4: 链接 (Linking)
ld负责将多个目标文件及库文件链接在一起,生成最终的可执行文件(Windows上为 .exe,Linux上为 .out)。
D:\dlltest\cjiajia\test>g++ test.o -o test.exe (也可执行g++ test.o ,默认生成a.exe,linux 一般是.out文件)
#-o为指定生成的可执行程序的名称 D:\dlltest\cjiajia\test>dir驱动器 D 中的卷没有标签。卷的序列号是 7C23-817ED:\dlltest\cjiajia\test 的目录2025/12/07 17:41 <DIR> . 2025/12/07 15:53 <DIR> .. 2025/12/07 15:47 164 test.cpp 2025/12/07 17:41 54,589 test.exe 2025/12/07 15:50 1,049,399 test.i 2025/12/07 17:38 1,581 test.o 2025/12/07 17:37 1,376 test.s 2025/12/07 15:39 440 test2.cpp 2025/12/07 17:39 1,847 说明.txt7 个文件 1,109,396 字节2 个目录 601,594,048,512 可用字节D:\dlltest\cjiajia\test>test 888
我们看到他的过程和c语言的编译过程是一样的,请参看 汇编的教程(四)- c程序的编译四个过程 - jinzi - 博客园
一步到位
你完全可以一步到位,执行 g++ test.cpp 直接生成a.exe
D:\dlltest\cjiajia\test>g++ test.cpp
他会产生一个a.exe ,直接就可
或通过-o 指定生成文件名称 g++ -o mytest.exe test.cpp
D:\dlltest\cjiajia\test3>g++ -o mytest.exe test.cpp
他会在当前目录下生成由-o指定的mytest.exe
三、复杂点的
需要把相关目录都创建好,否则会出现问题
project/
|-bin
|-includefoo.h
|-lib|-all|-shared|-static
|-srcfoo1.cppfoo2.cppmain.cpp
// foo.h
#ifndef TEST#define TESTint getnum();void sayhello(); #endif
源文件有两个版本
foo1.cpp 静态库 lib/foo.a
foo2.cpp 动态库lib /foo.so
源代码,这里foo1.cpp和foo2.cpp都对头文件foo.h中的两个函数getnum(),sayhello()分别进行了实现。
// foo1.cpp #include "foo.h" #include <iostream>int getnum(){return 1; }void sayhello(){std::cout<<"hello,I am foo1\n"; }// foo2.cpp #include "foo.h" #include <iostream>int getnum(){return 2; }void sayhello(){std::cout<<"hello,I am foo2\n"; }
我们将在main.cpp中引用头文件foo.h,用多种方式调用这个库 foo
// main.cpp #include "foo.h"int main(){sayhello();return 0; }
我们将在lib/static存在静态库,在lib/shared存放动态库,在lib/all存放动态库和静态库。
1) windows 静态库 以 .lib 为 后缀 的文 件 ,动态库(共享) 以 .dll 为 后缀
2)linux 静态库 以 .a 为 后 缀 的 文 件 , 动态库 以 .so 为 后 缀
1.静态库的编写
# 编译获得目标文件 g++ -c src/foo1.cpp -Iinclude -o lib/static/foo1.o# 打包得到.a静态库,注意前面是输出文件,后面是输入文件(可以有多个输入) ar -crv lib/static/libfoo.a lib/static/foo1.o# 从lib/static复制一份到lib/all cp lib/static/libfoo.a lib/all/libfoo.a
2.动态库的编写
# 注意在生成目标文件的时候需要加-fPIC选项,生成位置无关的目标文件 g++ -Iinclude -fPIC -c src/foo2.cpp -o lib/shared/foo2.o # 注意得到动态库时,需要加-shared选项 g++ -shared lib/shared/foo2.o -o lib/shared/libfoo.so# 两步可以合并为一步,效果一样 g++ -fPIC -shared src/foo2.cpp -Iinclude -o lib/shared/libfoo.so# 从lib/shared复制一份到lib/all cp lib/shared/libfoo.so lib/all/libfoo.so
注意:
- 从目标文件到动态库文件,必须使用
-shared选项 - 从源文件到目标文件,建议使用
-fPIC选项,可以生成位置无关的代码,此时动态库在内存中只需要加载一次,多个程序可以共同并且同时使用;否则只能相当于代码拷贝的方式,多个程序需要多次加载同一个动态库到内存中使用 - 相比于静态库,动态库生成之后的存放位置是很重要的,因为可执行程序在运行时,还需要找到动态库并一起加载到内存中
- 由于动态库通常有
-fPIC,而静态库通常没有,因此动态库中如果想要链接一个静态库会报错,解决办法可以是把静态库也添加选项,改为生成位置无关的代码。
库的使用
首先,库的名称需要满足一定的命名规范,对于静态库通常命名为libxxx.a,对于动态库的名称通常为libxxx.so.x.y.z,还需要附带动态库的版本号。