程序员 GNU C 编译器指南

深入了解生成二进制文件的步骤,以便在出现问题时,您知道如何逐步解决问题。
7 位读者喜欢这篇文章。
GitHub launches Open Source Friday 

Opensource.com

C 是一种著名的编程语言,深受经验丰富和新手程序员的欢迎。用 C 语言编写的源代码使用标准英语术语,因此被认为是人类可读的。但是,计算机只能理解二进制代码。要将代码转换为机器语言,您需要使用称为编译器的工具。

GCC(GNU C 编译器)是一种非常常见的编译器。编译过程涉及几个中间步骤和相邻工具。

安装 GCC

要确认您的系统是否已安装 GCC,请使用 gcc 命令

$ gcc --version

如果需要,请使用您的软件包管理器安装 GCC。在基于 Fedora 的系统上,使用 dnf

$ sudo dnf install gcc libgcc

在基于 Debian 的系统上,使用 apt

$ sudo apt install build-essential

安装后,如果您想查看 GCC 的安装位置,请使用

$ whereis gcc

使用 GCC 的简单 C 程序

这是一个简单的 C 程序,演示如何使用 GCC 编译代码。打开您喜欢的文本编辑器并粘贴以下代码

// hellogcc.c
#include <stdio.h>
 
int main() {
	printf("Hello, GCC!\n");
return 0;
}

将文件另存为 hellogcc.c,然后编译它

$ ls
hellogcc.c

$ gcc hellogcc.c

$ ls -1
a.out
hellogcc.c

如您所见,a.out 是编译生成的默认可执行文件。要查看新编译的应用程序的输出,只需像运行任何本地二进制文件一样运行它

$ ./a.out
Hello, GCC!

命名输出文件

文件名 a.out 不是很具描述性,因此如果您想为可执行文件指定特定名称,可以使用 -o 选项

$ gcc -o hellogcc hellogcc.c

$ ls
a.out  hellogcc  hellogcc.c

$ ./hellogcc
Hello, GCC!

当开发需要编译多个 C 源文件的大型应用程序时,此选项非常有用。

GCC 编译的中间步骤

即使 GCC 在简单用例中自动执行编译,但实际上编译有四个步骤。

  1. 预处理:GNU C 预处理器 (cpp) 解析头文件(#include 语句),展开宏(#define 语句),并生成一个中间文件,例如 hellogcc.i,其中包含展开的源代码。
  2. 编译:在此阶段,编译器将预处理的源代码转换为特定 CPU 架构的汇编代码。生成的汇编文件以 .s 扩展名命名,例如本例中的 hellogcc.s
  3. 汇编:汇编器 (as) 将汇编代码转换为目标文件中的机器代码,例如 hellogcc.o
  4. 链接:链接器 (ld) 将目标代码与库代码链接,以生成可执行文件,例如 hellogcc

运行 GCC 时,使用 -v 选项可以查看每个步骤的详细信息。

$ gcc -v -o hellogcc hellogcc.c
Compiler flowchart

(Jayashree Huttanagoudar,CC BY-SA 4.0)

手动编译代码

体验编译的每个步骤可能很有用,因为在某些情况下,您不需要 GCC 完成所有步骤。

首先,删除当前文件夹中 GCC 生成的文件,但源文件除外。

$ rm a.out hellogcc.o

$ ls
hellogcc.c

预处理器

首先,启动预处理器,将其输出重定向到 hellogcc.i

$ cpp hellogcc.c > hellogcc.i

$ ls
hellogcc.c  hellogcc.i

查看输出文件,并注意预处理器如何包含头文件和展开宏。

编译器

现在您可以将代码编译为汇编代码。使用 -S 选项将 GCC 设置为仅生成汇编代码。

$ gcc -S hellogcc.i

$ ls
hellogcc.c  hellogcc.i  hellogcc.s

$ cat hellogcc.s

查看汇编代码,了解生成了什么。

汇编

使用您刚刚生成的汇编代码创建目标文件

$ as -o hellogcc.o hellogcc.s

$ ls
hellogcc.c  hellogcc.i  hellogcc.o  hellogcc.s

链接

要生成可执行文件,您必须将目标文件链接到它所依赖的库。这不像之前的步骤那么容易,但它具有教育意义

$ ld -o hellogcc hellogcc.o
ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000
ld: hellogcc.o: in function `main`:
hellogcc.c:(.text+0xa): undefined reference to `puts'

在链接器完成查看 libc.so 库后,会出现引用 undefined puts 的错误。您必须找到合适的链接器选项来链接所需的库以解决此问题。这不是一件容易的事,它取决于您的系统的布局方式。

链接时,您必须将代码链接到核心运行时 (CRT) 对象,这是一组帮助二进制可执行文件启动的子例程。链接器还需要知道在哪里找到重要的系统库,包括 libc 和 libgcc,尤其是在特殊的开始和结束指令中。这些指令可以使用 --start-group--end-group 选项或使用 crtbegin.ocrtend.o 的路径来分隔。

此示例使用 RHEL 8 安装中出现的路径,因此您可能需要根据您的系统调整路径。

$ ld -dynamic-linker \
/lib64/ld-linux-x86-64.so.2 \
-o hello \
/usr/lib64/crt1.o /usr/lib64/crti.o \
--start-group \
-L/usr/lib/gcc/x86_64-redhat-linux/8 \
-L/usr/lib64 -L/lib64 hello.o \
-lgcc \
--as-needed -lgcc_s \
--no-as-needed -lc -lgcc \
--end-group
/usr/lib64/crtn.o 

Slackware 上的相同链接器过程使用一组不同的路径,但您可以看到过程的相似之处

$ ld -static -o hello \
-L/usr/lib64/gcc/x86_64-slackware-linux/11.2.0/ \
/usr/lib64/crt1.o /usr/lib64/crti.o \
hello.o /usr/lib64/crtn.o \
--start-group -lc -lgcc -lgcc_eh \
--end-group

现在运行生成的可执行文件

$ ./hello
Hello, GCC!

一些有用的实用程序

以下是一些实用程序,可帮助检查文件类型、符号表以及与可执行文件链接的库。

使用 file 实用程序确定文件类型

$ file hellogcc.c
hellogcc.c: C source, ASCII text

$ file hellogcc.o
hellogcc.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

$ file hellogcc
hellogcc: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=bb76b241d7d00871806e9fa5e814fee276d5bd1a, for GNU/Linux 3.2.0, not stripped

使用 nm 实用程序列出目标文件的符号表

$ nm hellogcc.o
0000000000000000 T main
             	          U puts

使用 ldd 实用程序列出动态链接库

$ ldd hellogcc
linux-vdso.so.1 (0x00007ffe3bdd7000)
libc.so.6 => /lib64/libc.so.6 (0x00007f223395e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2233b7e000)

总结

在本文中,您了解了 GCC 编译的各种中间步骤以及用于检查文件类型、符号表和与可执行文件链接的库的实用程序。下次您使用 GCC 时,您将了解它生成二进制文件的步骤,并且当出现问题时,您知道如何逐步解决问题。

标签
User profile image.
Jayashree Huttanagoudar 是红帽印度有限公司的高级软件工程师。她在中间件 OpenJDK 团队工作。她总是对学习新事物充满好奇,这为她的工作增添了色彩。

评论已关闭。

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 许可。
© . All rights reserved.