ARM学习(25)链接装载高阶认识

news2024/11/25 14:29:50

ARM学习(25)链接装载高阶认识

1、例子引出

笔者先引入几个编译链接的例子来介绍一下:

  • 声明无效:declared implicitly?,属于编译错误还是链接错误?
    在这里插入图片描述
    编译阶段的错误,属于编译错误,因为编译器发现这个函数没有声明,声明异常

  • 标识符/符号找不到:xxxx is undefined? undefined xxxxx? 无法解析的外部符号?属于编译错误还是链接错误?
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    第一个是结构体找不到,属于编译阶段错误,相当于类型找不到。
    第二个和第三个属于链接错误,找不到对应的函数符号

  • 只编译不实现:会出现什么情况? 编译可以通过?链接可以通过?
    编译通过,链接不过,因为链接会关心函数的大小和实现。
    补充例子图。

  • 如何骗过编译器/链接器

    • 骗过编译器:让编译器认为我们写的代码是OK的, 可以编译通过。
    • 骗过链接器:让链接器认为我们代码正常,可以链接成完整的可执行文件,比如axf。
    • 通常来说,我们为了让代码运行,可以会编译链接出一个完整的axf,此时需要快速解决一些编译问题和链接问题,就需要让编译器和链接器认为我们代码是OK的,就需要快速适配,即“骗过”。
    • 比如编译到一个函数test_speed(),找不到对应的函数,test_speed,此时就需要声明一下函数,然后就可以编译过**(下图1)**。
    • 然后到链接的时候,发现找不到符号,Error: L6218E: Undefined symbol test_speed (referred from main.o)。(下图2)那么只有声明不行,需要定义一下,所以再加上一个空函数就行,此时就可以链接过。(下图3)
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
  • 编译链接学习的意义:

    • 帮助理解代码执行过程
    • 提升代码质量 (熟悉处理编译等警告)
    • 优化代码性能 (了解编译优化)
    • 更好的跨平台开发 (各个平台编译差异)
    • 更深入掌握调试技巧 (各自视图 调试不按行)
  • 主要有PE和ELF两种可执行文件格式
    在这里插入图片描述

2、编译链接

整体框图,1、预处理 2、编译 3、汇编 4、链接
由下图可以看到,

  • C文件经过预处理可以得到.i文件,编译选项-E
  • .i文件经过编译可以得到汇编文件,编译选项-S
  • .s文件经过汇编可以得到目标文件,
  • .o文件经过打包,可以形成静态库.a文件,也可以经过与库文件链接形成可执行文件,后缀为out或者axf。
    在这里插入图片描述

2.1 预处理器

预处理的主要内容有如下:

  • #define进行替换
  • 处理#if #ifdef等预编译指令
  • 展开#include
  • 删除 // /* */
  • 添加行号和文件名
  • 保留Progma指令 ……
    在这里插入图片描述
    string.h 文件展开
    在这里插入图片描述

2.2 编译

编译遵循的语法规则(个人总结):

  • 函数需要声明,不能重复声明
  • 变量、结构体不能重复定义
  • 变量函数定义需要封号结尾
  • 定义变量数组需要指明大小,不能为负数 宏与枚举不能重复声明
  • 宏需要多行,如果多行,需要\进行链接
  • 包含头文件的路径需要指明
  • 需要包含正确的头文件
  • 函数的声明和定义需要一致
  • If whilefor等关键字得正确使用
  • 注释的正确使用

2.3 链接

链接:将目标文件粘贴在一起,形成可执行文件。
按.o文件进行地址排序

  • Main fun -> Uart1Init fun Main fun -> UartPoll fun
  • 每个目标文件为一个section
  • 目标文件中首个函数地址均从0开始
  • 根据链接顺序,依次向后排
  • 向后排的大小按照目标文件所有函数的大小
  • 后面的符号地址确定后会在前面地址进行修正
    在这里插入图片描述
    按section进行地址排序(设置了分割section 属性,将每个函数进行section分割)
  • Main fun -> Uart1Init fun
  • Main fun -> UartPoll fun
  • 目标文件每个函数为一个section
  • 函数地址均从0开始
  • 根据链接顺序,依次向后排
  • 想后排的大小按照函数的大小
  • 后面的符号地址确定后会在前面地址进行修正

