S32K3 工具篇9:如何在无源码情况下灵活调试elf文件

news2025/6/3 0:56:32

S32K3 工具篇9:如何在无源码情况下灵活调试elf文件

  • 一,文档简介
  • 二, 功能实现
    • 2.1 代码工具准备
    • 2.2 elf修改功能实现:Fun2功能跳过
      • 2.2.1 PC越过Fun2
      • 2.2.2 Fun2替换为nop
    • 2.3 elf修改功能实现:Fun4替换Fun2入口
      • 2.3.1 linkfile修改
      • 2.3.2 Fun4函数体
      • 2.3.3分离Fun4函数体到独立文件
      • 2.3.4合并Fun4函数体到原始elf对应的srec
      • 2.3.5修改main中原始Fun2调用为Fun4调用
    • 2.3 劳德巴赫同步加载原始elf符号表
  • 三,知识点

一,文档简介

平时在支持客户的时候,常会遇到客户因为公司政策的原因,无法提供问题工程源码,最多提供问题工程的elf文件,这里就涉及到,如何利用好elf来达到灵活的调试目的。Elf文件是一种二进制文件格式,包含程序的代码、数据、符号表,段表等信息。
调试elf的时候,无法看到源码,但是能看到函数的名称对应汇编地址的情况,这个时候调试还是相对能知道位置。但是,问题是无法像修改源码一样随意修改生成新的文件。那么对于elf文件,如果想特定做一些修改,来达到具体功能测试的目的是否可行?
本文将会在一个现有elf上,对特定位置予以跳过,抹掉功能,替换代码,拼接函数等给出测试方法,实现elf某些位置代码的跳过,或者任何时候都不执行,另起一段区域拼接其他功能性函数,再修改原有调用位置强行插入测试函数代码。
本文测试平台为S32K344, RTD500。
在这里插入图片描述

图1

二, 功能实现

图1是一个原本功能的elf文件,功能是启动之后,调用三个函数:Fun1, Fun2, Fun3.

  • Fun1 : 闪烁红灯
  • Fun2 : 闪烁绿灯
  • Fun3 : 闪烁蓝灯

本文需要实现的功能主要分为如下几点:
(1)Fun2功能跳过
这里功能跳过分为两种情况:
• debug的时候从Fun2开始的地方,修改PC跳到Fun3
• 上电都不运行Fun2,直接抹掉main里面Fun2值为nop
在这里插入图片描述
在这里插入图片描述

图2

(2)添加Fun4,修改Main中Fun2调用为跳转Fun4
这里涉及到插入Fun4代码到空flash地址,修改Fun2的跳转代码数据位跳转Fun4.
在这里插入图片描述

图 3

2.1 代码工具准备

硬件平台:S32K344-EVB
软件:RTD500, S32DS3.5, JFLASH, PE Multilink(EVB自带),lauterbach
JFLASH下载连接:https://www.segger.com/downloads/jlink/
新建一个简单的点灯工程,可以基于RTD原有的Siul2_Dio_Ip_Example_S32K344工程,添加三个led灯引脚,并且构造3个分别红灯闪烁,绿灯闪烁,蓝灯闪烁的函数,main中顺序调用3个函数。测试能够工作的情况下,生成elf备用。

2.2 elf修改功能实现:Fun2功能跳过

这里给出具体实现图2具体方法:PC越过Fun2以及Fun2替换为nop

2.2.1 PC越过Fun2

在Main函数里面,调用Fun2的汇编位置打断点,直接修改PC为Fun3+1的值,然后运行,即可跳过Fun2. 图4中可以看到已经运行到Fun2,但是还没有进入到Fun2的函数体,直接将原本要调用Func2函数体的指针入口改成0X4027B4+1,也就是Fun3的函数入口。可以看到,修改PC之后回车,单步,即可进入到Fun3的函数体。如果在Fun2的函数体里面开始地方修改PC跳转到Fun3,会从main功能整体上运行两边Fun3.
在这里插入图片描述

图4

在这里插入图片描述

图5

2.2.2 Fun2替换为nop

上面直接使用debug跳转PC的情况越过Fun2,虽然测试上是可以跳过Fun2,但是要注意在下载代码的时候,实际上是会运行一下代码再进入到debug。如果有些测试就希望从POR开始,就不曾运行Fun2,那么就需要把原始的elf的Fun2调用的位置代码直接抹去,常用的方法可以替换为人畜无害的nop指令。nop指令的汇编十六制值为:00 BF,如下图:

在这里插入图片描述

图6

