Menu Close

Red Hat Training

A Red Hat training course is available for RHEL 8

第 2 章 使用 GCC 构建代码

本章论述了源代码必须转换为可执行代码的情况。

2.1. 代码表单之间的关系

先决条件

  • 了解编译和连接的概念

可能的代码表单

C 和 C++ 语言有三种代码格式:

  • 使用 C 或者 C++} 语言编写的源代码,以纯文本文件的形式出现。

    文件通常使用扩展,如 .c.cc.cpp.h.hpp.i.inc。有关支持的扩展及其解释的完整列表,请查看 gcc 手册页:

    $ man gcc
  • 对象代码,通过使用 编译器 编译源代码来创建。这是一个中间形式。

    对象代码文件使用 .o 扩展名。

  • 可执行代码,通过带有一个 linkerlinking 对象代码来创建。

    Linux 应用程序可执行文件不使用任何文件名扩展名。共享对象(library)可执行文件使用 .so 文件名扩展。

注意

还存在用于静态链接的库归档文件。这是使用 .a 文件名称扩展的对象代码变体。不建议使用静态链接。请查看 第 3.2 节 “静态和动态链接”

处理 GCC 中的代码表单

从源代码生成可执行代码会分为两个步骤,这需要不同的应用程序或工具。GCC 可以用作编译器和链路器的智能驱动程序。这可让您在任何所需操作中使用单个 gcc 命令(编译和连接)。GCC 会自动选择操作及其序列:

  1. 源文件被编译到对象文件中。
  2. 对象文件和库链接(包括之前编译的源)。

可以运行 GCC 以便它只执行第 1 步、只执行第 2 步,或执行第 1 步和第 2 步。这由输入类型和请求的输出类型决定。

因为大型项目需要一个构建系统,它通常为每个操作单独运行 GCC,所以最好始终将编译和连接视为两个不同的操作,即使 GCC 可以同时执行。

2.2. 将源文件编译到对象代码

要立即从源文件而不是可执行文件创建对象代码文件,必须指示 GCC 只创建对象代码文件作为其输出。此操作是大型项目的构建过程的基本操作。

先决条件

流程

  1. 进入包含源代码文件的目录。
  2. 使用 -c 选项运行 gcc:

    $ gcc -c source.c another_source.c

    对象文件被创建,其文件名反映了原始源代码文件: source.c 结果为 source.o

    注意

    使用 C++ 源代码,将 gcc 命令替换为 g++ 以方便地处理 C++ Standard 库依赖项。

2.3. 使用 GCC 启用 C 和 C++ 应用程序调试

由于调试信息较大,默认情况下不包含在可执行文件中。要启用 C 和 C++ 应用程序调试,您必须明确指示编译器创建它。

要在编译和链接代码时使用 GCC 创建调试信息,请使用 -g 选项:

$ gcc ... -g ...
  • 由编译器和链接器执行的优化可执行代码,与原始源代码很难执行:变量可能会优化、循环未滚动,操作合并到相邻的源代码等。这会影响调试。要改进的调试体验,请考虑使用 -Og 选项设置优化功能。但是,更改优化级别会更改可执行代码,并可能会更改实际行为,包括删除一些程序错误。
  • 要在调试信息中包含宏定义,请使用 -g3 选项而不是 -g
  • -fcompare-debug GCC 选项测试由 GCC 编译的带有调试信息且没有调试信息的代码。如果得到的两个二进制文件相同,测试会传递。此测试可确保可执行代码不受任何调试选项的影响,这可以进一步确保 debug 代码中没有隐藏的错误。请注意,使用 -fcompare-debug 选项会显著增加编译时间。有关这个选项的详情,请查看 GCC 手册页。

其它资源

2.4. 使用 GCC 的代码优化

单个程序可以转换为多个机器指令。如果您在编译过程中为分析代码分配更多资源,则可以实现更最佳的结果。