在这里插入图片描述

3、目标文件的认识

3.1 简介

目标文件:以.o或者.obj文件结尾,是可重定位文件(下图1中 REL(Relocatable file))。

  • 包括了代码和数据 (下图2)
  • 入口地址为0 (下图1)
  • 包括多个section/Segment (下图2)
  • Section中包含符号表/重定位表(下图3)
  • 可以被用来链接成可执行文件或者共享库文件
  • 遵循ELF文件格式
    在这里插入图片描述
    下图中有365个段,包括了bss以及data段以及重定位段等。
    在这里插入图片描述在这里插入图片描述
    Section: 链接视图中的段
    Segemnt:装载视图中的段,合并一定相同属性的段

由下图可以看到Section中定义的段,到了Segment里面,代码都合并成了一段。
比如ER_IROM1 、ER_REGION_HEADER、ER_IROM2 合并了,
这样的好处可以减少段零散,节省内存,同时加载相对简单,不需要每个section都去分散加载。
在这里插入图片描述

3.2 目标文件分析

目标文件分析:分割section
分割section的意思,按函数分割为一个段,
UART1Init:Section10,Size 208 Byte,重定位后的地址0x08004C5C(下图1),
UART1Poll:Section11,Size 176Byte,重定位后的地址0x08004D2C(下图1),恰好相差0xD0,也就是208Byte(下图2)。
结论:目标文件确定后,其大小则确定,即链接器按照地址和size依次向后排列,确定地址。
从下图4也可以看出,最终的可执行文件指令代码和目标文件形成的指令代码是一致的
在这里插入图片描述
图1
在这里插入图片描述
图2
在这里插入图片描述
图3
在这里插入图片描述
图4
目标文件分析:文件为section
Uart.o 为一个section,内部函数按顺序地址递增,然后文件之间进行地址排序
Uart.o wifi.o:地址0x08007E68 – 0x08009004(下图2),相差0x119C(4508个byte)(下图3),
结论:目标文件确定后,链接器按照文件地址和size依次向后排列,确定地址,同时size增大(44280 -> 62128)下图5。

图1
在这里插入图片描述
图2
在这里插入图片描述
图3
在这里插入图片描述
图4
在这里插入图片描述
图5

3.3 目标文件重定位

目标文件重定位表:记录着哪些位置的值链接器需要进行重定位
表结构:两个成员,一个offset,一个type

typedef struct rel_table_struct
{
      u32 offset;
      u32 type;
}rel_table_t;

可能是数据重定位,也可能是函数重定位

  • 下图1 可以看到是一个重定位表,第一个是函数重定位,其type类型是 R_THM_CALL,符号是DMA_Get_CurrDataCounter
  • 下图1中其他是数据,Type是R_ARMC_ABS32,
  • 图2 可以看到UART1Poll函数,其数据地址都是0,重定位后,图2可以看到都有了相应的地址。

在这里插入图片描述
图1
在这里插入图片描述
图2
在这里插入图片描述
图3