有了目标修改值,下面就是找到elf main中Fun2的调用地址,将对应的4个字节替换为00BF00BF。
从原始的elf文件可以看到,调用Fun2的数据位置为绝对地址0X0040280E开始的4个字节,使用Segger JLINK驱动中的JFLASH工具打开原始elf文件,修改0X0040280E开始的4个字节数据为00BF00BF,修改后另存为srec文件,然后在临时工程中调用srec文件去运行修改后的代码。
下图是修改过程

在这里插入图片描述

图 7

修改后debug的结果如下:
在这里插入图片描述

图 8

可以看到,原本0X40280e区域的跳转到Fun2的汇编已经变成了nop指令。
这个时候,全速运行将会直接忽略fun2,顺序运行下去。不论debug,还是上电之后,从整体运行时序上彻底抹去了Fun2的调用。
当然,由于手动修改后的elf通过JFLASH另存的时候,不能直接存为elf文件,所以选择存为srec文件,再次debug的时候,就会丢掉了符号表了,需要前期elf的时候,大概记住几个需要用的函数的绝对地址。

2.3 elf修改功能实现:Fun4替换Fun2入口

上面是在Fun2的位置直接跳过或者插入nop,那么是否可以在原始的Fun2插入调用另外的函数体用于测试,达到移花接木的效果?是可以的。这里也分为两种:一种是破坏原始Fun2函数体位置,直接替换函数体代码内容,当然这点受到原始Fun2函数体大小的限制。另一种,保留原始Fun2以备后用,可以在flash其他空白的地址,另起一个函数Fun4,再将main中调用Fun2的代码改为调用Fun4即可实现Fun1->Fun4->Fun3运行的无缝对接。
本文主要使用在空白的特定绝对地址新建一个函数,当然要注意原始的elf map情况,保证空白的区域足够使用,这种新建函数最好是自成一体,不依赖于其他的函数的独立体,以免引起调用上的偏差,如果一定要调用其他函数体,那么需要在构建这个新函数的时候,把其他依赖的函数在样本工程中,地址设定为一致。
这里,我们新建一个S32DS工程,在linkfile里面划分一块flash区域,用于存放新建的Fun4,Fun4功能是实现红绿灯的交替闪烁。

2.3.1 linkfile修改

MEMORY
{
    int_pflash              : ORIGIN = 0x00400000, LENGTH = 0x00010000    /* 4096KB - 176KB (sBAF + HSE)*/
    int_pflash_user         : ORIGIN = 0x00410000, LENGTH = 0x003C4000
    int_dflash              : ORIGIN = 0x10000000, LENGTH = 0x00020000    /* 128KB */
    int_itcm                : ORIGIN = 0x00000000, LENGTH = 0x00010000    /* 64KB */
    int_dtcm                : ORIGIN = 0x20000000, LENGTH = 0x0001F000    /* 124KB */
    int_stack_dtcm          : ORIGIN = 0x2001F000, LENGTH = 0x00001000    /* 4KB */
    int_sram                : ORIGIN = 0x20400000, LENGTH = 0x0002FF00    /* 184KB, needs to include int_sram_fls_rsv */
    int_sram_fls_rsv        : ORIGIN = 0x2042FF00, LENGTH = 0x00000100
    int_sram_no_cacheable   : ORIGIN = 0x20430000, LENGTH = 0x0000FF00    /* 64KB, needs to include int_sram_results  */
    int_sram_results        : ORIGIN = 0x2043FF00, LENGTH = 0x00000100
    int_sram_shareable      : ORIGIN = 0x20440000, LENGTH = 0x00004000    /* 16KB */
    ram_rsvd2               : ORIGIN = 0x20444000, LENGTH = 0             /* End of SRAM */
}

SECTIONS
{
	.FUNC4 :
	{
		*(.func4)
	} > int_pflash_user
…
}

2.3.2 Fun4函数体

函数体代码构建如下,纯逻辑,不依赖任何外在其他函数,变量。

