你真的懂Hello World!吗?(编译与链接,静态链接与动态链接)

news2025/2/27 14:22:23

💫Hello World!

  对于大家来说Hello World!应该是最熟悉不过的一句话,我们从Hello World!走进了计算机的世界,但是你真的了解Hello World!吗?你又思考过它背后蕴含的机理吗?他是怎么从代码变成程序的你真的思考过吗?
  今天本篇文章会对它的底层做最基本的讲解,后续博主会再次对它进行深入解析!!
  那么我们开始吧!!

💫被隐藏的部分——编译与链接

  对于visual studio这样的集成开发平台(编译,链接,调试集一体),要看到细节是很难的,所有本文将使用Linux的gcc编译器进行操作。
  一个源文件变成可执行程序需要经过以下几个步骤:
1.预处理(也叫预编译)  2.编译  3.汇编  4.链接
  每个步骤又包含有许多小步骤。

⭐️预处理

  预处理的工作主要有以下:
1.宏替换(删除所有的“#define”,展开所有的宏)
2.条件编译
3.头文件包含
4.去注释

  我们对以下程序做预处理:
在这里插入图片描述

  我们输入编译指令,查看预处理之后生成的.i文件
  输入指令gcc -E file.c -o file.i -Dversion1=1(与在文件内部#define version1 1作用相同)
  (-E指令表示进行程序翻译在预处理完成后停下来
  (-o指令表示命名,上面指令表示把经过预处理后的文件命名为file.I)
  (-D能够实现对程序进行动态裁剪,假如我们的version1,version2,version3分别表示程序的不同版本,那么通过条件编译就能控制程序不同版本的发布)
  我们截取file.i的一部分
在这里插入图片描述  我们可以看到,以上几个步骤实际发生了,这里需要提及一下头文件包含,实际上头文件包含就是把头文件里面的内容拷贝到了我们的文件里。

⭐️编译

  编译的过程就是对预处理后文件进行一系列的处理(词法分析,语法分析,语义分析,符号汇总以及优化过程)最后产生汇编代码的过程。
我们对一个最简单的表达式做以上处理:

int A=7+9

  词法分析简单来说就是记录符号的过程。

记号类型
A标识符
7数字
9数字

  语法分析是生成语法树的过程。
在这里插入图片描述

  语义分析就是对表达式做语义分析判断它是否有意义,语义又分为静态和动态语义,静态语义通常是声明和类型转换,动态语义指在程序运行出现的语义问题,例如除零错误。
语义分析后的语法树
在这里插入图片描述  我们在命令行输入
gcc -S file.i -o file.s
  (-S指令表示开始程序翻译,编译步骤结束后停下来)
  以下就是编译后的汇编代码
(注:我们不需要懂汇编代码,现在我们只需要知道有转汇编这个步骤)
在这里插入图片描述

⭐️汇编

  汇编就是将汇编代码变成机器可以执行的指令的过程,最后输出一个目标文件(可重定位二进制文件),汇编有一个重要的步骤——形成符号表这个需要我们注意。
  在命令行输入
gcc -c file.s -o file.o
(-c指令表示开始程序翻译,汇编步骤结束后停下来)
  打开.o文件
在这里插入图片描述  里面大部分都是乱码,但是我们可以看到ELF和.data这些符号,对。这个很重要。
  我们可以使用od指令打开它,但是并没有一点用,二进制我又看不懂。
在这里插入图片描述
  与其说它是一个二进制文件,但实际上他是一个ELF文件。它有重要的段表和符号表,就是基于这个文件才能进行后续的链接步骤。ELF文件类型很多(例如动态库文件,可执行文件,可重定位二进制文件都是ELF文件)。

  我们可以使用readelf工具读取ELF文件
命令行输入readelf -a file.o可以详细的查看ELF文件,包括ELF文件头,段表,符号表等等

文件头
在这里插入图片描述段表
包含段的信息,有address(段虚拟地址),offset(段偏移),flags(段标志)等等等一系列信息。
在这里插入图片描述符号表

在这里插入图片描述  细心的小伙伴可能发现符号表里面怎么有puts符号,懵逼了吧,其实这里是gcc搞的鬼,他把只打印一个字符串的printf自作聪明的换成了puts.

num:符号表数组下标
value:符号值
size:符号大小
type:符号类型
bind:绑定信息
ndx:在语言中是否使用
name:符号名

⭐️链接

  链接是重新翻译的最后一步,链接后就生成可执行程序。
  链接步骤中至关重要的就是段表的合并符号表的合并与重定位工作
  关于符号表
  通过上面的学习我们知道在编译过程中有对符号的汇总,汇编步骤形成符号表,最后链接对符号表进行合并与重定位,他是怎么实现的。
我们有以下程序:
在这里插入图片描述
在这里插入图片描述  我们就拿Add符号举一个例子。
  在编译时两个文件都对符号进行汇总,两个文件都有一个相同符号Add
  在汇编阶段形成符号表,对于Add.c文件形成的符号表会给Add符号一个实际地址,对于test.c文件形成的符号表编译器并不知道它的目标地址会把他的目标地址置为0.
  最后链接器将他们链接起来的时候会将地址修正,也就是重定位。
  分别查看三个文件的符号表

在这里插入图片描述在这里插入图片描述
  最后的可执行程序的符号表就不再展示,链接会链接库,最后的符号表有库文件的符号,使得可执行符号表很长,有兴趣的小伙伴可自行查看。
  关于段表
  我们可以使用objdump工具查看段
  命令行输入objdump -h test.o
在这里插入图片描述其中size表示段长,file off表示段的位置

  这里介绍一下常见且易于理解的段
   .text段(代码段):存放编译后的机器指令
   .data段(数据段):初始化的全局变量以及静态局部变量(如果初始化为0则将其放在.bss段)
  .bss段:未初始化的全局变量以及静态局部变量
  .rodata(只读数据段):只读变量,例如const变量,以及字符串常量
  .rel.text:重定位表,存储的重定位信息。
命令行输入objdump -x -s -d test.o
(-s表示把段内容以十六进制打印出来,-x表示反汇编)
在这里插入图片描述  我们看.data和.rodata段
  .data前四个从低到高是0x0a 0x00 0x00 0x00表示的就是global_init_var
  同理后四个字节是static_var
  .rodata的256400既是表示字符串常量%d
  命令行输入objdump -r test.o可查看重定位表
  以下是代码段重定位表
  offset表示重定位入口的偏移。
在这里插入图片描述

💫动态链接与静态链接

⭐️ 动态库与静态库

动态库静态库
Windows.dll.lib
Linux.so.a

  动态库用于动态链接,静态库用于静态链接
  动静态库的本质其实是目标文件的集合。
  在Linux中链接默认进行动态链接。
file查看文件类型,ldd查看库依赖
在这里插入图片描述在这里插入图片描述框出的部分是C运行库

⭐️ 静态链接

  静态链接就是把我们的.o文件和使用到库中的文件链接形成可执行程序的过程,在我们使用某个库的时候,可能我们所使用到的这个库可能也依赖其他库,那么对他也需要链接,但是这个琐碎的步骤是由链接器完成的。
  对于静态链接你可以把它理解成拷贝其他文件代码的过程,需要哪里就拷贝哪里,最后形成的可执行程序不再依赖其他库就能运行。

⭐️ 动态链接

  动态链接的基本思想是把程序按照模块拆分成各个相对独立的部分,在运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行程序。
  动态链接形成的可执行程序每次运行都需要链接,重定位是不是很慢,确实,它相较于静态链接形成的可执行程序会慢,但是它有自己的解决方法即是延迟绑定,延迟绑定的基本思想就是:当函数第一次使用到的时候才会进行绑定,对于未使用到函数不会进行绑定。

⭐️ 对比

  动态链接节省资源但依赖库
  静态链接浪费资源但不再依赖库
我们对一个相同的程序分别采用两种链接方式生成一个可执行程序看一看。
在这里插入图片描述  file1和file2分别是动态链接和静态链接形成的可执行程序,我们通过上表可以明显看出他们在体积上的差别。
  对于静态库的安装

sudo yum install -y glibc-static
sudo yum install -y libstdc+±static

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1347127.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LINUX 抓包工具Tcpdump离线安装教程

本次教程基于内网环境无法访问网络使用安装包进行安装抓包工具 1、首先给大家看下一共有6个安装包,依次进行解压,包我就放到csdn上了,需要的可以联系我进行下载 2打包然后传到服务器任意一个目录下,进入到当前目录,然后…

第11章 访问维护

上一章讨论了在目标主机上提升权限的方法。本章将介绍在滲透测试过程的最后一个环节,即帮助我们随时进入目标主机的方法。 在完成了提升权限的阶段性工作之后,我们应当建立一种机制,以维持对目标主机的控制权。这样一来,即使我们…

GPT3.5 改用 GPT4 价格翻了30倍 如何破局? GPT 对话成本推演

场景介绍 假设你搭建了一个平台,提供 ChatGPT 3.5 的聊天服务。目前已经有一批用户的使用数据,想要测算一下如果更换 GPT 4.0 服务需要多少成本? 方案阐述 如果是全切,最简单粗暴的方案就是根据提供 ChatGPT 3.5 消费的金额乘…

Rust学习笔记004:Rust的所有权机制

内存相关的基础知识 不同语言的内存管理系统 栈和堆 堆和栈的使用 引出所有权方案 String类型 Rust 的所有权机制 Rust 的所有权机制是一种内存管理系统,它允许在编译时通过所有权、借用和生命周期来确保内存安全,同时避免了垃圾回收的运行时开销。这些…

2023-12-21 LeetCode每日一题(美丽塔 II)

2023-12-21每日一题 一、题目编号 2866. 美丽塔 II二、题目链接 点击跳转到题目位置 三、题目描述 给你一个长度为 n 下标从 0 开始的整数数组 maxHeights 。 你的任务是在坐标轴上建 n 座塔。第 i 座塔的下标为 i ,高度为 heights[i] 。 如果以下条件满足&a…

软件工程PPT 笔记摘录(2)

分析软件需求 UML 提供了用例图来分析和描述用例视角的软件需求模型 UML 提供了交互图和状态图来描述行为视角的软件需求模型 UML 提供了类图来描述和分析业务领域的概念模型 顺序图:强调消息传递的时间序 通信图:突出对象间的合作 类图&#xff0…

【SAP-FICO】--总账标识配置路径OBXR

FICO业务需求: F-02,财务会计凭证填写09客户A时,带出的总账标识为可编辑。 需求截图: 第一步:了解需求 首先,我们要明白,财务凭证生成,是分多种类型(不同类型的凭证&a…

【深度学习:Few-shot learning】理解深入小样本学习中的孪生网络

【深度学习:Few-shot learning】理解深入小样本学习中的孪生网络 深入理解孪生网络:架构、应用与未来展望小样本学习的诞生元学习小样本学习孪生网络的基本概念孪生网络的细节Triplet Loss架构特点关键组件训练过程主要应用领域未来展望示例图片结论 备注…

[JS设计模式]Prototype Pattern

Prototype pattern Prototype pattern可便于同类型的多个对象共享属性。原型(prototype)是JS原生的对象,其他对象可以通过原型链(prototype chain)来访问原型。单独看这句描述可能还是有点儿抽象,下面通过…

【HarmonyOS】ArkTS语言介绍与组件方式运用

从今天开始,博主将开设一门新的专栏用来讲解市面上比较热门的技术 “鸿蒙开发”,对于刚接触这项技术的小伙伴在学习鸿蒙开发之前,有必要先了解一下鸿蒙,从你的角度来讲,你认为什么是鸿蒙呢?它出现的意义又是…

OSG绘制视锥体

最近要来实现一个相机位姿态可视化的需求&#xff0c;不想使用pangolin&#xff0c;不好集成&#xff0c;想用osg来做可视化。以下是demo效果。 代码实现&#xff1a; // Cone_of_vision.cpp : 定义控制台应用程序的入口点。 //#include "stdafx.h" #include <os…

Angular进阶之六:Progressive rendering

简介 Progressive Rendering 是一种提高 Web 应用性能的方法&#xff0c;允许页面在加载过程中逐步呈现&#xff0c;以提高用户体验。在本文中&#xff0c;我们将探讨如何在 Angular 中通过自定义指令实现 Progressive Rendering&#xff0c;特别是处理从服务器获取大量数据的…

PAT 乙级 1046 划拳

划拳是古老中国酒文化的一个有趣的组成部分。酒桌上两人划拳的方法为&#xff1a;每人口中喊出一个数字&#xff0c;同时用手比划出一个数字。如果谁比划出的数字正好等于两人喊出的数字之和&#xff0c;谁就赢了&#xff0c;输家罚一杯酒。两人同赢或两人同输则继续下一轮&…

数据预处理时,怎样处理类别型特征?

1. 序号编码 序号编码通常用于处理类别间具有大小关系的数据。例如成绩&#xff0c;可以分为低、中、高三档&#xff0c;并且存在“高>中>低”的排序关系。序号编码会按照大小关系对类别型特征赋予一个数值ID&#xff0c;例如高表示为3、中表示为2、低表示为1&#xff0…

【网络安全 | MD5截断比较】PHP、Python脚本利用

前言 在解题中&#xff0c;当遇到类似 substr(md5(a),-6,6) 7788这样的MD5截断比较的题目时&#xff0c;只有求出a的值才能进行接下来的操作。 一个一个去猜是不可能的&#xff0c;通常使用脚本解决&#xff0c;文末给出实战案例。 PHP循环脚本 <?phpfor($i1;$i<9…

机器学习(一) -- 概述

系列文章目录 机器学习&#xff08;一&#xff09; -- 概述 机器学习&#xff08;二&#xff09; -- 数据预处理 未完待续…… 目录 系列文章目录 前言 一、机器学习定义&#xff08;是什么&#xff09; 二、机器学习的应用&#xff08;能做什么&#xff09; 三、***机器…

蒙牛的京东店铺的数字化经营

1、蒙牛的京东店铺的数字化经营框架是怎样的&#xff1f; 2. 蒙牛的京东店铺是如何进行环境分析的&#xff1f; 结合波特五力模型进行分析&#xff1a;客户、供应商、企业自身、潜在竞争对手、同行业竞争对手 3.蒙牛的京东店铺采取了哪些方式进行线上引流&#xff1f; 找智商长…

2023-12-23 LeetCode每日一题(移除石子使总数最小)

2023-12-23每日一题 一、题目编号 1962. 移除石子使总数最小二、题目链接 点击跳转到题目位置 三、题目描述 给你一个整数数组 piles &#xff0c;数组 下标从 0 开始 &#xff0c;其中 piles[i] 表示第 i 堆石子中的石子数量。另给你一个整数 k &#xff0c;请你执行下述…

Zookeeper-Zookeeper应用场景实战(二)

1. Zookeeper 分布式锁实战 1.1 什么是分布式锁 在单体的应用开发场景中涉及并发同步的时候&#xff0c;大家往往采用Synchronized&#xff08;同步&#xff09;或者其他同一个 JVM内Lock机制来解决多线程间的同步问题。在分布式集群工作的开发场景中&#xff0c;就需要 一种…

Spring Cloud Gateway + Nacos 实现动态路由

1、maven 依赖 主要依赖 <!-- 网关 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency>案件差不多完整主要依赖 <!--Spring boot 依赖(微服务基…