首页 > 其他分享 >静态链接——编译和链接

静态链接——编译和链接

时间:2024-07-16 17:25:16浏览次数:15  
标签:gcc 静态 hello 编译 usr include 链接

一、编译和链接的过程

1、GCC生成可执行文件的总体过程


在日常的开发过程中,IDE总是会帮我们将编译和链接合并,一键式的执行,即使在liunx中,使用命令行来编译一个源文件也只是简单的一句"gcc hello.c"。我们并没有过多的关注编译和链接的运行机制和机理,我想从本质出发,深入了解这些机制。对于下面一段hello.c代码

#include <stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}

在liunx中,当我们用GCC来编译时只需要`gcc hello.c`即可生成`a.out`文件(并不是所有可执行文件都是`.out`),使用`./a.out`即可运行输出。实际上,上述的过程可以分解为四个步骤,分别是预处理(Prepressing)、编译(Compilation)、汇编(Assembly)和链接(Linking)。


GCC编译过程分解

1.1、预编译(Prepressing)


预编译是指将源代码文件`(hello.c)`和相关头文件`(stdio.h)`等被**预编译器cpp**预编译成一个`.i`文件。需要注意的是对于C++程序来说,它的源代码文件的扩展名可能是`.cpp或.cxx`,头文件的扩展名可能是`.hpp`,而预编译后的文件扩展名是`.ii`。第一步预编译的过程相当于如下命令(E表示只进行预编译):` gcc -E hello.c -o hello.i 或者 cpp hello.c > hello.i`
预编译过程主要处理那些源代码文件中的以“#”开始的预编译指令。比如“#include”、“#define”等,主要处理规则如下:
  • 将所有的“define”删除,并且展开所有的宏定义。

  • 处理所有条件预编译指令,比如“#if”、“#ifdef'”、“#elif”、“#else”、“#endif'”。

  • 处理“#include”预编译指令,将被包含的文件插入到该预编译指令的位置。注意,这个过程是递归进行的,也就是说被包含的文件可能还包含其他文件。

  • 删除所有的注释。

  • 添加行号和文件名标识,比如#2“hello.c”2,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号。

  • 保留所有的#pragma编译器指令,因为编译器要使用它们。


# 1 "hello.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "hello.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 27 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
# 375 "/usr/include/features.h" 3 4
# 1 "/usr/include/sys/cdefs.h" 1 3 4
# 392 "/usr/include/sys/cdefs.h" 3 4
# 1 "/usr/include/bits/wordsize.h" 1 3 4
# 393 "/usr/include/sys/cdefs.h" 2 3 4
# 376 "/usr/include/features.h" 2 3 4
# 399 "/usr/include/features.h" 3 4
# 1 "/usr/include/gnu/stubs.h" 1 3 4
# 10 "/usr/include/gnu/stubs.h" 3 4
# 1 "/usr/include/gnu/stubs-64.h" 1 3 4
# 11 "/usr/include/gnu/stubs.h" 2 3 4
# 400 "/usr/include/features.h" 2 3 4
# 28 "/usr/include/stdio.h" 2 3 4
部分展示

经过预编译后的.i文件不包含任何宏定义,因为所有的宏已经被展开,并且包含的文件也已经被插入到.i文件中。所以当我们无法判断宏定义是否正确或头文件包含是否正确时,可以查看预编译后的文件来确定问题。

1.2、编译(Compilation)


编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生产相应的汇编代码文件,这个过程往往是我们所说的整个程序构建的核心部分,也是最复杂的部分之一。上面的编译过程相当于如下命令:

gcc -S hello.i -o hello.s


现在版本的GCC把预编译和编译两个步骤合并成一个步骤,使用一个叫做cc1的程序来完成这两个步骤。这个程序位于`usr/lib/gcc/i486-linux-gnu/4.1/`,我们也可以直接调用 ccl来完成它:

usr/lib/gcc/i486-linux-gnu/4.1/cc1 hello.c或者gcc -S hello.c -o hello.s都可以得到汇编输出文件helIo.s。对于C语言的代码来说,这个预编译和编译的程序是ccI,对于C++来说,有对应的程序叫做cclplus:Objective-C是cclobj::fortran是f77l;Java是 jc1。所以实际上gcc这个命令只是这些后台程序的包装,它会根据不同的参数要求去调用预编译编译程序cc1、汇编器as、链接器ld。


.file "hello.c"
.section .rodata
.LC0:
.string "hello world"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-44)"
.section .note.GNU-stack,"",@progbits
展示hello.s中的内容

1.3 汇编


汇编器是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。所以汇编器的汇编过程相对于编译器来讲比较简单,它没有复杂的语法,也没有语义,也不需要做指令优化,只是根据汇编指令和机器指令的对照表一一翻译就可以了,“汇编”这个名字也来源于此。上面的汇编过程我]可以调用汇编器as来完成:

as hello.s -o hello.o或者gcc -c hello.s -o hello.o或者使用gcc命令从C源代码文件开始,经过预编译、编译和汇编直接输出目标文件(Object File):gcc -c hello.c -o hello.o

1.4 链接


链接通常是一个让人比较费解的过程,为什么汇编器不直接输出可执行文件而是输出一个目标文件呢?链接过程到底包含了什么内容?为什么要链接?这恐怕是很多读者心中的疑惑。正是因为这些疑惑总是挥之不去,所以我们特意用这一章的篇幅来分析链接,具体地说分析静态链接的章节。下面让我们来看看怎么样调用ld才可以产生一个能够正常运行的 HelloWorld程序:

