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

article/2025/6/24 11:01:56

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 = 0x003C4000int_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 = 0x00000100int_sram_no_cacheable   : ORIGIN = 0x20430000, LENGTH = 0x0000FF00    /* 64KB, needs to include int_sram_results  */int_sram_results        : ORIGIN = 0x2043FF00, LENGTH = 0x00000100int_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, 0x4029131cwhile (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.hkcw.cn/article/tObChGhBYo.shtml

相关文章

树莓派PWM控制LED灯

目录 一、什么是PWM二、树莓派引脚图三、命令行控制LED灯四、PWM控制LED呼吸灯 一、什么是PWM PWM&#xff08;Pulse Width Modulation&#xff0c;脉冲宽度调制&#xff09;是一种通过调节数字信号的占空比&#xff08;Duty Cycle&#xff09;来模拟模拟信号的技术。它通过快…

第十四章 MQTT订阅

系列文章目录 系列文章目录 第一章 总体概述 第二章 在实体机上安装ubuntu 第三章 Windows远程连接ubuntu 第四章 使用Docker安装和运行EMQX 第五章 Docker卸载EMQX 第六章 EMQX客户端MQTTX Desktop的安装与使用 第七章 EMQX客户端MQTTX CLI的安装与使用 第八章 Wireshark工具…

六.MySQL增删查改

CRUD : Create(创建), Retrieve(读取)&#xff0c;Update(更新)&#xff0c;Delete&#xff08;删除&#xff09; 一.增 insert 1.单行数据 全列插入 语法特点&#xff1a;不指定字段名&#xff0c;按表结构字段顺序依次提供所有值。 注意&#xff1a;字段顺序必须与表定义一…

TKernel模块--自定义RTTI,对象句柄,引用计数

TKernel模块–RTTI&#xff0c;对象句柄&#xff0c;引用计数 1.DEFINE_STANDARD_HANDLE(x1, x2) #define DEFINE_STANDARD_HANDLE(C1,C2) DEFINE_STANDARD_HANDLECLASS(C1,C2,Standard_Transient)其中&#xff1a; #define DEFINE_STANDARD_HANDLECLASS(C1,C2,BC) class C1…

关于TongWeb数据源兼容mysql驱动的注意事项

问题现象&#xff1a; TongWeb数据源在采用mysql驱动的国产数据库时&#xff0c;因数据库慢报超时为数据源配置参数的 validation-query-timeout值5秒&#xff0c;而不是期望的maxwait、connectiontimeout值。 The last packet successfully received from the server was 5,0…

CSS专题之水平垂直居中

前言 石匠敲击石头的第 16 次 在日常开发中&#xff0c;经常会遇到水平垂直居中的布局&#xff0c;虽然现在基本上都用 Flex 可以轻松实现&#xff0c;但是在某些无法使用 Flex 的情况下&#xff0c;又应该如何让元素水平垂直居中呢&#xff1f;这也是一道面试的必考题&#xf…

(新)MQ高级-MQ的可靠性

消息到达MQ以后&#xff0c;如果MQ不能及时保存&#xff0c;也会导致消息丢失&#xff0c;所以MQ的可靠性也非常重要。 一、数据持久化 为了提升性能&#xff0c;默认情况下MQ的数据都是在内存存储的临时数据&#xff0c;重启后就会消失。为了保证数据的可靠性&#xff0c;必须…

Microsoft Word使用技巧分享(本科毕业论文版)

小铃铛最近终于完成了毕业答辩后空闲下来了&#xff0c;但是由于学校没有给出准确地参考模板&#xff0c;相信诸位朋友们也在调整排版时感到头疼&#xff0c;接下来小铃铛就自己使用到的一些排版技巧分享给大家。 注&#xff1a;以下某些设置是根据哈尔滨工业大学&#xff08;威…

Linux 基础IO(上)

目录 前言 重谈文件 文件操作 1.打开和关闭 2.对文件打开之后操作 理解文件fd 1.文件fd的分配规则与重定向 2.理解shell中的重定向 3.关于Linux下一切皆文件 关于缓冲区 1.为什么要有缓冲区 2.缓冲区刷新策略的问题 3.缓冲区的位置 前言 本篇到了我们linux中的文件…

单板机8088C语言计划

计划将原来用汇编写的小程序&#xff0c;用C语言重新写一遍 计划2个月能完成 然后再试试&#xff0c;能不能用C写一下固件BootLoad 和一个类似Dos时代的Debug调试器

C++11 语法特性一文详解

文章目录 1. C11 的发展史2. 列表初始化2.1 C98 中使用 {} 的初始化2.2 C11 中使用 {} 进行初始化2.3 std::initializer_list &#xff08;初始化列表&#xff09; 3. 右值引用与移动语义3.1 左值与右值3.1.1 右值分类 3.2 左值引用与右值引用3.2.1 const 左值引用为什么可以绑…

linux基础

参考视频 文章目录 1.网络的三种链接方式2. 目录结构详解3. 远程登陆和远程文件传输4. vi和vim4.1 vi和vim的三种模式4.2 vim快捷键 5. 关机重启和登录注销5.1 关机重启5.2 登录注销 6. 用户管理6.1 添加和删除用户6.2 用户信息6.3 用户组 7. 实用指令7.1 运行级别7.2 找回root…

【MLLM】多模态LLM 2025上半年技术发展(Better、Faster、Stronger)

note 文章目录 note一、新模型趋势任意模态模型推理模型小巧但功能强大的模型专家混合解码器视觉-语言-行动模型 VLA 二、特殊能力视觉语言模型中的目标检测、分割和计数多模态安全模型多模态RAG&#xff1a;检索器和重排器 三、多模态代理四、视频语言模型五、视觉语言模型的新…

python从零开始实现四极场离子轨迹仿真——框架

本篇将主要讲解程序的框架部分。 该程序主要分为三个部分&#xff0c;首先是初始化部分&#xff0c;主要为设置离子质荷比、初始位置、速度。 其次为求解轨迹部分&#xff0c;通过离子位置获取对应位置的电场&#xff0c;并经由空间电荷效应修改电场后&#xff0c;通过数值求解…

YOLO系列中的C3模块解析2025.5.31

YOLO系列中的 C3模块 是YOLOv5引入的核心组件之一&#xff0c;其设计目标是通过轻量化结构和高效特征提取提升模型性能。以下是C3模块的详细解析&#xff1a; 一、C3模块的网络层级结构 C3模块&#xff08;Cross Stage Partial Network with 3 convolutions&#xff09;结合了…

在Cesium中通过geojson和3d tiles分别加载楼宇白膜

一、geojson渲染楼宇白膜&#xff08;不推荐&#xff09; 如果你没有3dtiles文件来加载白膜&#xff0c;只有geojson加载白膜可以通过GeoJsonDataSource来加载白膜&#xff0c;json格式如下。 实现代码如下 <template><div id"cesium_container"></…

CRISPR-Cas系统的小型化研究进展-文献精读137

Progress in the miniaturization of CRISPR-Cas systems CRISPR-Cas系统的小型化研究进展 摘要 CRISPR-Cas基因编辑技术由于其简便性和高效性&#xff0c;已被广泛应用于生物学、医学、农学等领域的基础与应用研究。目前广泛使用的Cas核酸酶均具有较大的分子量&#xff08;通…

【Web API系列】WebTransportSendStream接口深度解析:构建高性能实时数据传输的基石

前言 随着Web应用复杂度的不断提升&#xff0c;传统的HTTP协议在某些场景下&#xff08;如实时游戏、视频流传输&#xff09;逐渐暴露出性能瓶颈。为解决这一问题&#xff0c;W3C提出了WebTransport API&#xff0c;旨在通过基于QUIC协议的低延迟、多路复用传输机制优化实时通…

MySQL中COUNT(*)、COUNT(1)和COUNT(字段名)的深度剖析与实战应用

MySQL中COUNT语句 三种COUNT函数的解析COUNT(*)COUNT(1)COUNT(字段名) 详细性能比较与实测分析性能差异的理论基础实际性能测试案例 实际案例解析案例1&#xff1a;电商平台订单统计案例2&#xff1a;带条件的计数比较案例3&#xff1a;性能优化实例 COUNT函数与索引的关系详解…

VS Code / Cursor 将默认终端设置为 CMD 完整指南

文章目录 &#x1f9ed; 适用范围&#x1f4cc; 背景与问题分析&#x1f6e0; 配置步骤1. 打开设置&#xff08;settings.json&#xff09;2. 添加或更新配置3. 重启终端与编辑器 &#x1f4a1; 补充&#xff1a;支持多个终端配置&#x1f9ef; 常见问题排查✅ 总结 在 Windows…