gcc 如何决定隐式包含哪些库?
How does gcc decide which libraries to implicitly include?
参考:
在一个小型微型嵌入式项目中,我发现编译后的代码比预期的要大得多。原来是因为我包含了使用 assert() 的代码。断言的使用在包含的代码中是合适的,但导致我编译的代码大小几乎翻了一番。
问题不在于 if/when 应该使用断言,而是 compiler/linker 如何决定包含断言的所有必要开销。
我原来的问题来自另一个post:
如果有人能向我解释 gcc 如何决定在调用 assert 时包含库函数,那将会很有帮助?我看到 assert.h 声明了一个外部函数 __assert_func。链接器如何知道从库中引用它而不是仅仅说 "undefined reference to __asert_func"?
gcc
(或g++
)命令只是一个驱动程序。它 运行 其他程序,包括编译器本身(cc1
用于 C 代码,cc1plus
用于 C++ 代码)以及汇编器和链接器。
是什么节目运行由spec file (and there is an implicit one, see the -dumpspecs
developer option)决定。顺便说一句,使用 -v
选项的 运行ning gcc
显示实际涉及的程序。
assert
宏被定义(参见文件 /usr/include/assert.h
)仅当 NDEBUG
不是已定义的预处理器符号时才在 <assert.h>
中进行一些检查。在我的 Linux/Glibc 系统上,它可以调用 C 标准库中的 __assert_failed
内部函数。引用 assert(3) 文档:
If the macro NDEBUG
is defined at the moment <assert.h>
was last
included, the macro assert()
generates no code, and hence does
nothing at all.
一些项目在生产模式下使用-DNDEBUG
它们的代码进行编译。
您应该阅读文档的 Invoking GCC 章节。
也许您想 compile with -ffreestanding
避免任何额外的库,即使是标准库?
在配置工具链时,作者决定默认应 link 编辑哪些库。
通常这包括 运行time startup/initializing 代码和名为 libc
的库,其中包含 C 标准的实现,以及作者认为相关的任何其他代码(例如 libc也可能实现 Posix、任何自定义电路板特定功能等),对于嵌入式目标,通常 link 到为目标实现 RTOS 的库。
您可以使用 gcc 的 -nodefaultlibs
标志在 linking 阶段忽略这些默认库。
对于assert(),它是一个标准的C macro/function,通常在libc中实现。如果失败,assert() 可能会打印到 stdout
,因此使用 assert() 可以引入实现 FILE* handling/buffering、printf 等的整个 stdio 设施,所有这些都在 libc 中实现。
如果您 运行 gcc -v
在 linking 阶段,您可以看到默认情况下 gcc link 的库。
在您的嵌入式系统上,链接是静态的。静态链接的工作原理如下。
静态库是目标文件的存档。链接器分别考虑每个对象。
在静态库中找到的引用函数或变量包含在生成的可执行文件中,连同包含引用符号的整个目标文件。不包含引用符号的目标文件不会被拉入。
这绝不是 gcc 特有的。链接器从一开始就是这样工作的。
参考
在一个小型微型嵌入式项目中,我发现编译后的代码比预期的要大得多。原来是因为我包含了使用 assert() 的代码。断言的使用在包含的代码中是合适的,但导致我编译的代码大小几乎翻了一番。
问题不在于 if/when 应该使用断言,而是 compiler/linker 如何决定包含断言的所有必要开销。
我原来的问题来自另一个post:
如果有人能向我解释 gcc 如何决定在调用 assert 时包含库函数,那将会很有帮助?我看到 assert.h 声明了一个外部函数 __assert_func。链接器如何知道从库中引用它而不是仅仅说 "undefined reference to __asert_func"?
gcc
(或g++
)命令只是一个驱动程序。它 运行 其他程序,包括编译器本身(cc1
用于 C 代码,cc1plus
用于 C++ 代码)以及汇编器和链接器。
是什么节目运行由spec file (and there is an implicit one, see the -dumpspecs
developer option)决定。顺便说一句,使用 -v
选项的 运行ning gcc
显示实际涉及的程序。
assert
宏被定义(参见文件 /usr/include/assert.h
)仅当 NDEBUG
不是已定义的预处理器符号时才在 <assert.h>
中进行一些检查。在我的 Linux/Glibc 系统上,它可以调用 C 标准库中的 __assert_failed
内部函数。引用 assert(3) 文档:
If the macro
NDEBUG
is defined at the moment<assert.h>
was last included, the macroassert()
generates no code, and hence does nothing at all.
一些项目在生产模式下使用-DNDEBUG
它们的代码进行编译。
您应该阅读文档的 Invoking GCC 章节。
也许您想 compile with -ffreestanding
避免任何额外的库,即使是标准库?
在配置工具链时,作者决定默认应 link 编辑哪些库。
通常这包括 运行time startup/initializing 代码和名为 libc
的库,其中包含 C 标准的实现,以及作者认为相关的任何其他代码(例如 libc也可能实现 Posix、任何自定义电路板特定功能等),对于嵌入式目标,通常 link 到为目标实现 RTOS 的库。
您可以使用 gcc 的 -nodefaultlibs
标志在 linking 阶段忽略这些默认库。
对于assert(),它是一个标准的C macro/function,通常在libc中实现。如果失败,assert() 可能会打印到 stdout
,因此使用 assert() 可以引入实现 FILE* handling/buffering、printf 等的整个 stdio 设施,所有这些都在 libc 中实现。
如果您 运行 gcc -v
在 linking 阶段,您可以看到默认情况下 gcc link 的库。
在您的嵌入式系统上,链接是静态的。静态链接的工作原理如下。
静态库是目标文件的存档。链接器分别考虑每个对象。
在静态库中找到的引用函数或变量包含在生成的可执行文件中,连同包含引用符号的整个目标文件。不包含引用符号的目标文件不会被拉入。
这绝不是 gcc 特有的。链接器从一开始就是这样工作的。