函数地址进行重定位

  • 目标文件中的BL指令F7FFFFFE,经过重定位后,变成F7DFFE94
  • BL的修改规则,是通过BL的ARM 指令编码表来计算的,如下图2。例如知道知道当前地址和编码后的指令代码,就可以知道跳转的地址(下图3和图4),当然如果知道当前地址以及跳转地址,可以推断出修订指令编码值。
  • 函数跳转地址实现如下面代码所示,根据ARM BL指令编码表,然后计算出S、J1和J2,imm1和imm2,最后再组装在一起,形成最后的值。
    在这里插入图片描述
    图1
    在这里插入图片描述
    图2
    在这里插入图片描述
    图3
    在这里插入图片描述
    图4
        int it,pc,offset = 0;
        printf("please input Intruction:\r\n");
        scanf("%x", &it);
        printf("please input pc:\r\n");
        scanf("%x", &pc);

        int S = (it & 0x04000000) >> 26;
        int J1 = (it & 0x00002000) >> 13;
        int J2 = (it & 0x00000800) >> 11;

        int I1 = (~(J1 ^ S))&0x1;
        int I2 = (~(J2 ^ S))&0x1;

        int imm10 = (it & 0x03FF0000) >> 16;
        int imm11 = (it & 0x000007FF);
        if(S == 1)
        {
            offset = 0xFF000000;
        }
        offset |= (S<<24);

        offset |= (I1<<23);
        offset |= (I2<<22);

        offset |= (imm10<<12);
        offset |= (imm11<<1);

        printf("jump addr=0x%x\r\n",(offset + pc + 4));

4、静态链接

4.1 空间地址分配

在链接的时候,如果形成图1的这种可执行文件,那么加载的时候,有一些劣势。
简单地址分配:

  • 空间浪费
  • 不利于管理
  • 不利于加载
    在这里插入图片描述
    在这里插入图片描述

4.2 强弱符号和修饰

  • 强符号、弱符号与符号修饰:__weak 或者 attribute((weak))
    符号:函数和变量,链接器接口
    符号名:函数名和变量名
    强符号:只允许存在一个
    弱符号:允许存在多个(weak修饰)
    符号修饰:符号名根据特定规则进行修改
    extern “C”:将函数名按照C语言中生成函数名的方式去生成。

对于弱符号,如果只定义不实现,可以编译过?链接过?能执行吗?
在这里插入图片描述
可以看到能编译过,同样可以链接过,但是执行报错,地址为空,可能无法访问。

所以GCC编译器:

  • 若符号即使没定义,也可以链接
  • 有符号名
  • 符号地址为空,允许出错
    在这里插入图片描述
    在这里插入图片描述

对于ARMCC编译器:

  • 若符号即使没定义,也可以链接
  • 没有符号名
  • 函数引用指令链接成nop,可正常运行

再来说说修饰规则C++调用C:

  • GCC:不作任何操作
  • VC编译器:符号前面加”_”下划线。
  • 不声明extern “C”情况:按照C++的函数命名去修饰
    在这里插入图片描述
    在这里插入图片描述
    修饰规则C++:
  • GCC:N或者_N开头,………
  • VC编译器:??或者?开头 ,…….
    在这里插入图片描述在这里插入图片描述

4.3 链接与ABI接口

链接与ABI接口:Application Binary Interface,应用程序二进制接口

  • API与ABI:前者为源码级别的接口(如POSIX), 后者来二进制级别的接口(各大编译器无法兼容的原因,就是ABI不同,比如GCC和VC编译器,C++标准都一样,但是ABI不同,导致无法互相调用)。

  • 影响ABI的因素:C角度

    • 基本类型大小以及存储方式(大小端)
    • 符号修饰方面 函数调用方式(入栈/返回值)
    • 寄存器使用约定等
    • 堆栈分布方式
  • 影响ABI的因素:C++角度

    • 继承类体系分布
    • 指向成员指针内存分布
    • 虚函数的调用
    • 模板类的实例化
    • 外部符号的修饰
    • 全局对象的构造和析构
    • 异常产生和捕获机制

4.4 链接过程控制与脚本语法

链接过程控制其实就是链接脚本来控制链接的过程,比如将数据分配到链接脚本指定的段。

加载视图:加载期间,代码和数据的分布情况
运行视图:运行期间,代码和数据的分布情况

存储地址:代码数据存放的位置
加载地址:代码数据加载到内存中(执行代码)的地址
执行地址:代码数据真正执行的地址

来看一个加载过程,具体可参考【Bootloader学习理解学习–加强版】。
在这里插入图片描述
链接脚本语法,如下面两张图所示。
在这里插入图片描述