__attribute__((section (".func4"))) void Func4(void)
{
	uint8 count1 = 0U;
	 static volatile uint32 DelayTimer = 0;
	 volatile uint8 *red_addr_byte = (volatile uint8 *)0x4029131e;
	 volatile uint8 *green_addr_byte = (volatile uint8 *)0x4029131d;
	 volatile uint8 *blue_addr_byte = (volatile uint8 *)0x4029131c;
              //RED: GPIO29, 0x4029131e
 	//green: GPIO30, 0x4029131d
 	//blue: GPIO31, 0x4029131c
    while (count1++ < 6)
    {

    	*red_addr_byte = 1;
    	*green_addr_byte = 0;
        while(DelayTimer < 4800000)
        {
            DelayTimer++;
        }
        DelayTimer = 0;

    	*red_addr_byte = 0;
    	*green_addr_byte = 1;
        while(DelayTimer < 4800000)
        {
            DelayTimer++;
        }
        DelayTimer = 0;
    	/*
        Siul2_Dio_Ip_WritePin(LED_RED_PORT, LED_RED_PIN, 1U);
        Siul2_Dio_Ip_WritePin(LED_GREEN_PORT, LED_GREEN_PIN, 0U);
        while(DelayTimer < 4800000)
        {
            DelayTimer++;
        }
        DelayTimer = 0;

        Siul2_Dio_Ip_WritePin(LED_RED_PORT, LED_RED_PIN, 0U);
        Siul2_Dio_Ip_WritePin(LED_GREEN_PORT, LED_GREEN_PIN, 1U);
        while(DelayTimer < 4800000)
        {
            DelayTimer++;
        }
        DelayTimer = 0;
        */
    }
	*red_addr_byte = 0;
	*green_addr_byte = 0;
}

这里知道,这个Fun4地址是从flash 0x00410000开始的,在带有上面函数的工程编译之后,生成elf。

2.3.3分离Fun4函数体到独立文件

使用JFLASH打开带有新建Fun4的elf文件,删去0x00410000以上的所有代码,方法:
JFLASH->Edit->Delete range:
在这里插入图片描述

图9

删除之后得到一个只有Fun4代码的文件,另存为ElfdebugSource_S32K344_RTD500_delete.srec文件。

2.3.4合并Fun4函数体到原始elf对应的srec

使用JFLASH打开原始elf对应的srec,以及刚才分离出来的Fun4 srec文件。选择合并两个文件,会自动把不同地址的Fun4拼接到原始srec文件中去,另存文件。
在这里插入图片描述

图10

在这里插入图片描述

图11

2.3.5修改main中原始Fun2调用为Fun4调用

直接上图,就是将原本的:
0040280e: ff f7 ab ff bl 0x402768 < Func2 >
修改为:
0040280e: 0d f0 f7 fb bl 0x410000
其中,0x410000就是Fun4的绝对地址。
在这里插入图片描述

图12

将已经添加了Fun4的srec文件修改0040280e开始的值为0d f0 f7 fb ,再另存为新的srec,并且debug,可以发现main的运行顺序已经变成了:Fun1->Fun4->Fun3.
跳过了Fun2的同时还运行了新接入的Fun4.
上图是在S32DS+PE Multilink的环境下运行的,这个时候因为运行的srec文件,所以已经不带有elf的符号表信息了,但是功能都是成功的。

2.3 劳德巴赫同步加载原始elf符号表

由于原始elf经过一系列的嫁接修改保存为了srec,丢失了符号表。那么如果还想查看未修改区域的符号表,可以借助于劳德巴赫工具,在attach了代码之后,可以通过如下在trace32中命令加载原始的elf文件:

Data.LOAD.Elf C:\S32DS35_RTD500\elfdebug\elftest\Debug_FLASH\Elfdebug_S32K344_RTD500.elf /nocode

可以看到,原始带有Fun2符号表的地方,只是因为被修改了,所以缺失了符号表,但是其他的调用头符号表还是存在的。这点也是便于代码的运行读取。
在这里插入图片描述

图13

在这里插入图片描述

图14

在这里插入图片描述

图15

三,知识点

这里分享关于BL addr跳转的对应16进制的数据运算情况。前面是直接使用S32DS生成的
0040280e: 0d f0 f7 fb bl 0x410000
可以知道,对于bl 0x410000指令对应的值是0d f0 f7 fb.
那么这个:0d f0 f7 fb值是怎么算出来的呢?
这点需要参考ARM的架构文档:DDI0403E_d_armv7m_arm.pdf
对于BL跳转thumb2指令对应的情况:
在这里插入图片描述

图 16

对于BL,是一个长跳转,实际上是由两条跳转指令组成的。Thumb
指令都是2个字节,BL是两条跳转指令组成了4个字节。
0-11位表示11位地址,具体含义如下:
第11位为0,代表偏移高位
第11位为1,代表偏移低位
计算公式如下:
offset = (目标地址- 源地址 -4) & 0x007fffff
high = offset >> 12(十进制)
low = ( offset & 0x00000fff )>>1
machineCode = ((0xF800 | low) << 16) | (0xF000 | high)