$ld -static /usr/lib/crt1.o /usr/lib/crti.o /uar/lib/gcc/1486-linux-gnu/4.1.3/crtbeginT.o -L/usr/lib/gcc/1486-linux-gnu/4.1.3 -L/usr/lib -L/lib hello.o --start-group-lgcc -lgcc_eh -1c --end-group /uar/lib/gcc/1486-linux-gnu/4.1.3/crtend.o /usr/lib/crtn.o

如果把所有的路径都省略掉,那么上面的命令就是:


ld -static crt1.o crti.o crtbeginT.o hello.o -start-group -lgcc -lgcc_eh -lc-end-group crtend.o crtn.o

可以看到,我们需要将一大堆文件链接起来才可以得到“a.out”,即最终的可执行文件。看了这行复杂的命令,可能很多读者的疑惑更多了,ctl.o、crti.o、crtbegin T.o、crtend.o、 crtn.o这些文件是什么?它们做什么用的?-lgcc-lgcc_ehlc这些都是什么参数?为什么要使用它们?为什么要将它们和hello.o链接起来才可以得到可执行文件?等等。后面我们会陆续讲解

标签:gcc,静态,hello,编译,usr,include,链接
From: https://blog.csdn.net/booming2/article/details/140470634

相关文章

  • 面向3-static、代码块、静态导入包,abstract(抽象类),interface(接口)、implements(接口
    static、代码块、静态导入包,abstract(抽象类),interface(接口)、implements(接口实现方式)static详解//staticpackageoop;publicclassF1{privatestaticintage;//静态的变量privatedoublescore;//非静态的变量publicvoidrun(){go();//当前......
  • 易优cms apache服务器伪静态规则
    易优cms在apache服务器环境默认自动隐藏index.php入口。如果发现没隐藏,可以检查根目录.htaccess是否含有以下红色代码段: <IfModule mod_rewrite.c>Options +FollowSymlinks -MultiviewsRewriteEngine on#http跳转到https#RewriteCond %{HTTPS} !=on#RewriteRule ^......
  • 编译xx.java文件时,未生成对应class文件
    现象:编译xx.java文件时,未生成对应class文件;或报错:不可映射字符原因:文件中有中文,编码模式不匹配控制台-属性-选项,可看见控制台的编码是GBK编码;如果.java文件里含有中文,需将.java文件的编码模式也改为GBK保存。  现象:执行命令>javaHello.class,报错:找不到或无法加载主类原......
  • 什么是 Java 中的静态变量和静态方法?它们在类与对象间的关系是如何体现的?
    在Java编程的世界里,静态变量和静态方法是两个非常基础且重要的概念,它们让我们的代码更加灵活和高效。想象一下,你正在设计一个班级管理系统,每个学生都有姓名和学号,但班级的名称只有一个,对所有人共享。这里的班级名称就可以用静态变量来表示,因为它不属于任何一个特定的学生,而是......
  • 条件断点 预编译头文件
    作用:当满足某些条件时才会触发断点怎么设置:代码左侧单击添加断点,右键条件,添加触发条件;就是说在条件断点处暂停运行;预编译头文件作用:抓取头文件,编译成二进制存放在文件中,可以避免多个文件包含相同头文件时多次编译读头文件注意:不能将经常改动的头文件放到预编译头文件中会......
  • Windows下C++动态链接库的生成以及使用
    目录一.前言二.生成动态链接库三.使用动态链接库四.其他一.前言这篇文章简单讨论一下Windows下如何使用VS生成和使用C++动态链接库,示例使用VS2022环境。二.生成动态链接库先创建C++项目-动态链接库(DLL)然后将默认生成的.h和.cpp文件清理干净,当然你也可以选择保......
  • C语言的编译和链接
    翻译环境和运⾏环境在ANSIC的任何⼀种实现中,存在两个不同的环境。第1种是翻译环境,在这个环境中源代码被转换为可执⾏的机器指令。第2种是执⾏环境,它⽤于实际执⾏代码。翻译环境那翻译环境是怎么将源代码转换为可执⾏的机器指令的呢?这⾥我们就得展开开讲解⼀下......
  • 基于AM5708开发板——开箱初探+环境搭建、源码编译
    本次测评板卡是创龙科技旗下的TL570x-EVM,它是一款基于TISitara系列AM5708ARMCortex-A15+浮点DSPC66x处理器设计的异构多核SOC评估板,由核心板和评估底板组成。核心板经过专业的PCBLayout和高低温测试验证,稳定可靠,可满足各种工业应用环境。评估板接口资源丰富,引出双路PRU百兆......
  • 程序员的电脑都有些什么?什么是必备的?(软件+资料+链接大全)
    前言网上看一堆教程,安装了五花八门的软件,说的是不是你?面对满天飞的广告,卡成ppt的编程软件,使用着蹩脚的编程语法…现在,我将结合我的经验,从选择合适的开发工具到配置高效的工作环境,帮助大家避免走弯路,直接步入高效编程的快车道。插件推荐1.VSCode(VisualStudioCode)它......
  • Hypertable 自编译二进制包安装
    HOWTOINSTALL==============Youcaneitherdownloadanappropriatebinarypackageforyourplatformorbuildfromsource.Binarypackagescanbeobtainedfrom[here](Download|Hypertable-BigData.BigPerformance).See[thiswikipage](http://code.......