在这里插入图片描述

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

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

相关文章

《量子计算:下一个大风口,还是一个热炒概念?》

引言 量子计算,作为一项颠覆性的技术,一直以来备受关注。它被认为是未来计算领域的一次革命,可能改变我们对计算能力和数据处理的理解。然而,随着技术的不断进步和商业应用的探索,人们开始思考,量子计算到底是一个即将到来的大风口,还是一个被过度炒作的概念? 量子计…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的夜间车辆检测系统(深度学习代码+UI界面+训练数据集)

摘要&#xff1a;开发夜间车辆检测系统对于自动驾驶技术具有关键作用。本篇博客详细介绍了如何运用深度学习构建一个夜间车辆检测系统&#xff0c;并提供了完整的实现代码。该系统基于强大的YOLOv8算法&#xff0c;并对比了YOLOv7、YOLOv6、YOLOv5&#xff0c;展示了不同模型间…

Hadoop学习1:概述、单体搭建、伪分布式搭建

文章目录 概述基础知识Hadoop组件构成Hadoop配置文件 环境准备配置Hadoop配置下载配置环境变量 Hadoop运行模式Standalone Operation&#xff08;本地&#xff09;官方DemoWordCount单词统计Demo Pseudo-Distributed Operation&#xff08;伪分布式模式&#xff09;配置修改启动…

vscode使用remote-ssh免密连接服务器

你还在使用XShell、Hyper、FinalShell等等SSH客户端软件吗&#xff0c;作为前端的我们&#xff0c;一直在用的功能强大的开发工具vscode&#xff0c;早已实现SSH连接功能&#xff08;借助官方提供的插件&#xff09;。而且更加好用&#xff0c;可以直接打开服务器上的文件&…

使用npm版本管理工具解决npm 的EACCES permissions errors when installing packages globally错误

EACCES错误通常表示“权限被拒绝”&#xff0c;意味着您没有足够的权限来执行某个操作。在计算机领域&#xff0c;尤其是在文件系统和程序安装中&#xff0c;这个错误很常见。以下是可能导致EACCES错误的原因以及相应的解决方法&#xff1a; 文件系统权限&#xff1a;当您尝试…

❤️算法笔记❤️-(每日一刷-141、环形链表)

文章目录 题目思路解法 题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接…

【调参】如何为神经网络选择最合适的学习率lr-LRFinder-for-Keras

【调参】如何为神经网络选择最合适的学习率lr-LRFinder-for-Keras_学习率选择-CSDN博客文章浏览阅读9.2k次&#xff0c;点赞6次&#xff0c;收藏55次。keras 版本的LRFinder&#xff0c;借鉴 fast.ai Deep Learning course。前言学习率lr在神经网络中是最难调的全局参数&#x…

YOLOv9改进 添加可变形注意力机制DAttention

一、Deformable Attention Transformer论文 论文地址:arxiv.org/pdf/2201.00520.pdf 二、Deformable Attention Transformer注意力结构 Deformable Attention Transformer包含可变形注意力机制,允许模型根据输入的内容动态调整注意力权重。在传统的Transformer中,注意力是…

Qt 如何搭建lua的运行环境

一、lua简介 Lua 是一种强大的、高效的、轻量级的、可嵌入的脚本语言。它支持过程&#xff08;procedural&#xff09;编程、面向对象编程、函数式编程以及数据描述。Lua 是动态类型的&#xff0c;运行速度快&#xff0c;支持自动内存管理&#xff0c;因此被广泛用于配置、脚本…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Select)

提供下拉选择菜单&#xff0c;可以让用户在多个选项之间选择。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 Select(options: Array<SelectOption>) 参数&#xff1a;…

HUAWEI 华为交换机 配置 MAC 地址漂移检测示例

