1. GCC编译/连接/优化等选项
- 1. GCC编译/连接/优化等选项
- 1.1. 简介
- 1.2. 常用选项
- 1.2.1.
-c -E -S -o
- 1.2.2.
-L<path> -l<library>
- 1.2.3.
-D<macro>
- 1.2.4.
-I<path>
- 1.2.1.
- 1.3. 代码生成和优化
- 1.3.1.
-std=<standard>
- 1.3.2.
-shared
- 1.3.3.
-fPIC
- 1.3.4.
-static
- 1.3.5.
-O0 -O1 -O2 -O3 -Os
- 1.3.6.
-ffunction-sections
和-fdata-sections
- 1.3.1.
- 1.4. 调试与警告选项
- 1.4.1.
-g -ggdb
- 1.4.2.
-Wall -Werror -Wextra
- 1.4.3.
-Wno-pointer-sign
- 1.4.4.
-fno-strict-aliasing -fstrict-aliasing
- 1.4.1.
- 1.5. 安全与连接选项
- 1.5.1.
-z noexecstack -z relro -z now
- 1.5.2.
-Wl,option
- 1.5.3.
-Wl,--strip-all,--gc-sections,--as-needed
- 1.5.4.
-Wl,--verbose,-soname,<name>
- 1.5.5.
-Wl,-T,<script>,-Ttext,<address>,-Tdata,<address>
- 1.5.1.
- 1.6. 高级选项
- 1.6.1.
-pedantic
- 1.6.2.
-fsanitize=<type>
- 1.6.3.
-rpath <path>
- 1.6.1.
1.1. 简介
GCC 编译流程分为四个阶段:预处理、编译、汇编、链接,这里讲解gcc用到的一些参数
-
GCC 原生选项:直接由 GCC 处理,如:
- -o(输出文件名)
- -lm(链接数学库 libm)
- -Wall(启用警告)
- -O2(优化级别)
-
链接器专属选项:需通过 -Wl, 传递给链接器(如 ld),如:
- –gc-sections(移除未使用的节)
- -soname(设置共享库运行时名称)
- -T(使用自定义链接脚本)
1.2. 常用选项
1.2.1. -c -E -S -o
-E 仅进行预处理,输出预处理后的代码。
gcc -E source.c -o source.i
-c 只进行编译,不进行链接,生成目标文件(.o)
gcc -c source.c
# 生成 source.o
-S 生成汇编代码(.s)。
gcc -S source.c
# 生成 source.s
-o 指定输出文件的名称
gcc source.c -o program
1.2.2. -L<path> -l<library>
-l 链接指定的库(如-lm链接数学库)。
-L 指定库文件的搜索路径。
gcc source.c -L/path/to/lib -lmylib
1.2.3. -D<macro>
定义宏(等价于在代码中使用#define)。
1.2.4. -I<path>
指定头文件的搜索路径。
1.3. 代码生成和优化
1.3.1. -std=<standard>
指定 C/C++ 标准(如-std=c99、-std=c++17)。
1.3.2. -shared
生成共享库(.so)
1.3.3. -fPIC
生成位置无关代码(用于共享库)。
1.3.4. -static
静态链接所有库(生成独立可执行文件)。
gcc -static source.c
1.3.5. -O0 -O1 -O2 -O3 -Os
选项 | 优化目标 | 编译时间 | 代码体积 | 运行性能 | 典型场景 |
---|---|---|---|---|---|
-O0 | 无优化(调试) | 最快 | 最大 | 最慢 | 开发调试 |
-O1 | 平衡性能与时间 | 较快 | 较小 | 一般 | 日常开发的 release 版本 |
-O2 | 优化性能 | 较长 | 中等 | 快 | 生产环境默认 |
-O3 | 最高性能 | 最长 | 最大 | 最快 | 高性能计算 |
-Os | 最小代码体积 | 较长 | 最小 | 接近 | - O2 嵌入式系统、固件 |
1.3.6. -ffunction-sections
和 -fdata-sections
-ffunction-sections
:将每个函数放在独立的 ELF 节(section)中。
-fdata-sections
:将每个全局变量放在独立的节中。
目的:配合链接器选项 -Wl,--gc-sections
(垃圾回收),在最终二进制文件中移除未使用的函数和变量,减小文件体积。
1.4. 调试与警告选项
1.4.1. -g -ggdb
-g 生成调试信息,用于 GDB 调试。
-ggdb 生成更详细的 GDB 调试信息。
一般配合 -O0 使用, 优化选项(如 -O2)可能会改变代码结构,导致调试信息与实际执行不匹配。
选项 | 调试信息格式 | 适用调试器 | 文件大小 | 调试体验 |
---|---|---|---|---|
-g | 标准 DWARF | 通用 | 较小 | 基本调试 |
-ggdb | GDB 扩展 | GDB 专用 | 较大 | 高级调试 |
1.4.2. -Wall -Werror -Wextra
-Wall 启用常见的编译警告(如未使用的变量、隐式转换等)。
-Werror 将所有警告视为错误,强制修复警告。
-Wextra 启用额外的警告(如未初始化的变量、冗余代码等)。一般用的比较少
1.4.3. -Wno-pointer-sign
-Wno-pointer-sign 是 GCC 编译器的一个警告选项,用于禁用关于指针符号不匹配的警告
禁用警告:通过 -Wno-pointer-sign 可以关闭这类警告,避免编译时产生干扰信息。
相反选项:对应的启用警告选项是 -Wpointer-sign,但通常通过 -Wall 或 -Wextra 自动启用。
1.4.4. -fno-strict-aliasing -fstrict-aliasing
-fstrict-aliasing
(默认启用):严格遵循别名规则,进行激进优化。
-fno-strict-aliasing
:禁用规则,允许不同类型指针别名,可能导致显著的性能损失。
- 禁用 GCC 的严格别名规则优化,允许不同类型指针指向同一块内存。
- 禁用优化会降低性能,建议
优先修改代码以符合严格别名规则
(如使用 union)。 - 适用于遗留代码或依赖类型双关(type punning)的场景(如底层硬件访问)。
int a = 1;
float* f = (float*)&a; // 违反规则
*f = 3.14f;
printf("%d\n", a); // 可能输出 1(而非预期的修改后的值)
解决方案:
- 使用 union 实现类型双关,确保类型兼容。
- 使用字符指针转换
- 使用 memcpy
1.5. 安全与连接选项
1.5.1. -z noexecstack -z relro -z now
-z noexecstack
- 现代操作系统(如 Linux)支持内存区域的 NX 位(No-eXecute),通过该选项将栈标记为不可执行。
- 若攻击者尝试在栈上执行恶意代码,会触发内存访问异常。
- 某些动态链接库(如使用 setjmp/longjmp 的库)可能需要可执行栈,此时需谨慎使用。
- 现代 Linux 发行版默认启用 NX 保护,但通过 -z execstack 可禁用(不推荐)。
-z relro
(Relocation Read-Only)- 功能:启用只读重定位(Relocation Read-Only)。
- 防御机制:将全局偏移表(GOT,Global Offset Table)和动态符号表标记为只读,防止攻击者修改这些表中的函数地址。
-z now
- 功能:强制链接器立即绑定所有符号(而非延迟绑定)。
- 防御机制:减少动态加载时的符号表修改机会,增强 RELRO 的安全性。
1.5.2. -Wl,option
-Wl,option 是 GCC 编译器用于将参数传递给链接器(如 ld)的语法。通过 , 分隔多个选项,例如 -Wl,–gc-sections,-Map=output.map
gcc source.c -Wl,-o,program.out # -o 选项,指定输出文件名,等价于 gcc source.c -o program.out
gcc source.c -Wl,-e,my_entry_point # -e 选项,指定入口点,代替 main 函数,等价于 gcc source.c -emy_entry_point
gcc source.c -Wl,-L,/path/to/lib # -L 选项,指定库搜索路径,等价与 gcc source.c -L/path/to/lib
gcc source.c -Wl,-l,m # -l 选项,等价与 gcc source.c -lm,建议直接使用 -lm,语法更简洁。
- 选项类别 必须使用 -Wl, 的典型选项
- 代码优化 --gc-sections, --as-needed
- 内存布局 -T, -Ttext, -Tdata
- 共享库 -soname, --version-script
- 安全增强 -z noexecstack, -z relro, -z now
- 调试 --verbose, --cref
1.5.3. -Wl,--strip-all,--gc-sections,--as-needed
gcc source.c -Wl,--strip-all,--gc-sections,--as-needed
gcc source.c -Wl,--wrap,malloc # 使用 __wrap_malloc 替代 malloc
--gc-sections
移除未使用的代码和数据段(需配合 -ffunction-sections 和 -fdata-sections)。- 用于移除可执行文件中未使用的代码和数据段(sections),从而减小二进制文件体积。
- 若某个节被标记为 USED(如通过 attribute((used))),则不会被回收。
- 启用 GC 会增加链接时间(需分析所有符号),但通常对运行时性能无影响。
- 若二进制体积不是关键因素,可关闭此选项以加快编译。
- -O2 会自动启用一些内联优化,但无法替代 --gc-sections 的全局代码删除能力。
- 验证:使用 size 命令对比文件大小
--strip-all 或 --strip-debug
移除符号表和调试信息。--as-needed
仅链接真正使用的共享库(减少依赖)。--wrap <symbol>
允许替换标准库函数(用于测试或钩子)。
1.5.4. -Wl,--verbose,-soname,<name>
gcc source.c -Wl,--verbose,-soname,libmylib.so.1
--verbose
输出链接过程的详细信息。
-soname <name>
设置共享库的运行时名称(用于版本控制)。
1.5.5. -Wl,-T,<script>,-Ttext,<address>,-Tdata,<address>
gcc source.c -Wl,-T,memory_layout.ld
gcc source.c -Wl,-Ttext,0x10000
gcc source.c -Wl,-Tdata,0x20000
-T <script>
使用自定义链接脚本(用于嵌入式系统或特殊内存布局)。
-Ttext <address>
指定代码段(.text)的加载地址。
-Tdata <address>
指定数据段(.data)的加载地址。
1.6. 高级选项
1.6.1. -pedantic
严格遵循语言标准,拒绝非标准特性。
1.6.2. -fsanitize=<type>
启用运行时检查(如内存泄漏、未定义行为)。
1.6.3. -rpath <path>
设置运行时库搜索路径(替代 LD_LIBRARY_PATH)。
gcc source.c -Wl,-rpath,/path/to/lib