[S32K3 tool part]:How to flexibly debug elf files without source code
1.Doc Introduction
When supporting customers with the MCU software technical issues, we often encounter situations where customers are unable to provide the source code of problematic projects due to company policies. At most, they can provide the elf files of problematic projects. This involves how to make good use of elf to achieve flexible debugging goals. Elf file is a binary file format that contains program code, data, symbol tables, segment tables, and other information.
When debugging elf, you cannot see the source code, but you can see the function name corresponding to the assembly address. At this time, debugging can still relatively know the location. However, the problem is that it is not possible to freely modify and generate new files like modifying the source code. Is it feasible to make specific modifications to elf files to achieve the purpose of functional testing?
This article will provide testing methods for skipping specific positions, erasing functions, replacing code, concatenating functions, etc. on an existing elf, to achieve skipping of code in certain positions of elf, or not executing it at any time, creating a new area to concatenate other functional functions, and then modifying the original call position to forcibly insert test function code.
The testing platform for this article is S32K344 and RTD500.
Fig 1
2. Function implementation
Fig 1 is an elf file of the original function. The function is to call three functions after startup: Fun1, Fun2, and Fun3
Fun1: Flashing Red Light
Fun2: Flashing green light
Fun3: Flashing Blue Light
The main functions that need to be implemented in this article are as follows:
(1) Fun2 function skipped
There are two types of function skipping here:
When debugging, start from Fun2 and modify the PC to jump to Fun3
Do not run Fun2 even when powered on, simply erase the Fun2 value from the main as nop
Fig 2
(2)Add Fun4, modify Fun2 call in Main to jump to Fun4
This involves inserting Fun4 code into an empty flash address, modifying the jump asm code of Fun2 to jump to Fun4
Fig 3
2.1 Code and Tool preparation
Hardware Platform: S32K344-EVB
Software: RTD500, S32DS3.5, JFLASH, PE Multilink(built-in to EVB), Lauterbach
JFLASH tool download link:https://www.segger.com/downloads/jlink/
Create a simple led blinking project that can be based on RTD's existing Siul_2Dio_ip_Instance_S32K344 project, add three LED light pins, and construct three functions for flashing red, green, and blue lights respectively. Call the three functions sequentially in main. Generate elf backup when testing is working.
2.2 Implementation of elf modification function: Fun2 function skipped
Here is the specific implementation method for Figure 2: the PC crosses Fun2 and replaces Fun2 with asm nop
2.2.1 PC Crossing Fun2
In the Main function, call the assembly position breakpoint of Fun2, directly modify PC to the value of Fun3 address+1, and then run to skip Fun2. As shown in Figure 4, it has already been run to Fun2, but has not yet entered the function body of Fun2. Simply change the pointer entry that was originally intended to call Func2 to 0X4027B4+1, which is the function entry of Fun3. As can be seen, after modifying the PC, press enter and step into the Fun3 function body. If you modify the starting point of Fun2's function body to jump from PC to Fun3, both sides of Fun3 will run from the main function as a whole
Fig 4
Fig 5
2.2.2 Replace Fun2 with NOP
The situation of directly using debug to jump to PC above bypasses Fun2. Although Fun2 can be skipped in testing, it should be noted that when downloading the code, it will actually run the code before entering debug. If some tests want to start from POR without running Fun2, then it is necessary to directly erase the location code of the original elf Fun2 call, and the commonly used method can be replaced with harmless nop instructions. The assembly hexadecimal value of the nop instruction is 00 BF, as shown in the following figure:
Fig 6
With the target modification value, the following is to find the call address of Fun2 in elf main and replace the corresponding 4 bytes with 00BF00BF.
From the original elf file, we can see that the data location of the call Fun2 is 4 bytes starting from the absolute address 0X0040280E, use the JFLASH tool in the Segger JLINK driver to open the original elf file, modify the data of the 4 bytes starting address 0X0040280E from FFF7ABFF to 00BF00BF, save the modified data as an srec file, and then call the srec file in the temporary project to run the modified code.
The following figure shows the modification process
Fig 7
The modified debug result is as follows:
Fig 8
As can be seen, the assembly that jumped from the 0X40280e area to Fun2 has now become a nop instruction.
At this point, running at full speed will ignore fun2 and proceed in sequence. Whether debugging or after powering on, Fun2 calls have been completely erased from the overall runtime sequence.
Of course, since manually modified elf cannot be directly saved as an elf file through JFLASH, choosing to save it as an Srec file will result in losing the symbol table when debugging again. When using elf in the early stage, remember the absolute addresses of several functions that need to be used.
2.3 Elf modification function implementation: Fun4 replaces Fun2 entrance
In the above content, we skip or insert nop directly at the main Fun2 call position, can we call another function body in the original Fun2 for testing, achieving the same area but with other functions? It's possible. There are also two types here: one is to destroy the original Fun2 function body position and directly replace the code content of the function body, of course, this is limited by the size of the original Fun2 function body. On the other hand, keep the original Fun2 for future use. You can start another function Fun4 at other blank addresses in flash, and then change the code that calls Fun2 in main to call Fun4 to achieve seamless docking of Fun1->Fun4->Fun3 operations.
This article mainly uses to create a new function at a specific absolute address in the flash blank space. Of course, attention should be paid to the original elf map situation to ensure that the blank area is sufficient for use. It is best for this new function to be self-contained and independent of other functions to avoid calling bias. If it is necessary to call other function bodies, it is necessary to set the addresses of other dependent functions in the sample project to be consistent when constructing this new function.
Here, we will create a S32DS project and allocate a flash area in the linkfile to store the newly created Fun4. The Fun4 function is to achieve alternating blink green and red led.
2.3.1 linkfile modification
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 function code
The function body code is constructed as follows, purely logical, without relying on any external functions or variables.
__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;
}
Here we know, this Fun4 address starts from flash 0x00410000 and generates elf after compiling the project with the above function.
2.3.3 Separate Fun4 function body into independent files
Open the elf file with newly created Fun4 using JFLASH and delete all code above 0x00410000. Method:
JFLASH->Edit->Delete range:
Fig 9
After deletion, obtain a file containing only Fun4 code and save it as the ElfdebugSource_S32K344_STD500_delete.srec file.
2.3.4 Merge the Fun4 function body into the corresponding Srec of the original elf
Open the Srec corresponding to the original elf and the Fun4 Srec file that was just separated using JFLASH. Choosing to merge two files will automatically concatenate Fun4 files with different addresses into the original Srec file and save them as a separate file.
Fig 10
Fig 11
2.3.5 Modify the original Fun2 calling in main to Fun4 calling
Modify code from:
0040280e: ff f7 ab ff bl 0x402768 <Func2>
To:
0040280e: 0d f0 f7 fb bl 0x410000
Among them, 0x410000 is the absolute address of Fun4.
Fig12
Modify the Srec file that has already added Fun4 to start with 0040280e with a value of 0d f0 f7 fb, then save it as a new Srec and debug it. It can be found that the running order of main has changed to Fun1->Fun4->Fun3
Skipped Fun2 while also running the newly integrated Fun4.
The above image was run in the S32DS+PE Multilink environment. At this time, due to the operation of the Srec file, the symbol table information for elf was no longer included, but the functionality was successful.
2.3 Lauderbach synchronously loads the original elf symbol table
Due to a series of grafting modifications on the original elf and saving it as SREC, the symbol table was lost. So if you still want to view the symbol table of unmodified areas, you can use the Laubach tool. After attaching the code, you can load the original elf file in trace32 by following the command:
Data.LOAD.Elf
C:\S32DS35_RTD500\elfdebug\elftest\Debug_FLASH\Elfdebug_S32K344_RTD500.elf /nocode
As can be seen, the original part with Fun2 symbol table is only missing due to modifications, but other call header symbol tables still exist. This is also convenient for code execution and reading.
Fig 13
Fig 14
Fig 15
3. Knowledge points
Here we share the corresponding hexadecimal data operations for BL addr jumps. The previous one was generated directly using S32DS
0040280e: 0d f0 f7 fb bl 0x410000
It can be known that the value corresponding to the bl 0x410000 instruction is 0d f0 f7 fb
So how is the value of 0d f0 f7 fb calculated?
This requires reference to ARM's architecture document: DDI0403E_d_armv7m_arm.pdf
For the situation corresponding to the BL jump thumb2 instruction:
Fig 16
For BL, it is a long jump, which is actually composed of two jump instructions, Thumb
instructions are all 2 bytes long, and BL consists of 2 jump instructions that make up 4 bytes.
The 0-11 bits represent an 11 bit address, with the following specific meanings:
The 11th digit is 0, representing a high offset
The 11th digit is 1, representing a low offset
The calculation formula is as follows:
offset = (target address- source address -4) & 0x007fffff
high = offset >> 12(decimalism)
low = ( offset & 0x00000fff )>>1
machineCode = ((0xF800 | low) << 16) | (0xF000 | high)
Let's calculate what we use here:
bl 0x410000
Offset=(Destination Address - Source Address -4)&0x007fffff
= (0x410000-0x40280e-4)& 0x007fffff =D7EE
High=offset>>12 (decimal)=D
low = ( offset & 0x00000fff )>>1 = 3F7
machineCode = ((0xF800 | low) << 16) | (0xF000 | high)
=((0xF800 | 3F7) << 16) | (0xF000 | D)
=0XFBF7F00D
This corresponds to a low to high: 0D 00 7F FB
This is also the source of the following binary modulation instructions:
0040280e: 0d f0 f7 fb bl 0x410000
記事全体を表示