组网需求 如 图 2-17 所示&#xff0c;网络中两台 LSW 间网线误接形成了网络环路&#xff0c;引起 MAC 地址发生漂 移、MAC 地址表震荡。 为了能够及时检测网络中出现的环路&#xff0c;可以在 Switch 上配置 MAC 地址漂移检测功能&#xff0c; 通过检测是否发生MAC 地址漂移…

网络学习:BGP路径属性分类

目录 前言&#xff1a; 路径属性分类 公认必遵 公认任意 可选过渡 可选非过渡 前言&#xff1a; 在默认情况下&#xff0c;到达同一目的地&#xff0c;BGP只走单条路径&#xff0c;并不会在多条路径之间执行负载均衡。对于IGP路由协议&#xff0c;当有多条路径可以到达同…

丘一丘正则表达式

正则表达式(regular expression,regex,RE) 正则表达式是一种用来简洁表达一组字符串的表达式正则表达式是一种通用的字符串表达框架正则表达式是一种针对字符串表达“简洁”和“特征”思想的工具正则表达式可以用来判断某字符串的特征归属 正则表达式常用操作符 操作符说明实…

[vscode]将命令行参数传递给调试目标

一、简介 本文介绍了在vscode中使用cmake工具时&#xff0c;如何传递参数给编译目标的方法。 前提&#xff1a;使用vscodecmake编译C/C程序。 二、方法 在.vscode/目录下新建settings.json文件&#xff0c;并将待传底的参数写在 cmake.debugConfig里。 下面介绍了一个示例&a…

DAY14二叉树迭代遍历

二叉树前序迭代法遍历 前序遍历是中左右&#xff0c;每次先处理的是中间节点&#xff0c;那么先将根节点放入栈中&#xff0c;然后将右孩子加入栈&#xff0c;再加入左孩子。 为什么要先加入 右孩子&#xff0c;再加入左孩子呢&#xff1f; 因为这样出栈的时候才是中左右的顺…

Stable Diffusion 如何写好提示词(Prompt)

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里。 大家好&#xff0c;我是水滴~~ 本文深入探讨了如何撰写出优质的提示词&#xff0c;内容涵盖多个维度&#xff1a;提示词的多样化分类、模型应用中的经典提示词案例、提供丰富资源的提示词参考…

基于STM32的智慧农业管理系统设计与实现

文章目录 一、前言1.1 项目介绍【1】项目功能【2】设计实现的功能【3】项目硬件模块组成 1.2 设计思路1.3 传感器功能介绍1.4 开发工具的选择 二、EMQX开源MQTT服务器框架三、购买ECS云服务器3.1 登录官网3.2 购买ECS服务器3.3 配置安全组3.4 安装FinalShell3.5 远程登录到云服…

Java代码审计工程师直播第六期

本期直播课程将深入探讨Java代码审计的关键概念和技术。涵盖课题包括安全漏洞分析、代码审查方法、常见漏洞案例分析等。学员将通过实例掌握代码审计实战技能&#xff0c;提升对Java应用程序安全的认知和技能水平。 课程大小&#xff1a;6.1G 课程下载&#xff1a;https://do…

Docker 容器化技术:构建高效、可移植的开发环境和部署流程|Docker 三要素

镜像、容器、镜像仓库是 Docker 中最核心的三个概念&#xff0c;组成了 Docker 的整个生命周期。 &#xff08;镜像、容器、镜像仓库三者运行关系&#xff09; 1、镜像 镜像是 Docker 的核心元素质疑&#xff0c;作为容器运行的基础&#xff0c;Docker Hub 官网提供了庞大的镜…

第十五届蓝桥杯(Web 应用开发)模拟赛 3 期-大学组(被题目描述坑惨了)

目录 1.创意广告牌 2.原子化css 3.神秘咒语 4.朋友圈 5.美食蛋白揭秘 6.营业状态变更 7.小说阅读器 8.冰岛人 9.这是一个”浏览器“ 10.趣味加密解密 总结 1.创意广告牌 这个题目不多说了&#xff0c;只要知道这些css应该都能写出来&#xff0c;不会的平时多查查文…