0%

【构建】常用编译/链接选项说明及其应用分析

GCC编译选项CFLAGS参数及说明

  • -ffunction-sections : 告知编译器将每个函数放置在单独的小节中,与-Wl,--gc-sections搭配使用。使得可以在链接过程中只保留实际使用到的函数,未用到的函数将不会被链接,大大缩减生成可执行文件的大小

  • -fdata-sections : 标准的编译选项,与-ffunction-sections类似,告知编译器将全局和静态变量放置在单独的小节中。

  • --data-sections : 扩展的编译选项,只能在特定的编译器中使用,如GCC。

  • -std=gnu99 -mabi=aapcs : 指定使用C语言的GNU 99标准,-mabi=aapcs指定使用ARM架构的AAPCS(ARM Architecture Procedure Call Standard)ABI。

    指定编译器的C语言标准是有必要的,同时开发者需要明确的程序代码是否与编程标准兼容
    在Linux下输入arm-none-eabi-gcc -E -dM - </dev/null | grep "STDC_VERSION"查看编译器默认支持的C语言标准

  • -munaligned-access :是汇编语言的编译选项,告诉汇编器为非字节对齐的变量生成相应的指令,使用此选项后,汇编器将为非字节对齐的变量生成 lwa 或 swa 指令,而不是 lw 或 sw 指令

    lwa 和 swa 是用于访问非字节对齐内存的专用指令,而使用ls 或 sw 指令访问非字节对齐内存可能会导致程序崩溃或数据丢失

  • -funaligned-access :是 C 语言的编译选项,用于告诉 C 编译器为非字节对齐的变量生成相应的指令。使用此选项后,C 编译器将为非字节对齐的变量生成 lwa 或 swa 指令,而不是 lw 或 sw 指令

    访问非字节对齐变量(如一字节对齐的结构体等),通常需要引起高度注意,其可能带来内存访问错误

  • -mfpu=fpv4-sp-d16 :指定使用 FPU 的 fpv4-sp-d16 浮点单元,函数的参数直接传递到FPU的寄存器(s0、d0)中。fpv4-sp-d16 是 MIPS 架构的 32 位浮点单元,支持单精度和双精度浮点运算。

  • -mfloat-abi=hard :指定使用硬件浮点 ABI。硬件浮点 ABI 是 MIPS 架构的标准浮点 ABI,支持硬件浮点运算

    如果程序中使用了软件浮点库,则需要使用 -mfloat-abi=soft 来编译,否则将导致编译失败
    如果程序中使用了与硬件浮点 ABI 不兼容的代码,则需要修改代码,使其与硬件浮点 ABI 兼容,否则将导致编译失败
    需要确保-march和-mfpu参数与硬件和编译器兼容。如果处理器是Cortex-A9(ARMv7架构),那么应该使用-march=armv7-a -mfpu=neon

  • -mfloat-abi=soft :表示使用软件浮点 ABI,所有浮点运算都将在软件中进行,即使硬件中存在 FPU

  • -mfloat-abi=softfp :使用FPU硬件来做浮点运算,只是,函数的参数传递到整数寄存器(r0-r3)中,然后再传递到FPU中。


GCC链接选项LDFLAGS参数及说明

  • --specs=nosys.specs : 禁用标准库的系统调用,在嵌入式程序中应用。

    当编译时提示:undefined reference to _sbrk' 或 undefined reference to _write'd 等等时,可以加上此条选项

  • --specs=nano.specs : 不使用C语言标准库,改用微库,大大减小可执行文件大小。效果类似于应用newlib

    注意:使用微库后,不支持浮点数格式化标准输出

  • --specs=rdimon.specs : 不了解,在嵌入式MCU中加与不加都一样

  • **-Wl**:用于将其后的参数传递给链接器,其后可以跟随多个链接选项,每个选项用逗号分隔,具体含义取决于后面的链接选项

  • -Wl,--gc-sections : 告知链接器删除未使用的代码和数据段,搭配-fdata-sections-ffunction-sections使用

  • -Wl,–whole-archive :告诉链接器将指定的静态库中的所有对象文件都链接到生成的目标文件中,即使这些对象文件中定义的符号在目标文件中没有被使用

  • -Wl,–no-whole-archive :选项告诉链接器只链接到静态库中那些定义了目标文件中使用的符号的对象文件

  • -Wl,--wrap=symbol :用于为给定的符号创建一个包装函数 == 用于将symbol函数的调用重定向到__wrap_symbol函数,同时提供一个__real_symbol函数来调用原始的symbol函数

  • -L 和 -l :用于链接库文件,-L用于指定库文件的路径(如果库文件不在默认的搜索路径),-l用于指定要链接的库的名称(只需要提供库的名称,不需要lib前缀或.a、.so)

    例如,-L/path/to/library
    例如,对于libxxx.a或libxxx.so,只需要写-lxxx
    -lm,链接数学库,如果要用到<math.h>的数学库函数,则需要添加此链接选项

