How to disassemble? Where is the entry point?

When I build my firmware, I get a file "myfirmware.bin" which I can then upload to my Arduino Due sam3x8e.

I'm trying to use the program "arm-none-eabi-objdump" to disassemble the firmware file and see what happens at the entry point, but I don't know where the entry point is. Also I don't know where the constant data is stored.

The address is stored in the RESET vector.

Is the RESET vector the first two bytes of the firmware binary? Stored in Little Endian?

The second FOUR bytes. Little endian.
First 4 is initial SP.

This guy made an dump project...

@westfw That doesn't make sense. The second four bytes are 00089f79. In Little Endian, that's 799f0800, which in decimal is over 2 gigabytes.

Your tool is probably already doing the endian conversion. 0x89f79 is a reasonable start address for a Due program.

You'd be better off disassembling the .elf file, which contains symbols and other useful info.
For a empty sketch, I see:

Disassembly of section .text:

00080000 <exception_table>:
   80000:       00 80 08 20 fd 05 08 00 9d 0a 08 00 9d 0a 08 00     ... ............

;; Much later

   805f8:       2007002c        .word   0x2007002c

000805fc <Reset_Handler>:
   805fc:       4b15            ldr     r3, [pc, #84]   ; (80654 <Reset_Handler+0x58>)
   805fe:       4a16            ldr     r2, [pc, #88]   ; (80658 <Reset_Handler+0x5c>)
   80600:       b510            push    {r4, lr}
   80602:       4293            cmp     r3, r2
   80604:       461c            mov     r4, r3

(start address 0x000805fd (well, 805fc, actually. Set the low bit because Thumb mode.))

There's a reason why I'm dealing with the binary file instead of the ELF, which I will soon reveal.

It's not possible for 0x89f79 to be the entry point, because my firmware file is only 114 kB. You can't jump to position 565113 if the code ends at 116736.

Flash doesn't start at 0 by default on a SAM3X.

Do you understand the contents of the file? How many sections, how big and at which addresses?

In order to get the correct offset into the firmware file, I need to subtract 0x80000 from the entry point because the Flash begins at address 0x80000.

I wrote a script in Linux to show me the first 20 lines of the disassembly at the entry point:

#!/bin/sh
objdump=/opt/sloeber/arduinoPlugin/packages/arduino/tools/arm-none-eabi-gcc/4.8.3-2014q1/arm-none-eabi/bin/objdump

# On the next line, we get the entry point from the second 32-Bit Little Endian integer
entry_point=`dd if=myfirmware.bin bs=4 skip=1 count=1 2> /dev/null | xxd -ps -u | tac -rs .. | xargs echo obase=10\; ibase=16\; | bc`

echo Raw address of entry point in decimal = ${entry_point}
echo Thumb entry address = $((entry_point-1))
echo In the sam3x8e, Flash begins at 0x80000, so subtract 524288, giving an entry point of $((entry_point-1-524288))
dd if=myfirmware.bin of=zero.bin bs=1 skip=$((entry_point-1-524288)) 2> /dev/null
${objdump} -b binary -m arm -D ./zero.bin > disassembly.txt
head -20 disassembly.txt

And here are the first few lines of the disassembly:

Disassembly of section .data:

00000000 <.data>:
       0:	4a164b15 	bmi	0x592c5c
       4:	4293b510 	addsmi	fp, r3, #16, 10	; 0x4000000
       8:	d001461c 	andle	r4, r1, ip, lsl r6
       c:	e0012300 	and	r2, r1, r0, lsl #6
      10:	e0074b13 	and	r4, r7, r3, lsl fp

The first instruction is "bmi 0x592c5c" which means "If the negative flag is set, jump to address 0x592c5c". Given that the first instruction is a jump instruction, this makes me feel confident that I've found the correct entry point. I wonder though why the negative flag would be set though? Does the negative flag perhaps indicate something like brown out?

And just to see what's located at the jump address, I wrote the following script:

#!/bin/sh

# The next line converts the address from hex to dec
addr=`echo obase=10\; ibase=16\; $1 | bc`

objdump=/opt/sloeber/arduinoPlugin/packages/arduino/tools/arm-none-eabi-gcc/4.8.3-2014q1/arm-none-eabi/bin/objdump

echo Jump address in decimal = ${addr}
echo In the sam3x8e, Flash begins at 0x80000, so subtract 524288, giving a firmware file offset of $((addr-524288))

bytes_to_skip=$((addr-524288))
size_of_original=`stat -c %s ./myfirmware.bin`

if [ ${addr} -lt 524288 ] || [ ${addr} -gt 1048575 ]; then
    echo You have specified an address past the end of the Flash
    echo The Flash begins at address        524,288
    echo The Flash ends   at address      1,048,575
    echo Internal ROM begins at address   1,048,576
    echo Reserved begins at address       2,097,152
    echo SRAM begins at address         536,870,912
    exit 1
else
	if [ ${bytes_to_skip} > ${size_of_original} ]; then
            echo Specified address is within range of Flash however your firmware file is too small
            echo Firmware size = ${size_of_original}
            echo Jump address  = ${bytes_to_skip}
            exit 1
	fi
fi

dd if=myfirmware.bin of=showfrom.bin bs=1 skip=$((addr-524288)) 2> /dev/null

${objdump} -b binary -m arm -D ./showfrom.bin > disassembly_showfrom.txt
head -20 disassembly_showfrom.txt

I run this script at the command line with: ./showfrom.sh 592C5C

And here's what I get back:

Jump address in decimal = 5844060
In the sam3x8e, Flash begins at 0x80000, so subtract 524288, giving a firmware file offset of 5319772
You have specified an address past the end of the Flash
The Flash begins at address 524,288
The Flash ends at address 1,048,575
Internal ROM begins at address 1,048,576
Reserved begins at address 2,097,152
SRAM begins at address 536,870,912

image

The address 0x592c5c is in the Reserved section. So when the microcontroller starts executing instructions from the Flash, the first thing it does is check if the negative flag is set, and if it is then it jumps to an address inside the Reserved section. Does anyone know what's going on here?

A code section is rarely named ".data".

I'm invoking 'objdump' on a binary file that doesn't have any section headers in it, hence it's going with the default, ".data".

I doubt that your interpretation is correct. The listed "opcodes" do not make any sense, starting with the "bmi...". Can you find the listed bytes at the assumed flash address? I'd interpret the first dumped line

as two words of 0x4a16, 0x4b15. These may be Thumb codes as already shown

Also it's not a good idea to show decimal addresses. Hex format is the suggested format for addresses and other data of unknown type.

But it's your game, good luck with it :slight_smile:

It looks like you're disassembling ARM32 instructions, while the SAM3X8A uses THUMB instructions. try adding -M force-thumb

The data looks right - working directly from the .elf file for Blink, I see:


00080624 <Reset_Handler>:
   80624:       4b15            ldr     r3, [pc, #84]   ; (8067c <Reset_Handler+0x58>)
   80626:       4a16            ldr     r2, [pc, #88]   ; (80680 <Reset_Handler+0x5c>)
   80628:       b510            push    {r4, lr}
   8062a:       4293            cmp     r3, r2
   8062c:       461c            mov     r4, r3

The binary is the same...

I've changed the invocation of 'objdump' so that it now uses "-M force-thumb", and so now here's what I get:

Disassembly of section .data:

00000000 <.data>:
       0:	4b15      	ldr	r3, [pc, #84]	; (0x58)
       2:	4a16      	ldr	r2, [pc, #88]	; (0x5c)
       4:	b510      	push	{r4, lr}
       6:	4293      	cmp	r3, r2
       8:	461c      	mov	r4, r3
       a:	d001      	beq.n	0x10
       c:	2300      	movs	r3, #0
       e:	e001      	b.n	0x14
      10:	4b13      	ldr	r3, [pc, #76]	; (0x60)
      12:	e007      	b.n	0x24
      14:	4913      	ldr	r1, [pc, #76]	; (0x64)
      16:	18d0      	adds	r0, r2, r3
      18:	4288      	cmp	r0, r1

Does this look right?

Yep.

Ok cool now let's get down to business. . .

OK here's what I'm envisaging. . .

I have the Atmel SAM D21 microcontroller connected by I2C to another microcontroller made by Texas Instruments (hereafter referred to as TI).

The TI microcontroller will send a firmware update to the Atmel microcontroller in 32-byte packets, something like:

set 0000,30b5dd346296d14b681f3265ccd7c0a1b583cd85e0f00af681af104ed032bd36
set 0001,83cae16dfcd7d553957934526291fb7c4944e61a8f6ba64260b6b0c2c16a4d1f
set 0002,3ce7b78d53ad60f95dd499ada67998c92f1b420475bc01ee3528a490b102cbec
set 0003,a49f24219268fc2b2d926fa9c96b3e35216016b4fcfce95811a38e2029111abf
set 0004,202b9e306e0624a5206556424550073c68b2f5dd095fdd0b45f0cac7a48dac39

The new firmware will be approx. 100 kB in size, and so the TI microcontroller will send 3,200 packets to the Atmel microcontroller.

After all 3,200 packets have been sent, the final command from TI to Atmel will be:

upgrade cdd642c52840a7946298766353ba2b02

This final 'upgrade' command contains the hash digest of the new firmware. When the Atmel microcontroller receives this hash digest, it calculates the hash of the new data it received, and if the two digests match then it performs the firmware upgrade.

And so the next question is. . . . . how will the Atmel microcontroller update its own firmware when it's actually turned on and running? I have come up with two possibilities:

Possibility 1: Of the entire 256 kB of Flash that's available for program memory, split it into two banks, and have a bank selection flag at boot-up. So if the microcontroller boots up into bank 1, then the firmware update will be written to bank 2. Similarly if the microcontroller boots up into bank 2, then the firmware update will be written to bank 1. The firmware machine code will of course have to be compiled to be Position Independent (e.g. compiled with "-fPIC"), and also somehow I will have to deal with the addresses of global constant data (i.e. the stuff that what would normally go inside the .rodata section in an ELF file).

Possibility 2: When the firmware update comes in over I2C, store it in the unused second half of the Flash. Write an updater function called 'update_firmware' in ARM assembler and link it into the firmware, and then at runtime, load the 'update_firmware' function into volatile RAM, and execute it from volatile RAM. This function 'update_firmware' in RAM will copy the new firmware from the end of the Flash to the start of the Flash.

So for Possibility No. 2, here's what would happen when the 'upgrade' instruction comes in over I2C:

void Message_Handler(char const *const msg)
{
if ( 0 == memcmp("upgrade ", msg, 8u) )
{
sha256digest digestA = sha256digest_from_string(msg + 8u);
sha256digest digestB = calc_digest_of_data( /* start addr = / 0xA0000, / end addr = */ 0xC0000);
if ( digestA != digestB ) return; // Don't do anything if hash is wrong
// Next we copy the function 'update_firmware' into RAM
static char buf[238u];
memcpy(buf, update_firmware, 238u);
// Now we just execute the function from RAM
void (const RamFunc)(void) = (void ()(void))&buf;
RamFunc();
}
}

The ARM assembler for the function 'update_firmware' would look something like the following:

update_firmware:
sub sp, #8
movs r3, #0
str r3, [sp, #4]
ldr r3, [sp, #4]
cmp.w r3, #524288 ; 0x80000
bcs.n 20 <update_firmware()+0x20>
ldr r3, [sp, #4]
ldr r3, [sp, #4]
ldr r3, [sp, #4]
adds r3, #1
str r3, [sp, #4]
ldr r3, [sp, #4]
cmp.w r3, #524288 ; 0x80000
bcc.n e <update_firmware()+0xe>
b.n 20 <update_firmware()+0x20>
nop

which would be approximately equivalent to the following C code:

void update_firmware(void)
{
for ( char p = (char)0x80000u; p < (char*)0xA0000u; ++p )
{
*p = *(p + 0x20000u);
}
for (;;); // Hang here forever -- don't return -- must powercycle the microcontroller
}

If anyone has ever done this before, or if you have any ideas at all to share, please share!

I've made a new firmware that accepts the following command over RS232:

MEG,0,?

It responds to this command with 1 megabyte of data starting from address 0.

And so to read the microcontroller's entire address space, which is 4.3 gigagbytes, I issue this command 4,300 times as follows:

MEG,0,?
MEG,1048576,?
MEG,2097152,?
MEG,3145728,?
............all the way up to.............
MEG,4293918720,?

This process will take 17 hours, and will be finished at about 3pm tomorrow. So far though, I've got the first 436 megabytes so I can at least analyse the first two megabytes.

image

The first 512 kB is the Boot Memory.
Next is the Flash which is also 512 kB.
Next is the ROM which is only supposed to be 16 kB however it takes up 1 megabyte of address space (why is that?).

When I look at the first 8 bytes of the boot memory, I see: 0080 0820 69a0 0800

If I treat these 8 bytes as two 32-Bit Little Endian Numbers, I get:
0x20088000 and 0x0008a069

These are the exact same two numbers I find at the beginning of the Flash, and they are the stack pointer and the entry point address.

Actually, just now I've compared the first 100kB of the boot memory with the first 100kB of the Flash, and they're identical. Anybody know what's going on here?

Could it be possible that my firmware is unable to analyse the boot memory? I mean if you try to access a memory address less than 0x80000, then does the CPU add 0x80000 to whatever number you gave it? Sort of like the following:

char GetByteAtAddress(char const *p)
{
    uint32_t const addr = (uint32_t)p;

    if ( addr < 0x80000u ) p += 0x80000u;

    return *p;
}

Is this what's happening?

Or could it be possible that the boot memory actually is identical to the Flash? If so, why would that be? When you use the program 'bossac' to do a firmware upgrade, does it store the firmware at address 0x00000 and also a duplicate at 0x80000?

I don't understand your process. Your ultimate goal seems to be an I2C bootloader with image verification, for a SAMD21.

the SAMD21 has an entirely different memory map than the SAM3X in the Due. Flash/program memory starts at 0x0, there is no ROM bootloader, and no fancy re-mapping of the flash between 0x0 and 0x80000. If your eventual target is a SAMD21, start working with a SAMD21.

Your memory dump will probably fail because memory accesses to unimplemented addresses will cause faults.

The mapping of memory, such that the same physical memory appears in multiple places in the address space(on SAM3X), is documented in the datasheet... The flash memory can appear at either 0 or 0x80000, and IIRC, either bank of flash can be configured to appear at 0. This rather complicated scheme is so that the chip can boot from the ROM bootloader, even though the ROM would not normally be located at 0x0 where the start vectors are located. The SAMD line is considerably simplified.