下面来算算我们这里用到的:
bl 0x410000
offset = (目标地址- 源地址 -4) & 0x007fffff
= (0x410000-0x40280e-4)& 0x007fffff =D7EE
high = offset >> 12(十进制) = D
low = ( offset & 0x00000fff )>>1 = 3F7
machineCode = ((0xF800 | low) << 16) | (0xF000 | high)
=((0xF800 | 3F7) << 16) | (0xF000 | D)
=0XFBF7F00D
也就对应了从低到高的:0D 00 7F FB
这也是如下二进制调制指令的来源:
0040280e: 0d f0 f7 fb bl 0x410000
代码链接:
https://community.nxp.com/t5/S32K-Knowledge-Base/S32K3-tool-part-How-to-flexibly-debug-elf-files-without-source/ta-p/2108317

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

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

相关文章

Nacos 配置文件总结

Nacos 配置文件总结 文章目录 Nacos 配置文件总结1 、在 Nacos 服务端添加配置文件1. 启动Nacos Server。2. 新建配置文件。3. 发布配置集后&#xff0c;我们便可以在配置列表中查看相应的配置文件。4. 配置nacos数据库5. 运行 Nacos 容器6. 验证安装结果7. 配置验证 2 、在 Na…

ASP.NET Web Forms框架识别

ASP.NET 支持三种不同的开发模式&#xff1a; Web Pages&#xff08;Web 页面&#xff09;、MVC&#xff08;Model View Controller 模型-视图-控制器&#xff09;、Web Forms&#xff08;Web 窗体&#xff09;&#xff1a; Web Pages 单页面模式MVC 模型-视图-控制器Web Form…

哈工大计统大作业-程序人生

摘 要 本项目以“程序人生-Hellos P2P”为核心&#xff0c;通过编写、预处理、编译、汇编、链接及运行一个简单的Hello程序&#xff0c;系统探讨了计算机系统中程序从代码到进程的全生命周期。实验基于Ubuntu环境&#xff0c;使用GCC工具链完成代码转换&#xff0c;分析了预处…

设计模式——装饰器设计模式(结构型)

摘要 文中主要介绍了装饰器设计模式&#xff0c;它是一种结构型设计模式&#xff0c;可在不改变原有类代码的情况下&#xff0c;动态为对象添加额外功能。文中详细阐述了装饰器模式的角色、结构、实现方式、适合场景以及实战示例等内容&#xff0c;还探讨了其与其他设计模式的…

途景VR智拍APP:开启沉浸式VR拍摄体验

在数字化时代&#xff0c;VR技术以其沉浸式的体验逐渐走进了人们的日常生活。途景VR智拍APP作为一款集看图和拍照于一体的VR软件&#xff0c;为用户带来了全新的视觉体验和便捷的拍摄方式&#xff0c;无论是专业摄影师还是普通用户&#xff0c;都能轻松上手&#xff0c;拍出令人…

Linux环境搭建MCU开发环境

操作系统版本&#xff1a; ubuntu 22.04 文本编辑器&#xff1a; vscode 开发板&#xff1a; stm32f103c8t6 调试器&#xff1a; st-link 前言 步骤一&#xff1a; 安装交叉编译工具链 步骤二&#xff1a; 创建工程目录结构 步骤三&#xff1a; 调试…

【基础算法】高精度(加、减、乘、除)

文章目录 什么是高精度1. 高精度加法解题思路代码实现 2. 高精度减法解题思路代码实现 3. 高精度乘法解题思路代码实现 4. 高精度除法 (高精度 / 低精度)解题思路代码实现 什么是高精度 我们平时使用加减乘除的时候都是直接使用 - * / 这些符号&#xff0c;前提是进行运算的数…

Windows最快速打开各项系统设置大全

目录 一、应用背景 二、设置项打开方法 2.1 方法一界面查找&#xff08;最慢&#xff09; 2.2 方法二cmd命令&#xff08;慢&#xff09; 2.3 方法三快捷键&#xff08;快&#xff09; 2.4 方法四搜索栏&#xff08;快&#xff09; 2.5 方法五任务栏&#xff08;最快&am…

嵌入式编译工具链熟悉与游戏移植

在自己的虚拟机Ubuntu系统下&#xff0c;逐步编译 mininim源码(波斯王子重制开源版&#xff09; 指令流程 sudo apt-get remove liballegro5-dev liballegro-image5-dev \liballegro-audio5-dev liballegro-acodec5-dev liballegro-dialog5-dev sudo apt-get install automak…

DeepSeek-R1-0528,官方的端午节特别献礼