非典型应用示例

包装函数

如: -Wl,–wrap=_calloc_r和-Wl,–wrap=_malloc_r

  • 其会将所有的_calloc_r和_malloc_r函数调用重定向到__wrap__calloc_r和__wrap__malloc_r函数
  • 如果想调用原始的_calloc_r和_malloc_r函数,可以通过__real__calloc_r和__real__malloc_r函数来调用
  • 通常用于函数的包装:即在函数执行前后添加一些额外的操作
    1
    2
    3
    4
    5
    6
    7
    8
    9
    void *__wrap__calloc_r(struct _reent *reent_ptr, size_t nmemb, size_t size) {
    printf("calloc_r called with %zu, %zu\n", nmemb, size);
    return __real__calloc_r(reent_ptr, nmemb, size);
    }

    void *__wrap__malloc_r(struct _reent *reent_ptr, size_t size) {
    printf("malloc_r called with %zu\n", size);
    return __real__malloc_r(reent_ptr, size);
    }

控制链接器链接静态库文件

  • 以下为工程中链接选项的片段:
    • 其将以下指示的静态库中所有对象文件都链接到目标文件中
    • 而后将链接器恢复为只链接到静态库中定义了目标文件中使用的符号的对象文件
      1
      2
      3
      4
      5
      6
      7
      # xxx
      -Wl,--whole-archive \
      libs/libbootstrap.a libs/libbroadcast.a \
      libs/libsamgr.a libs/libtoken_static.a libs/libnative_file.a \
      libs/libhilog_lite.a libs/libhiview_lite.a libs/libhievent_lite.a \
      -Wl,--no-whole-archive
      # xxx

在链接其它工程静态库时,出现报错,需要考虑以下原因:

  • 编译器版本是否一致
  • 两者所处系统是否同一版本
  • 文件损坏

编译纠错修改以及一些注意点

  • 当编译提示:- undefined reference to _init,类似时,检查是否添加了编译选项--nostdlib,不链接标准库,却又没指定库,导致的编译错误

  • 提示- exit.c:(.text.exit+0x2c): undefined reference to _exit,考虑在.ld链接文件的.text段的末尾加上_exit = .;

  • 开启两级优化时, 需要注意C程序中汇编函数的调用可能会被优化掉, 可以考虑加上__attribute__ ((used))

  • 需要注意编译等级的选择,可能会影响到程序的运行,代码编写时需要严谨

  • 应用 -fdata-sections 可能会导致程序出错

    • 编程时尽量避免在不同的源文件中定义相同名称的全局变量或函数,避免链接错误
    • 确保先正确初始化全局变量或函数,然后再引用
  • 编译选项和链接选项是分开处理的,编译选项只影响编译过程,链接选项只影响链接过程。而,在GCC中,可以在同一条命令中指定编译选项和链接选项,GCC会在编译和链接过程中分别应用这些选项。但不建议这样做

Makefile应用实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 指定交叉编译工具链的 编译器、连接器、库管理器、
CROSS_COMPILE=arm-none-eabi-
CC=$(CROSS_COMPILE)gcc
LD=$(CROSS_COMPILE)ld
AR=$(CROSS_COMPILE)ar
AS=$(CROSS_COMPILE)as
OC=$(CROSS_COMPILE)objcopy
OD=$(CROSS_COMPILE)objdump
SZ=$(CROSS_COMPILE)size

# 指定目标硬件的架构和浮点运算单元 -mfpu=fpv4-sp-d16 -mfloat-abi=hard
# O0:禁用优化,O1:基本优化,O2, O3, Os
# g0:禁用调试信息, g1, g2, g3, g
MCU = -mcpu=cortex-m4 -mthumb \
-ffunction-sections \
-fdata-sections \
-Os -ggdb

# 定义编译C源文件的编译选项:禁用共享变量、函数和数据分段、优化级别、调试信息级别、开启所有警告、目标架构、预定义宏
CFLAGS= -c -fno-common \
--specs=rdimon.specs \
-std=gnu99 -mabi=aapcs \
-Wall \
$(MCU) \
$(C_DEFS)

# 定义链接器脚本、链接选项、目标文件格式转换选项、反汇编等等
LDSCRIPT=n32l40x_flash.ld
LDFLAGS = -Wl,--gc-sections --data-sections -mabi=aapcs $(MCU) -T$(LDSCRIPT) \
--specs=nosys.specs \
--specs=nano.specs \
-x assembler-with-cpp -Wa,-mimplicit-it=thumb
OCFLAGS = -Obinary
ODFLAGS = -S

参考站点