使用 GCC,您可以使用 -Olevel 选项设置优化级别。这个选项接受一组值来代替 级别

level描述

0

优化编译速度 - 不优化代码优化(默认)。

1, 2, 3

优化代码执行速度(容量越大,速度越高)。

s

文件大小优化。

fast

与级别 3 设置相同,另外 fast 会考虑严格的标准合规性,以允许额外的优化。

g

调试体验优化。

对于发行版本构建,使用优化选项 -O2

在开发过程中,该 -Og 选项在某些情况下可用于调试程序或库。因为一些程序错误清单只带有特定的优化级别,所以使用发行优化级别测试程序或库。

GCC 提供了大量启用单独优化的选项。如需更多信息,请参阅以下附加资源。

其它资源

2.5. 使用 GCC 强化代码的选项

当编译器将源代码转换为对象代码时,它可以添加各种检查来防止通常被利用的情况并提高安全性。选择正确的编译器选项可帮助生成更安全的程序及库,而无需更改源代码。

发行版本选项

对于针对 Red Hat Enterprise Linux 的开发人员,推荐的选项列表最少:

$ gcc ... -O2 -g -Wall -Wl,-z,now,-z,relro -fstack-protector-strong -fstack-clash-protection -D_FORTIFY_SOURCE=2 ...
  • 对于程序,添加 -fPIE-pie 独立执行可执行选项。
  • 对于动态链接的库,强制的 -fPIC (独立代码)选项会间接提高安全性。

开发选项

使用以下选项检测开发过程中的安全漏洞。这些选项和发行版本的选项一起使用:

$ gcc ... -Walloc-zero -Walloca-larger-than -Wextra -Wformat-security -Wvla-larger-than ...

其它资源

2.6. 链接代码以创建可执行文件

连接是构建 C 或 C++ 应用程序时的最后步骤。将所有对象文件和库合并到可执行文件中。

先决条件

流程

  1. 进入包含对象代码文件的目录。
  2. 运行 gcc:

    $ gcc ... objfile.o another_object.o ... -o executable-file

    从提供的对象文件和库创建一个名为 executable-file 的可执行文件。

    要连接其他库,请在对象文件列表后添加所需的选项。如需更多信息,请参阅 第 3 章 在 GCC 中使用代理

    注意

    使用 C++ 源代码,将 gcc 命令替换为 g++ 以方便地处理 C++ Standard 库依赖项。

2.7. 示例:使用 GCC 构建 C 程序

本例演示了构建简单示例 C 程序的具体步骤。

先决条件

  • 您必须了解如何使用 GCC。

流程

  1. 创建目录 hello-c 并更改它:

    $ mkdir hello-c
    $ cd hello-c
  2. 创建包含以下内容的 hello.c 文件:

    #include <stdio.h>
    
    int main() {
      printf("Hello, World!\n");
      return 0;
    }
  3. 使用 GCC 编译代码:

    $ gcc -c hello.c

    对象文件 hello.o 已创建。

  4. 从对象文件链接可执行文件 helloworld:

    $ gcc hello.o -o helloworld
  5. 运行生成的可执行文件:

    $ ./helloworld
    Hello, World!

2.8. 示例:使用 GCC 构建 C++ 程序

本例演示了构建最小 C++ 程序示例的步骤。

先决条件

  • 您必须了解 gccg++ 之间的区别。

流程

  1. 创建目录 hello-cpp 并更改它:

    $ mkdir hello-cpp
    $ cd hello-cpp
  2. 创建包含以下内容的 hello.cpp 文件:

    #include <iostream>
    
    int main() {
      std::cout << "Hello, World!\n";
      return 0;
    }
  3. 使用 g++ 编译代码:

    $ g++ -c hello.cpp

    对象文件 hello.o 已创建。

  4. 从对象文件链接可执行文件 helloworld:

    $ g++ hello.o -o helloworld
  5. 运行生成的可执行文件:

    $ ./helloworld
    Hello, World!