编译速度的优化 - 无代码优化(默认)。
1
,
2
,
3
优化以加快代码执行速度(数量越多,速度越快)。
文件大小的优化。
与级别
3
设置一样,
fast
忽略严格的标准合规性,以允许额外的优化。
调试体验的优化。
对于版本构建,请使用优化选项
-O2
。
在开发过程中,
-Og
选项在某些情况下对于调试程序或库很有用。由于某些 bug 只在某些优化级别上出现,因此请使用版本优化级别测试程序或库。
GCC 提供了大量选项来启用单个优化。如需更多信息,请参阅以下额外资源。
使用 GNU Compiler Collection -
控制优化的选项
GCC 的 Linux 手册页:
$ man gcc
当编译器将源代码转换为目标代码时,它可以添加各种检查来防止经常被利用的情况,并提高安全性。选择正确的编译器选项有助于生成更安全的程序和库,而无需更改源代码。
$ gcc ... -O2 -g -Wall -Wl,-z,now,-z,relro -fstack-protector-strong -fstack-clash-protection -D_FORTIFY_SOURCE=2 ...
-
对于程序,添加
-fPIE
和
-pie
位置独立可执行文件选项。
对于动态链接库,强制
-fPIC
(Position Independent Code)选项会间接提高安全性。
使用以下选项来检测开发过程中的安全漏洞:使用这些选项与发行版本的选项相结合:
$ gcc ... -Walloc-zero -Walloca-larger-than -Wextra -Wformat-security -Wvla-larger-than ...
2.1.7. Example:使用 GCC 构建一个 C 程序(在一个步骤中编译和链接)
此示例演示了构建一个简单示例 C 程序的确切步骤。
在本例中,编译和链接代码在一个步骤中完成。
您必须了解如何使用 GCC。
创建一个目录
hello-c
,并进到其中:
$ mkdir hello-c
$ cd hello-c
创建包含以下内容的文件
hello.c
:
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
使用 GCC 编译和链接代码:
$ gcc hello.c -o helloworld
这会编译代码,创建目标文件 hello.o ,并从目标文件链接可执行文件 helloworld 。
运行生成的可执行文件:
$ ./helloworld
Hello, World!
2.1.8. Example:使用 GCC 构建一个 C 程序(编译和连接在两个步骤中)
此示例演示了构建一个简单示例 C 程序的确切步骤。
在本例中,编译和链接代码是两个独立的步骤。
您必须了解如何使用 GCC。
创建一个目录
hello-c
,并进到其中:
$ mkdir hello-c
$ cd hello-c
创建包含以下内容的文件
hello.c
:
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
使用 GCC 编译代码:
$ gcc -c hello.c
目标文件 hello.o 已创建。
从目标文件链接可执行文件 helloworld :
$ gcc hello.o -o helloworld
运行生成的可执行文件:
$ ./helloworld
Hello, World!
2.1.9. Example:使用 GCC 构建一个 C++ 程序(在一个步骤中编译和链接)
本例显示了构建最小 C++ 程序的确切步骤。
在本例中,编译和链接代码在一个步骤中完成。
您必须了解
gcc
和
g++
之间的区别。
创建
hello-cpp
目录,并进到其中:
$ mkdir hello-cpp
$ cd hello-cpp
创建包含以下内容的文件
hello.cpp
:
#include <iostream>
int main() {
std::cout << "Hello, World!\n";
return 0;
使用 g++ 编译和链接代码:
$ g++ hello.cpp -o helloworld
这会编译代码,创建目标文件 hello.o ,并从目标文件链接可执行文件 helloworld 。
运行生成的可执行文件:
$ ./helloworld
Hello, World!
2.1.10. Example:使用 GCC 构建一个 C++ 程序(编译和连接在两个步骤中)
本例显示了构建最小 C++ 程序的确切步骤。
在本例中,编译和链接代码是两个独立的步骤。
您必须了解
gcc
和
g++
之间的区别。
创建
hello-cpp
目录,并进到其中:
$ mkdir hello-cpp
$ cd hello-cpp
创建包含以下内容的文件
hello.cpp
:
#include <iostream>
int main() {
std::cout << "Hello, World!\n";
return 0;
使用 g++ 编译代码:
$ g++ -c hello.cpp
目标文件 hello.o 已创建。
从目标文件链接可执行文件 helloworld :
$ g++ hello.o -o helloworld
运行生成的可执行文件:
$ ./helloworld
Hello, World!
了解在代码中使用库。
对库使用特殊文件名惯例:名为
foo
的库应该以文件
lib
foo
.so
或
lib
foo
.a
的形式存在。通过链接 GCC 的输入选项(而非输出选项)可自动理解这一惯例:
当链接到库时,只能将其名称
foo
和
-l
作为
-l
foo
来指定库:
$ gcc ... -lfoo ...
在创建库时,必须指定完整文件名
lib
foo
.so
或
lib
foo
.a
。
第 2.3.2 节 “soname 机制”
开发人员在使用完全编译的语言构建应用程序时可以选择使用静态或动态链接。务必要了解静态和动态链接之间的区别,特别是在 Red Hat Enterprise Linux 上使用 C 和 C++ 语言的上下文中。总之,红帽不建议对 Red Hat Enterprise Linux 的应用程序使用静态链接。
库是可在您的程序中重复使用的代码软件包。C 或 C++ 库由两个部分组成:
静态库作为包含目标文件的存档提供。链接后,它们成为生成的可执行文件的一部分。
出于安全原因,红帽不建议使用静态链接。请参阅
第 2.2.2 节 “静态和动态链接”
。仅在需要时才使用静态链接,特别是对红帽提供的库。
GCC 必须安装在您的系统上。
您必须了解静态和动态链接。
您有一组组成有效程序的源或目标文件,需要一些静态库
foo
,但没有其他库。
foo
库作为
libfoo.a
文件提供,对于动态链接,不提供文件
libfoo.so
。
作为 Red Hat Enterprise Linux 一部分的大多数库都只支持动态链接。以下步骤仅适用于
没有
为动态链接启用的库。请参阅
第 2.2.2 节 “静态和动态链接”
。
要从源和目标文件链接程序,请添加静态链接库
foo
,该库可作为
libfoo.a
文件找到:
进到包含您代码的目录。
编译带有
foo
库的头的程序源文件:
$ gcc ... -Iheader_path -c ...
使用包含
foo
库的头文件的目录的路径替换
header_path
。
将程序与
foo
库链接:
$ gcc ... -Llibrary_path -lfoo ...
使用包含文件
libfoo.a
的目录的路径替换
library_path
。
要稍后运行该程序,只需:
$ ./program
动态库作为独立的可执行文件提供,在链接时和运行时需要。它们独立于您应用程序的可执行文件。
GCC 必须安装在系统上。
组成有效程序的一组源或目标文件需要一些动态库
foo
,但不需要其他库。
foo
库必须作为文件
libfoo.so
提供。
$ gcc ... -Llibrary_path -lfoo ...
当程序链接了动态库时,生成的程序必须总是在运行时加载库。定位库有两个选项:
使用存储在可执行文件本身中的
rpath
值
在运行时使用
LD_LIBRARY_PATH
变量
2.2.6. 将静态库和动态库与 GCC 一起使用
有时需要静态链接一些库,有时需要动态链接一些库。这种情况带来了一些挑战。
了解静态和动态链接
gcc
识别动态和静态库。遇到
-l
foo
选项时,
gcc
将首先尝试查找包含
foo
库的动态链接版本的共享目标(一个
.so
文件),然后查找包含库的静态版本的存档文件(
.a
)。因此,这个搜索可能会导致以下情况:
只找到了共享目标,
gcc
会动态链接它。
只找到了归档,
gcc
会静态链接它。
共享目标和存档都都找到了,默认情况下,
gcc
会选择对共享目标的动态链接。
共享目标和存档都未找到,链接失败。
由于这些规则,选择用于链接的库的静态或动态版本的最佳方法是让
gcc
只找到该版本。在指定
-L
path
选项时,可以使用或省略包含库版本的目录,来在某种程度上控制它。
此外,由于动态链接是默认的,因此链接被明确指定的唯一情形是存在两个版本的库都应被静态链接时。有两种可能的解决方案:
通过文件路径而不是
-l
选项指定静态库
使用
-Wl
选项将选项传给链接器
$ gcc ... path/to/libfoo.a ...
从文件扩展名
.a
,
gcc
将理解为这是一个与程序链接的库。但是,指定库文件的全整路径是一个不太灵活的方法。
了解创建库以及 Linux 操作系统用于库的必要概念的步骤。
对库使用特殊文件名惯例:名为
foo
的库应该以文件
lib
foo
.so
或
lib
foo
.a
的形式存在。通过链接 GCC 的输入选项(而非输出选项)可自动理解这一惯例:
当链接到库时,只能将其名称
foo
和
-l
作为
-l
foo
来指定库:
$ gcc ... -lfoo ...
在创建库时,必须指定完整文件名
lib
foo
.so
或
lib
foo
.a
。
第 2.3.2 节 “soname 机制”
动态加载的库(共享对象)使用一个名为
soname
的机制来管理库的多个兼容版本。
您必须了解动态链接和库。
您必须了解 ABI 兼容性的概念。
您必须了解库命名约定。
您必须了解符号链接。
动态加载的库(共享目标)作为一个独立的可执行文件存在。这使得可以在不更新依赖于它的应用程序的情况下更新库。但是,这个概念会出现以下问题:
库的实际版本的标识
需要存在同一库的多个版本
表示多个版本中每个版本的 ABI 兼容性
$ objdump -p somelibrary | grep SONAME
使用您要检查的库的实际文件名替换
somelibrary
。
动态链接的库(共享目标)允许:
通过代码重用来节约资源
通过更轻松地更新库代码来提高安全性
按照以下步骤从源构建和安装动态库。
您必须了解 soname 机制。
GCC 必须安装在系统上。
您必须有库的源代码。
进到有库源文件的目录。
使用位置独立代码选项
-fPIC
将每个源文件编译成目标文件:
$ gcc ... -c -fPIC some_file.c ...
目标文件具有与原始源代码文件相同的文件名,但它们的扩展名是
.o
。
链接目标文件的共享库:
$ gcc -shared -o libfoo.so.x.y -Wl,-soname,libfoo.so.x some_file.o ...
使用的主版本号是 X ,次版本号是 Y。
将
libfoo.so.x.y
文件复制到合适的位置,其中系统的动态链接器可以找到它。在 Red Hat Enterprise Linux 中,库的目录是
/usr/lib64
:
# cp libfoo.so.x.y /usr/lib64
请注意,您需要 root 权限才能操作此目录中的文件。
为 soname 机制创建符号链接结构:
# ln -s libfoo.so.x.y libfoo.so.x
# ln -s libfoo.so.x libfoo.so
其他资源
-
Linux 文档项目 - 程序库 HOWTO -
3。共享库
通过将目标文件转换为特殊类型的存档文件,可以创建用于静态链接的库。
出于安全原因,红帽不建议使用静态链接。只在需要时才使用静态链接,特别是红帽提供的库。详情请查看
第 2.2.2 节 “静态和动态链接”
。
系统上必须安装了 GCC 和 binutils 。
您必须了解静态和动态链接。
提供了要作为库共享的函数的源文件。
使用 GCC 创建中间目标文件。
$ gcc -c source_file.c ...
如果需要,可附加更多源文件。生成的目标文件共享文件名,但使用
.o
文件扩展名。
使用
binutils
软件包中的
ar
工具将目标文件转换为静态库(存档)。
$ ar rcs libfoo.a source_file.o ...
文件
libfoo.a
已创建。
使用
nm
命令检查生成的归档:
$ nm libfoo.a
将静态库文件复制到合适的目录。
与库链接时,GCC 将自动从
.a
文件扩展名中识别出库是一个用于静态链接的存档。
$ gcc ... -lfoo ...
其他资源
-
ar(1)
的 Linux 手册页:
$ man ar
GNU make 程序(通常缩写为
make
)是一个控制从源文件生成可执行文件的工具。
make
自动确定复杂程序的哪个部分已更改,需要重新编译。
make
使用名为 Makefile 的配置文件来控制构建程序的方式。
2.4.1. GNU
make
和
Makefile
概述
要从特定项目的源文件创建一个可用的表单(通常是可执行文件),请执行几个必要的步骤。记录操作及其顺序,以便稍后可重复这些操作。
Red Hat Enterprise Linux 包含 GNU
make
,这是专为此目的设计的构建系统。
了解编译和链接的概念
2.4.2. Example:使用 Makefile 构建一个 C 程序
按照本示例中的步骤,使用 Makefile 构建一个示例 C 程序。
您必须理解 Makefile 的概念和
make
。
创建一个
hellomake
目录,并进到此目录:
$ mkdir hellomake
$ cd hellomake
创建包含以下内容的文件
hello.c
:
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("Hello, World!\n");
return 0;
创建包含以下内容的文件 Makefile :
CC=gcc
CFLAGS=-c -Wall
SOURCE=hello.c
OBJ=$(SOURCE:.c=.o)
EXE=hello
all: $(SOURCE) $(EXE)
$(EXE): $(OBJ)
$(CC) $(OBJ) -o $@
%.o: %.c
$(CC) $(CFLAGS) $< -o $@
clean:
rm -rf $(OBJ) $(EXE)
Makefile recipe 行必须以制表符字符开头!从文档中复制上面的文本时,剪切和粘贴过程可能会粘贴空格而不是制表符。如果发生这种情况,请手动纠正问题。
运行 make :
$ make
gcc -c -Wall hello.c -o hello.o
gcc hello.o -o hello
这将创建一个可执行文件 hello 。
运行可执行文件 hello :
$ ./hello
Hello, World!
运行 Makefile 目标
clean
以删除创建的文件:
$ make clean
rm -rf hello.o hello
有关
make
的更多信息,请参阅以下列出的资源。
|