Linux中gcc的详解用法及其可重定位目标文件

2023-10-31

  

1.gcc组成

gcc是一组编译工具的总称,包括:C编译器、C++编译器、源码预处理程序和库文件。

2.gcc编译

1.生成一个程序

 

 gcc hello.c -o hello 把hello.c编译成一个可执行程序

如果gcc hello.c 不指定输出名,生成一个a.out文件。

可以通过./hello或者./a.out来运行程序

2.gcc编译步骤(包括预处理preprocessor,编译compiler,汇编assemble,链接linker)

预处理gcc -E hello.c -o hello.i      完成对代码的预处理

处理源文件中以“#”开头的预编译指令,包括:

– 处理所有条件预编译指令,如“#if”,“#ifdef”, “#endif”等

– 插入头文件到“#include”处,可以递归方式进行处理

– 删除所有的注释“//”和“/* */”

– 添加行号和文件名标识,以便编译时编译器产生调试用的行号信息

– 保留所有#pragma编译指令(编译器需要用)

• 经过预编译处理后,得到的是预处理文件(如,hello.i) ,

它还是一个可读的文本文件 ,但不包含任何宏定义

编译: gcc -S hello.i -o hello.s      gcc –S hello.c –o hello.s 将源代码编译成汇编代码
• 编译过程就是将预处理后得到的预处理文件(如 hello.i)

进行 词法分析、语法分析、语义分析、优化后,生成汇编代码文

• 经过编译后,得到的汇编代码文件(如 hello.s)还是可读的文 本文件,CPU无法理解和执行它

gcc命令实际上是具体程序(如ccp、cc1、as等)的包装命令,

用户通过gcc命令来使用具体的预处理程序ccp、编译程序cc1和汇编程序as等

汇编gcc -c hello.s -o hello.o ,  gcc -c hello.c -o hello.o ,  as hello.s -o hello.o

(as是一个汇编程序)将汇编代码转换成可重定位目标文件(二进制)

