raspberrypi / pico-sdk

BSD 3-Clause "New" or "Revised" License
3.55k stars 881 forks source link

Linking with a different start address produces odd UF2 file #573

Closed rhulme closed 2 years ago

rhulme commented 2 years ago

Following the suggestion in #545 I have been working on a small flashloader. I copied memmap_default.ld to my project, moved the start address of my main application to start 4k later:

FLASH(rx) : ORIGIN = 0x10000000 + 4k, LENGTH = 2048k - 4k

and used pico_set_linker_script to force that script to be used.

The generated UF2 file still starts a 0x10000000 and on closer inspection contains the first raw 4k of the ELF file.

Looking at the ELF file, the first program header apparently starts at offset 0 in the file (also with the "wrong" address in memory), which is what elf2uf2 is parsing:

Program Header:
    LOAD off    0x00000000 vaddr 0x10000000 paddr 0x10000000 align 2**16
         filesz 0x0000a914 memsz 0x0000a914 flags rwx
    LOAD off    0x000100c0 vaddr 0x200000c0 paddr 0x1000a914 align 2**16
         filesz 0x00000dc4 memsz 0x00000dc4 flags rwx
    LOAD off    0x00000e88 vaddr 0x20000e88 paddr 0x20000e88 align 2**16
         filesz 0x00000000 memsz 0x000012d0 flags rw-

The file offset of the sections is correct:

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .boot2        00000100  10001000  10001000  00001000  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .text         00008e00  10001100  10001100  00001100  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .rodata       000009f8  10009f00  10009f00  00009f00  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA

Normally, the first program header starts at offset 0x10000:

Program Header:
    LOAD off    0x00010000 vaddr 0x10000000 paddr 0x10000000 align 2**16
         filesz 0x00004fe8 memsz 0x00004fe8 flags rwx
    LOAD off    0x000200c0 vaddr 0x200000c0 paddr 0x10004fe8 align 2**16
         filesz 0x0000024c memsz 0x0000024c flags rwx
    LOAD off    0x00000310 vaddr 0x20000310 paddr 0x20000310 align 2**16
         filesz 0x00000000 memsz 0x000003c8 flags rw-

I'm not yet clued up enough to know whether elf2uf2 needs to be more sophisticated or there has been an issue with the generation of the elf file itself but something is not right.

geurtv commented 2 years ago

I've noticed this as well. Haven't looked at what causes the problem (linker or elf2uf2), but everything seems fine as long as 64k aligned offsets are used:

FLASH(rx) : ORIGIN = 0x10000000 + 64k, LENGTH = 2048k - 64k
FLASH(rx) : ORIGIN = 0x10000000 + 128k, LENGTH = 2048k - 128k
...
rhulme commented 2 years ago

Still not entirely sure whether this is a problem with the program headers in the ELF image (although I tend to think it is) but I found this page: https://stackoverflow.com/questions/33005638/how-to-change-alignment-of-code-segment-in-elf

The alignment (as seen in the output from objdump) is set to 64k (2^16). Why the offset within the ELF image needs to be the same as the alignment on the target, I'm not sure but using the suggestion of

-Wl,-z,max-page-size=4096

as additional linker flags seems to produce a better looking ELF image:

Program Header:
    LOAD off    0x00001000 vaddr 0x10001000 paddr 0x10001000 align 2**12
         filesz 0x00009914 memsz 0x00009914 flags rwx
    LOAD off    0x0000b0c0 vaddr 0x200000c0 paddr 0x1000a914 align 2**12
         filesz 0x00000dc4 memsz 0x00000dc4 flags rwx
    LOAD off    0x00000e88 vaddr 0x20000e88 paddr 0x20000e88 align 2**12
         filesz 0x00000000 memsz 0x000012d0 flags rw-

Would make sense for the SDK to set that as a default?

lurch commented 2 years ago

Why the offset within the ELF image needs to be the same as the alignment on the target, I'm not sure

Using the picotool program, you can flash ELF, BIN or UF2 files to the RP2040, so in that case I guess it makes sense for the alignment in the ELF to match the alignment of the target? (unless I've misunderstood your question)

rhulme commented 2 years ago

I guess it makes sense for the alignment in the ELF to match the alignment of the target? (unless I've misunderstood your question)

That's possible, although probably more in the context of meatier systems that want to be able to mmap the image into the address space rather than a tool that's trying to extract the raw binary payload, which can then be stored in flash.

Even so, with a 64k alignment, I would have expected ld to pad to 64k (as it normally does), then pad to whatever the start address was (4k in this case) and then write the payload rather than cause the ELF header to be included in the theoretical payload. I suppose that's more of a philosophical question.

Either way, in the context of the Pico, a 64k alignment means unnecessarily wasting a stupid amount of space to be able to have a flashloader that isn't going to be clobbered and made unbootable by someone writing the application file using picotool or the bootrom (in the case that the flashloader is ok but the main application isn't).

kilograham commented 2 years ago

-Wl,-z,max-page-size=4096

Would make sense for the SDK to set that as a default?

I think this is probably reasonable. I assume other than this issue it also makes the ELF file smaller on disk, but not much else. I guess LD is thinking about targets which physically map the ELF file into RAM, so it figures if you start in the middle of a page it should pad the page with zeroes.

elf2uf2 is pretty much doing the right thing based on what it sees (this part of the ELF file is mapped into this part of memory) - so i'd probably not try to "fix" that.

I'd be curious to see what this does to the BIN/HEX files though

rhulme commented 2 years ago

I'd be curious to see what this does to the BIN/HEX files though

With my program it makes no difference whatsoever, which is what I would normally expect. The setting isn't altering the alignment of sections (as far as I can tell), more how the blocks within the ELF image are aligned.

I don't quite know why the data (actually the code) referred to in the first program header is forced to be aligned on a 64k boundary within the file but the second one (which is effectively the .data segment) is not (see my original post - the second block is at offset 0x000100c0 in the image but the memory alignment is still 64k).

When objdump is used to extract the binary data, whatever it's doing is obviously more involved than just reading the program headers.

kilograham commented 2 years ago

merged into develop