DeepSeek&#xff1a;端午安康&#xff01;刻在国人骨子里的浪漫 2025 年 05 月 28 日 | DeepSeek 端午特别献礼 当粽叶飘香时&#xff0c;DeepSeek 悄然带来一份节日惊喜 版本号 DeepSeek-R1-0528 正式上线 官方赋予它的灵魂是&#xff1a; 思考更深 推理更强 用户通过官网…

001 flutter学习的注意事项及前期准备

在学习flutter之前&#xff0c;还需要进行一些初始的配置&#xff0c;然后才可以学习flutter 1.安装flutter 国内官网&#xff1a;https://flutter.cn​​​​​​ 国际官网&#xff1a;https://flutter.dev 安装完成后&#xff0c;按照官网上面的操作步骤进行配置&#xf…

CS144 - Lecture 1 记录

CS144 - Lecture 1 由于没讲义&#xff0c;全看课了&#xff0c;系统性的总结有点难&#xff0c;记一些有趣的东西吧。 数据链路和网络层的传输 我们可以看见&#xff0c;对于发送方&#xff0c;我们的数据链路层为我们的网络层提供服务&#xff0c;在经过路由的时候&#xf…

【数据结构】——二叉树--链式结构

一、实现链式结构二叉树 二叉树的链式结构&#xff0c;那么从名字上我们就知道我们这个二叉树的底层是使用链表来实现的&#xff0c;前面我们的二叉树是通过数组来实现的&#xff0c;那么在其是完全二叉树的情况下&#xff0c;此时我们使用数组来实现就会使得其空间浪费较少&a…

充电便捷,新能源汽车移动充电服务如何预约充电

随着新能源汽车的普及&#xff0c;充电便捷性成为影响用户体验的关键因素之一。传统的固定充电桩受限于地理位置和数量&#xff0c;难以完全满足用户需求&#xff0c;而移动充电服务的出现&#xff0c;为车主提供了更加灵活的补能方式。通过手机APP、小程序或在线平台&#xff…

基于 Chrome 浏览器扩展的Chroma简易图形化界面

简介 ChromaDB Manager 是基于 Chrome 浏览器扩展的一款 ChromaDB&#xff08;一个流行的向量数据库&#xff09;的数据查询工具。提供了一个用户友好的界面&#xff0c;可以直接从浏览器连接到本地 ChromaDB 实例、查看集合信息和分片数据。本工具特别适合开发人员快速查看和…

IM系统的负载均衡

1.IM场景的负载均衡 2.方案总览 SDK层想要连接一个TCP网关或者WebSocket网关的方案 SDK单地址:在SDK中写死某个网关的IP或者域名,缺点是更换地址需要重新打包SDK SDK多地址:防止某一个地址嗝屁了写上多个地址用足保持高可用 暴露接口给客户端:SDK层访问接口动态获得地址 注…

使用 Zabbix 监控 MySQL 存储空间和性能指标的完整实践指南

目录 引言 一、最终目标支持功能 二、监控方案设计 2.1 技术选型 2.2 设计思路 三、实现步骤 3.1 准备工作 3.11 创建 MySQL 监控账号 3.12 配置 .my.cnf 文件 3.2 编写统一脚本 3.3 配置 Zabbix Agent UserParameter 3.4 Zabbix 前端配置建议 四、总结 引言 MySQL …

【技能拾遗】——家庭宽带单线复用布线与配置(移动2025版)

&#x1f4d6; 前言&#xff1a;在家庭网络拓扑中&#xff0c;客厅到弱电箱只预埋了一根网线&#xff0c;由于已将广电的有线电视取消并改用IPTV。现在需要解决在客厅布置路由器和观看IPTV问题&#xff0c;这里就用到单线复用技术。 目录 &#x1f552; 1. 拓扑规划&#x1f55…

异步日志监控:FastAPI与MongoDB的高效整合之道

title: 异步日志监控:FastAPI与MongoDB的高效整合之道 date: 2025/05/27 17:49:39 updated: 2025/05/27 17:49:39 author: cmdragon excerpt: FastAPI与MongoDB整合实现日志监控系统的实战指南。首先配置MongoDB异步连接,定义日志数据模型。核心功能包括日志写入接口、聚合…

在 Android 上备份短信:保护您的对话

尽管我们的Android手机有足够的存储空间来存储无数的短信&#xff0c;但由于设备故障、意外删除或其他意外原因&#xff0c;您可能会丢失重要的对话。幸运的是&#xff0c;我们找到了 5 种有效的 Android SMS 备份解决方案&#xff0c;确保您的数字聊天和信息保持安全且可访问。…