• 汇编程序(汇编器)用来将汇编语言源程序转换为机器指令序列 (机器语言程序

• 汇编指令和机器指令一一对应,前者是后者的符号表示,它们都 属于机器级指令

所构成的程序称为机器级代码

• 汇编结果是一个可重定位目标文件(如,hello.o),其中包含 的是

不可读的二进制代码,必须用相应的工具软件来查看其内容

链接gcc hello.o -o hello   

将目标代码和所需要的库链成一个完整的应用程序。

gcc的结果输出是后缀名不相关的,只与输入参数有关

为了构造可执行文件,连接器必须完成两个主要任务:

符号解析(symbol resolution),重定位(relocation)

 

目标文件有三种形式:

可重定位目标文件。包含二进制代码和数据,其形式可以在编译时与其他可重定位

目标文件合并起来,创建一个可执行目标文件。

可执行目标文件。包括二进制代码和数据,其形式可以被直接复制到内存并执行。

共享目标文件。一种特殊类型的可重定位目标文件,可以在加载或者运行

时被动态的加载进内存并链接。

3.使用多个源码的项目

比如有两个源程序main.c和sum.c                 

使用gcc编译器并链接生成可执行程序p:gcc -O2 -g -o p main.c sum.c     ./p

-O2:2级优化               

-g:生成调试信息

-o:目标文件名

链接过程的本质(以main.c和swap.c为例)

main.c                          swap.c

int buf[2] = {1, 2};                extern int buf[];

void swap();                       int *bufp0 = &buf[0];

int main()                           static int *bufp1;

{ swap();                            void swap() {

return 0; }                          int temp;bufp1 = &buf[1];temp = *bufp0;*bufp0 = *bufp1;*bufp1 = temp; }

 

关于ELF格式文件符号表解析及readelf命令使用

1.读取ELF文件头   readelf -h main.c

 

在 readelf 的输出中:
第 1 行,ELF Header: 指名 ELF 文件头开始。
第 2 行,Magic 魔数,用来指名该文件是一个 ELF 目标文件。第一个字节 7F 是个固定的数;后面的 3 个字节正是 E, L, F 三个字母的 ASCII 形式。
第 3 行,CLASS 表示文件类型,这里是 64位的 ELF 格式。
第 4 行,Data 表示文件中的数据是按照什么格式组织(大端或小端)的,不同处理器平台数据组织格式可能就不同,如x86平台为小端存储格式。
第 5 行,当前 ELF 文件头版本号,这里版本号为 1 。
第 6 行,OS/ABI ,指出操作系统类型,ABI 是 Application Binary Interface 的缩写。
第 7 行,ABI 版本号,当前为 0 。
第 8 行,Type 表示文件类型。ELF 文件有 3 种类型,一种是如上所示的 Relocatable file 可重定位目标文件,一种是可执行文件(Executable),另外一种是共享库(Shared Library) 。
第 9 行,机器平台类型。
第 10 行,当前目标文件的版本号。
第 11 行,程序的虚拟地址入口点。
第 12 行,与 11 行同理,这个目标文件没有 Program Headers。
第 13 行,sections 头开始处,这里 856 是十进制.。
第 14 行,是一个与处理器相关联的标志。
第 15 行,ELF 文件头的字节数。
第 16 行,因为这个不是可执行程序,故此处大小为 0。
第 17 行,同理于第 16 行。
第 18 行,sections header 的大小,这里每个 section 头大小为 64个字节。
第 19 行,一共有多少个 section 头,这里是 13个。
第 20 行,section 头字符串表索引号,从 Section Headers 输出部分可以看到其内容的偏移在 0xa0 处,从此处开始到0xcf 结束保存着各个 sections 的名字,如 .data,.text,.bss等。

2.读取节头表    readelf -S main.o

可重定位目标文件

 

                           ELF头
                          .text
                           .rodata
                            .data
                             .bss
                             .symtab
                              .rel.text
                              .rel.data
                              .debug
                               .line
                               .strtab

                                  节头部表

 

ELF 头

包括16字节标识信息、文件类型 (.o, exec, .so)、机器类型(如 IA-32)、 节头表的偏移、节头表的表项大小以及 表项个数 .text 节 编译后的代码部分

.rodata 节 只读数据,如 printf 格式串、switch 跳转表

.data 节 已初始化的全局变量

.bss 节 未初始化全局变量,仅是占位符,不占 据任何实际磁盘空间。区分初始化和非 初始化是为了空间效率

.symtab 节 存放函数和全局变量 (符号表)信息 , 它不包括局部变量

.rel.text 节 .text节的重定位信息,用于重新修改代 码段的指令中的地址信息

.rel.data 节 .data节的重定位信息,用于对被模块使 用或定义的全局变量进行重定位的信息

.debug 节 调试用符号表 (gcc -g)

strtab 节 包含symtab和debug节中符号及节名

Section header table(节头表) 每个节的节名、偏移和大小

以下以main.o为例子解释

 

Key to Flags:

W (write), A (alloc), X (execute), M (merge), S (strings)

I (info), L (link order), G (group), x (unknown)

O (extra OS processing required) o (OS specific), p (processor specific) 

可重定位目标文件中,每个可装入节的起始地址总是0

 

3.符号表机制    readelf -s main.o

可以看出全局变量,静态全局变量,静态局部变量,

全局函数名都会出现在符号表中,而局部变量不会被保存在符号表中。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux中gcc的详解用法及其可重定位目标文件 的相关文章

  • SpringBoot 更优雅的实现接口操作日志获取

    SpringBoot 更优雅的实现接口操作日志获取 使用注解 延迟到子类处理的方式实现解耦合 1 定义 Oplog 系统操作日志注解 author chenyusheng create 2023 6 28 9 56 description
  • 字符串系列题目(C++)

    参考链接 https leetcode cn com problems fan zhuan dan ci shun xu lcof solution yi ge mo ban shua bian suo you zi fu chu x6vh
  • 五大常用算法之二:动态规划算法

    一 基本概念 动态规划过程是 每次决策依赖于当前状态 又随即引起状态的转移 一个决策序列就是在变化的状态中产生出来的 所以 这种多阶段最优化决策解决问题的过程就称为动态规划 二 基本思想与策略 基本思想与分治法类似 也是将待求解的问题分解为
  • Spring基础入门2 - IoC容器

    在上文介绍了Spring IoC容器的基本用法 下面做一个稍微复杂点的例子 继续学习Spring的IoC和AoP的 实际 使用 我们假设有一个在线商城OnlineStore 在线商城需要提供一些银行相关服务 假设 因为银行服务的实现是容易变
  • Qt 基于元对象系统实现反射

    背景 C 不支持反射 但使用 Qt 的元对象系统可以实现反射机制 使用反射创建对象 概述 使用 Qt 的元对象系统 可以使用反射来创建对象 需要把构造函数用 Q INVOKABLE 进行标记 类定义示例 class demoClass pu
  • 微信小程序 全局路由拦截

    1 微信小程序 全局路由拦截 utils filter js function loginCheck pageObj if pageObj onLoad let onLoad pageObj onLoad 使用onLoad的话需要传递opt
  • synchronized关键字(一)

    一 线程安全和不安全 非线程安全 在多个线程对同一个对象的实例变量进行并发访问时会出现值被更改 值不同步的情况 线程安全 获得的实例变量的值是经过同步处理的 按照顺序执行 不会出现脏读情况 举个例子 5个销售员 卖同一堆货物 每个销售员在卖
  • 高可用mysql集群搭建(mysql5.6+keepalived)

    1 方案在一定程度上保证主库的高可用 在一台主库down掉之后 可以在极短的时间内切换到另一台从库上 尽可能减少主库宕机对业务造成的影响 1 一台主库 master 提供服务 只负责数据的写入 2 一台数据库服务器资源做master主库的从
  • python脚本寻找Java文件方法

    统计 Java 文件中方法行数 最近接到一个需求 有一个安全扫描 可以扫描到是那个Java文件有问题 但是不知道是该文件下哪个方法有问题 所以想根据行号找到对应方法进行统计 本文将介绍如何使用 Python javalang 库实现这个功能
  • java自动化测试

    Java是一种强大的编程语言 也可以用于自动化测试 以下是使用Java进行自动化测试的一般步骤 确保您已安装Java开发环境 JDK 在开始之前 请先安装适合您系统的Java JDK 并设置正确的环境变量 选择自动化测试框架 Java有多个
  • 新手入门:Python和C语言哪个更难?零基础学哪个好?

    Python和C语言哪个难 零基础学哪个好 六月编程语言排行榜 Python直追C语言龙头老大 预计将会有可能超过C语言成为下次的编程语言排行榜第一 于是乎 很多同学又听说Python简单易学 不由心动 那么 我们零基础入门编程的话 选择哪
  • myeclipse破解文件破解后只能使用5天的解决方法

    我装myeclipse真的是已经一个多月了 之前那个 舍友的按照那个方法破解就可以了 但是我的不行 期间重装了好几十次 今天终于不懒 找到了解决的方法 1 按照的方法还是一样的 有finish那一步 取消勾选绿色大 下的勾选项 完成安装后不
  • 2023第十四届蓝桥杯Python大学生B组真题?(真题+附链接)

    第十四届蓝桥杯大赛软件赛省赛 Python 大学 B 组 试题 A 2023 本题总分 5 分 问题描述 请求出在 12345678 至 98765432 中 有多少个数中完全不包含 2023 完全不包含 2023 是指无论将这个数的哪些数
  • 什么是多态,如何在Java中实现多态?

    欢迎来到多态的世界 在这里 我们将探讨Java中的多态性 以及如何让你的代码实现多态性 让我们先从一个有趣的故事开始吧 有一天 一只小猪走进了一家餐馆 它点了一份 红烧猪肉 但是 当它拿到盘子时 却发现上面只有一块 猪肉 而且还是生的 小猪
  • 【毕业设计项目】基于单片机的手势识别设计与实现 - 物联网 嵌入式 stm32 c51

    文章目录 1 简介 2 实现效果 3 使用场景 4 参数说明 5 注意事项 6 最后 1 简介 Hi 大家好 这里是丹成学长 今天向大家介绍一个学长做的单片机项目 基于单片机得手势识别系统 大家可用于 课程设计 或 毕业设计 单片机 嵌入式
  • RFID智能仓储温湿度自动监测系统,物联网+RFID仓库管理-新导智能

    一 RFID仓库温湿度自动监测管理系统 1 1 RFID智能仓库管理系统简介 苏州新导药品储运温湿度监测系统由管理主机 测点终端 运行软件等组成 通过主服务器实时显示和监测各监测点的温湿度状况 自动记录温湿度实际数值 实现药品储存 运输温湿
  • 微信小程序分享功能(uniapp、uView)

    微信小程序分享功能 uniapp uView 该对象已集成到this u中 内部属性如下 uni u mpShare 默认为小程序名称 可自定义 title 分享的标题 默认为当前页面路径 一般无需修改 QQ小程序不支持 path page
  • 回归分析中,证明:总离差平方和=回归平方和+误差平方和。

    证明 总离差平方和 回归平方和 误差平方和 S S T S S R S S E S S T S S R S S E SST
  • java内存结构

    一 Java内存分配 1 Java有几种存储区域 寄存器 在CPU内部 开发人员不能通过代码来控制寄存器的分配 由编译器来管理 栈 在Windows下 栈是向低地址扩展的数据结构 是一块连续的内存的区域 即栈顶的地址和栈的最大容量是系统预先
  • Alpine Docker 安装 bash

    Alpine Linux是一个轻型Linux发行版 它不同于通常的Linux发行版 Alpine采用了musl libc 和 BusyBox以减少系统的体积和运行时的资源消耗 Alpine Linux提供了自己的包管理工具 apk 我们可以

随机推荐