[S32K3 tool part]:How to flexibly debug elf files without source code

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

[S32K3 tool part]:How to flexibly debug elf files without source code

[S32K3 tool part]:How to flexibly debug elf files without source code

[S32K3 tool part]:How to flexibly debug elf files without source code

kerryzhou_16-1748671511335.png

 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.     

kerryzhou_0-1748671404063.png

 

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     
kerryzhou_1-1748671404069.png

 

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

kerryzhou_2-1748671404077.png

 

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

kerryzhou_3-1748671404133.png

 

Fig 4

kerryzhou_4-1748671404164.png

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:    

kerryzhou_5-1748671404183.png

 

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

kerryzhou_6-1748671404277.png

Fig 7

The modified debug result is as follows:

kerryzhou_7-1748671404330.png

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:

kerryzhou_8-1748671404381.png

 

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.

kerryzhou_9-1748671404427.png

 

Fig 10

kerryzhou_10-1748671404468.png

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.

kerryzhou_11-1748671404609.png

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.    

kerryzhou_12-1748671404682.png

Fig 13

kerryzhou_13-1748671404823.png

Fig 14

kerryzhou_14-1748671404883.png

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:

kerryzhou_15-1748671405043.png

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

 

 

Attachments
No ratings
Version history
Last update:
‎05-30-2025 11:16 PM
Updated by: