Author Topic: A question on GCC .ld linker script syntax  (Read 8347 times)

0 Members and 1 Guest are viewing this topic.

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3697
  • Country: gb
  • Doing electronics since the 1960s...
A question on GCC .ld linker script syntax
« on: March 12, 2023, 06:55:15 am »
This is to do with initialising DATA sections. This stuff is stored in FLASH and copied to RAM at startup.

This is the bit of the linkerscript

Code: [Select]

/* Initialized data sections go into RAM, load LMA copy after code */
/* This stuff is copied from FLASH to RAM by the startupxxx.s code */
/* This actually collects *all* "data" sections for the whole product, which is not ideal */
/* The "data" sections from the non-boot-block code should be stored outside the boot block */
/* This wastes about 3k in the boot block */
  .boot_data :
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */
      . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */
  } >RAM  AT >FLASH_BOOT

  /* used by the startup .s code to initialize data */
  _sidata = LOADADDR(.boot_data);

and this is the startup.s code which sets it up in RAM

Code: [Select]

/* Copy the data segment initializers from flash to RAM */ 
/* This actually sets up only boot block DATA but that happens to contain all DATA sections
   for the whole unit. These come to only about 3k, which is OK because there is plenty of space
   in the 32k boot block but is not ideal.
 */

  movs  r1, #0
  b  LoopCopyDataInit

CopyDataInit:
  ldr  r3, =_sidata
  ldr  r3, [r3, r1]
  str  r3, [r0, r1]
  adds  r1, r1, #4
LoopCopyDataInit:
  ldr  r0, =_sdata
  ldr  r3, =_edata
  adds  r2, r0, r1
  cmp  r2, r3
  bcc  CopyDataInit

I found that the label "boot_data" performs no function. It is not referenced anywhere. Originally it was "data" and I think that was just an ambiguity. The only labels from the above linkerscript are sdata edata and sidata, and I understand this.

The above collects DATA sections (variables initialised to nonzero; not text strings which seem to go elsewhere) from the entire project. This is ok because the DATA from the whole project currently comes to just 3k, but it could be way bigger in the future.

I am after two things:

1) How can I construct the above linker script so it collects DATA only from specific .o files? I need to have two of the above linkerscripts. One which collects DATA from specific .o files (4 of them, and that DATA will be set up in RAM by the above asm code) and the other which collects DATA from all the rest of the project (and that DATA will be set up in RAM by C code in 2) below)

2) C code for performing the same function as the asm code above. I have the following in another part of the project and am probably near to working this out.

Code: [Select]

extern char _loader_start;
extern char _loader_end;
extern char _loader_loadaddr;
extern char _loader_bss_start;
extern char _loader_bss_end;

// Copy loader code to RAM. This also sets up its initialised data.
B_memcpy(&_loader_start, &_loader_loadaddr, &_loader_end - &_loader_start);

// Clear loader's BSS and COMMON
B_memset(&_loader_bss_start, 0, &_loader_bss_end - &_loader_bss_start);

// See comments in loader.c for why the long call.

extern void loader_entry() __attribute__((long_call));
loader_entry();

// never get here (loader always reboots)
for (;;);
}

The above copies over not just DATA but code and data. The corresponding linker script is

Code: [Select]
 
  /* loader.o - goes ===LAST=== in the boot block, because after the block below
     the location pointer ends up pointing into RAM_L. You can check the boot block code
     usage by checking _loader_end_in_flash, and _loader_size, in the .map file */
     
  . = ALIGN(4);
  _loader_loadaddr = .; /* this is where the loader start is in FLASH */
 
  .loader.o :
  {
    . = ALIGN(4);
    _loader_start = .; /* this is where the loader start is in RAM */
    KEEP(*(.loader.o))
    *loader.o (.text .text*)
      . = ALIGN(4);
  } >RAM_L AT>FLASH_BOOT
 
  /* These variables are not necessarily used but can be inspected in the .map file */
  _loader_size = _loader_end - _loader_start;
_loader_end_in_flash = _loader_loadaddr + _loader_size;

/* Place loader DATA next (right after the loader code). This is data initialised to nonzero.
   Previously we had
   *loader.o (.text .text* .rodata .rodata* .data .data*)
   in the above, but that placed DATA *before* the loader code, which is dumb, even
   though it would not really matter because the loader is entered via a named function */

  _loader_rodata_data_start :
  {
  . = ALIGN(4);
  *loader.o(.rodata .rodata* .data .data*)
  . = ALIGN(4);
  _loader_end = .; /* this is where the loader end is in RAM */
} >RAM_L AT>FLASH_BOOT

  /* Place loader BSS & COMMON right after the loader code+DATA, in RAM. This is zeroed in b_main.c */

_loader_bss_start :
  {
  . = ALIGN(4);
  _loader_bss_start = .;
  *loader.o(.bss* .common COMMON*)
  . = ALIGN(4);
  _loader_bss_end = .;
} >RAM_L

I have a feeling that the answer to 1) may be to prefix the script with the name(s) of the .o file(s) but I am not sure - e.g. the ".loader.o :" below

Code: [Select]
  .loader.o :
  {
    . = ALIGN(4);
    _loader_start = .; /* this is where the loader start is in RAM */
    KEEP(*(.loader.o))
    *loader.o (.text .text*)
      . = ALIGN(4);
  } >RAM_L AT>FLASH_BOOT

And here is another bit where I think the label ".text :" is just ambiguous; you could call it "fred"...

Code: [Select]
/* This collects all other stuff, which gets loaded into FLASH after main.o above */
 
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
*(.eh_frame)

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _etext = .;        /* define a global symbol at end of code */
} >FLASH_APP

Thank you very much for any help.
« Last Edit: March 12, 2023, 07:03:57 am by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: A question on GCC .ld linker script syntax
« Reply #1 on: March 12, 2023, 09:26:52 am »
1) How can I construct the above linker script so it collects DATA only from specific .o files? I need to have two of the above linkerscripts. One which collects DATA from specific .o files (4 of them, and that DATA will be set up in RAM by the above asm code) and the other which collects DATA from all the rest of the project (and that DATA will be set up in RAM by C code in 2) below)
Let's say the four files are named boot-*.o:

You replace the *(.data) and *(.data*) in the first one with
    boot-*.o (.data .data*)
and in the second one with
    EXCLUDE_FILE (boot-*.o) *(.data .data*)

Or, if you want to keep the data in section order, first one with
    boot-*.o (.data)
    boot-*.o (.data*)
and the second one with
    EXCLUDE_FILE (boot-*.o) *(.data)
    EXCLUDE_FILE (boot-*.o) *(.data*)

Note that boot-*.o are still subject to gc.  If you want to ensure they are included and not subject to gc, replace the first one with
   KEEP(boot-*.o) boot-*.o (.data .data*)
or
   KEEP(boot-*.o) boot-*.o (.data)
   KEEP(boot-*.o) boot-*.o (.data*)

2) C code for performing the same function as the asm code above
You know the assembly is more than a bit silly?  Why not
Code: [Select]
    ldr   r1, =_sram  /* the start address of RAM, actually */
    ldr   r2, =_sdata
    ldr   r3, =_edata
    subs  r3, r3, r2   /* number of bytes to copy */
    ble  .done
    subs  r3, #4
    blt  .done
.loop:
    ldr  r0, [ r2, r3 ]
    str  r0, [ r1, r3 ]
    subs  r3, #4
    bge  .loop
.done:
This exploits the fact that after subtraction, condition code flags are set.  r3 is the decreasing index, r0 the scratch register, r1 the destination (start of RAM), and r2 the source.

Also, you set up a .boot_data section, with _sdata having the address of the contents of that section, so _sidata = LOADADDR(.boot_data) == _sdata.

The straightforward C version of above would be
Code: [Select]
extern char        _sram;
extern const char  _sdata;
extern const char  _edata;

unsigned int *const        dst = (unsigned int *)&_sram;
const unsigned int *const  src = (const unsigned int *)&_sdata;
unsigned int  len = (unsigned int)(&_edata - &sdata);
while (len >= sizeof (unsigned int)) {
    len -= sizeof (unsigned int);
    *(dst + len) =*(src + len);
}

Note: I didn't check any of this, so expect typos.
 
The following users thanked this post: peter-h

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3697
  • Country: gb
  • Doing electronics since the 1960s...
Re: A question on GCC .ld linker script syntax
« Reply #2 on: March 12, 2023, 09:46:06 am »
That's neat - thank you. Also it's nice to avoid library calls so early in the code, although in this case it is OK. I will change it to yours though.

Incidentally, would the following work?

Code: [Select]

/* Initialized data sections for boot block. These go into RAM, load LMA copy after code */
/* This stuff is copied from FLASH to RAM by the startupxxx.s code */
.boot_data :
  {
    . = ALIGN(4);
    _s_boot_data = .;        /* create a global symbol at data start */
    *b_mod1.o (.data .data*)      /* .data sections */
    *b_mod2.o (.data .data*)        /* .data sections */
    *b_mod3.o (.data .data*) /* .data sections */
    *b_mod4.o (.data .data*)        /* .data sections */
      . = ALIGN(4);
    _e_boot_data = .;        /* define a global symbol at data end */
  } >RAM  AT >FLASH_BOOT
  /* used by the startup .s code to initialize data */
  _si_boot_data = LOADADDR(.boot_data);
 
 
/* Initialized data sections for rest of unit. These go into RAM, load LMA copy after code */
/* This stuff is copied from FLASH to RAM by C code in the main stub */
.all_nonboot_data :
  {
    . = ALIGN(4);
    _s_nonboot_data = .;        /* create a global symbol at data start */
    *(.data .data*)      /* .data sections */
      . = ALIGN(4);
    _e_nonboot_data = .;        /* define a global symbol at data end */
  } >RAM  AT >FLASH_APP
  /* used by the main.c code to initialize data */
  _si_nonboot_data = LOADADDR(.all_nonboot_data);

IOW, all DATA sections not collected by the first lump (the 4 named .o files) get collected by the *(.data .data*) in the second lump.

I realise the asm code is crap, it was pointed out previously, but it came from ST and I never got around to changing it.

For the "2nd lump" I have

Code: [Select]
// Initialise DATA

extern char _s_nonboot_data;
extern char _e_nonboot_data;
extern char _si_nonboot_data;
void * memcpy (void *__restrict, const void *__restrict, size_t);  // avoid #including string.h
memcpy(&_s_nonboot_data, &_si_nonboot_data, &_e_nonboot_data - &_s_nonboot_data);

BTW the reason to avoid including any .h files in this .c module is because it is linked to a specific address and sometimes one finds .h files generate code (macros etc)!

I also wonder why use data and data* and common COMMON common* but not COMMON*. Is there some intellectual work behind this or was somebody recycling some 1978 unix code? I see no data* or data1 or data2 or common1 etc.
« Last Edit: March 13, 2023, 05:23:49 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: A question on GCC .ld linker script syntax
« Reply #3 on: March 12, 2023, 11:18:37 am »
Oopsie!

I didn't notice the AT in the section output section, so I actually recommend the following:
Code: [Select]
.boot_data ALIGN(4) {
    _sram_boot_data_start = .;
    *b_mod?.o (.data .data*);
    . = ALIGN(4);
    _sram_boot_data_stop = .;
} >RAM AT >FLASH_BOOT
_flash_boot_data_start = LOADADDR(.boot_data);
The difference being that the symbols provided by the linker are virtual addresses, but LOADADDR() yields the load memory address.

Thus,
Code: [Select]
extern char _sram_boot_data_start, _sram_boot_data_stop;
extern const char _flash_boot_data_start;

memcpy32((unsigned int *)&_sram_boot_data_start,
         (const unsigned int *)&_flash_boot_data_start,
         (unsigned int)(&_sram_boot_data_stop - &_sram_boot_data_start) / 4);
where memcpy32(unsigned int *dst, const unsigned int *src, unsigned int n) copies n 32-bit words from src to dst, with both pointers guaranteed to be aligned (by the linker script above), and non-overlapping.

Incidentally, would the following work?
.boot_data would contain only the .data sections from the four files, yes.  You could also use just
    *b_mod?.o (.data .data*)
or
    *b_mod[0-9].o (.data .data*)
That is still subject to gc by the linker, so do also consider KEEP (*b_mod?.o) *b_mod?.o (.data .data*) to ensure all code in them gets included.

However, .all_nonboot_data would contain a copy of the above.  To avoid that, you could use something like
Code: [Select]
.nonboot_data ALIGN(4) {
    _sram_nonboot_data_start = .;
    EXCLUDE_FILE(*b_mod?.o) *(.data .data*);
    . = ALIGN(4);
    _sram_nonboot_data_stop = .;
} >RAM AT >FLASH_BOOT
_flash_nonboot_data_start = LOADADDR(.nonboot_data);
Edited: as peter-h points out in a later message, in the same linker script those sections have already been "consumed", so the EXCLUDE_FILE(*b_mod?.o) is not needed.

I realise the asm code is crap, it was pointed out previously, but it came from ST and I never got around to changing it.
No worries.  It's only going to be run once, so it's nothing to worry about.

For the "2nd lump" I have
Hmm.  Adapting to my above suggestion, memcpy32(&_sram_nonboot_data_start, &_flash_nonboot_data_start, (unsigned int)(&_sram_nonboot_data_stop - &_sram_nonboot_data_start)/4) should work, which basically matches the memcpy().

Just to check (ld basic concepts), virtual memory addresses (obtained by referencing . or using ADDR(section)) are what the code sees at run time, i.e. RAM addresses; and load memory addresses are the output section addresses as specified by AT, i.e. FLASH addresses.

If you use the Thumb instruction set on Cortex-M4 or -M7, then
Code: [Select]
memcpy32:
    cmp  r2, #0
    ble  .L1
    add  r3, r1, r2, lsl #2
    add  r0, r0, r2, lsl #2
.L3:
    ldr  r2, [r3, #-4]!
    cmp  r3, r1
    str  r2, [r0, #-4]!
    bne  .L3
.L1:
    bx   lr
should work pretty well; it is the assembly equivalent of
Code: [Select]
void memcpy32(unsigned int *dst,
              const unsigned int *src,
              int n) {
    while (n-->0)
        dst[n] = src[n];
}
when the compiler doesn't decide to replace it with memcpy() or memmove().

You can compile assembly to an object file using gcc or g++; it will detect the language correctly based on filename suffix .s.
See arm-none-eabi-gcc-11.2.1 -O2 -march=armv7e-m -mtune=cortex-m7 -mthumb at Compiler Explorer for the above.

What I would actually do, is use the following in my C sources:
Code: [Select]
// Copy 'n' 32-bit words starting at 'src' to 'dst'.
// 'dst' and 'src' must be properly aligned.
__attribute__((used))
void memcpy32(unsigned int *dst,
              const unsigned int *src,
              int n) {
    if (n > 0)
        asm volatile ( "\n"
                       ".Lmemcpy32b_loop:\n\t"
                       "ldr r2, [%[src]], #4\n\t"
                       "str r2, [%[dst]], #4\n\t"
                       "cmp %[src], %[end]\n\t"
                       "blt .Lmemcpy32b_loop\n\t"
                     :
                     : [dst] "r" (dst),
                       [src] "r" (src),
                       [end] "r" (src + n)
                     : "r2", "memory" );
}

I also wonder why use data and data* and common COMMON common* but not COMMON*.
Section names are case sensitive AFAIK, so common and COMMON are different sections.

COMMON is a special section name for common symbols.
« Last Edit: March 12, 2023, 12:39:59 pm by Nominal Animal »
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3697
  • Country: gb
  • Doing electronics since the 1960s...
Re: A question on GCC .ld linker script syntax
« Reply #4 on: March 12, 2023, 11:42:15 am »
Quote
However, .all_nonboot_data would contain a copy of the above

I find that really surprising because the linker scripts I have seen appear to assume that once stuff from one .o file has been collected into a named section (e.g. FLASH_APP) then it is gone.

Quote
*b_mod?.o (.data .data*)

Can one use b_*.o to collect all b_*.o files? That isn't a proper regexp, hence I am not sure. The actual filenames I didn't post but they all start with b_.

Quote
That is still subject to gc by the linker, so do also consider KEEP

I do have a KEEP on all code i.e. TEXT. That was always there. Is it needed for DATA (i.e. nonzero-initialised variables?

KEEP (*b_*.o) *b_*.o (.data .data*) doesn't work. By inspection of similar code, it needs to be KEEP (*b_*.o (.data .data*)).

Getting there, code running as it should be, but I am trying to also understand it :)


« Last Edit: March 12, 2023, 12:20:09 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: A question on GCC .ld linker script syntax
« Reply #5 on: March 12, 2023, 12:37:46 pm »
I find that really surprising because the linker scripts I have seen appear to assume that once stuff from one .o file has been collected into a named section (e.g. FLASH_APP) then it is gone.
Hm.  Waitasecond.  Indeed, you're right.  :-[

Quote
*b_mod?.o (.data .data*)
Can one use b_*.o to collect all b_*.o files? That isn't a proper regexp, hence I am not sure. The actual filenames I didn't post but they all start with b_.
Yes.  These are glob-style wildcard patterns, not regexes.

KEEP (*b_*.o) *b_*.o (.data .data*) doesn't work. By inspection of similar code, it needs to be KEEP (*b_*.o (.data .data*)).
Right,  :-[ I should've checked.

It really isn't my day today, it seems.

Right now I am getting weird issues with invalid opcodes (0xffffffff) inserted between C source lines.
Section alignment?

In the output (text) sections, try adding FILL(0xBF00BF00), or add =0xBF00BF00 output section attribute (before > or AT>).  0xBF00 is the Thumb encoding for NOP.  This means that when .text sections from different object files are concatenated, the alignment (per the object file section alignment) is done with 0xBF00BF00 (NOP; NOP) instead of 0xFFFFFFFF.
« Last Edit: March 12, 2023, 12:41:02 pm by Nominal Animal »
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3697
  • Country: gb
  • Doing electronics since the 1960s...
Re: A question on GCC .ld linker script syntax
« Reply #6 on: March 12, 2023, 02:18:25 pm »
Interestingly, if I uncomment the 2nd KEEP below, I get loads of undefined symbol linker errors

Code: [Select]

/* Initialized data sections for boot block. These go into RAM, load LMA copy after code */
/* This stuff is copied from FLASH to RAM by the startupxxx.s code */
.boot_data :
  {
    . = ALIGN(4);
    _s_boot_data = .;        /* create a global symbol at data start */
    KEEP (*b_*.o (.data .data*))
    *b_*.o (.data .data*)      /* DATA sections */
      . = ALIGN(4);
    _e_boot_data = .;        /* define a global symbol at data end */
  } >RAM  AT >FLASH_BOOT

  /* used by the startup .s code to initialize data */
  _si_boot_data = LOADADDR(.boot_data);
 
 
/* Initialized data sections for rest of unit. These go into RAM, load LMA copy after code */
/* This stuff is copied from FLASH to RAM by C code in the main stub */
.all_nonboot_data :
  {
    . = ALIGN(4);
    _s_nonboot_data = .;        /* create a global symbol at data start */
    /*KEEP (*(.data .data*))*/
    *(.data .data*)      /* .data sections */
      . = ALIGN(4);
    _e_nonboot_data = .;        /* define a global symbol at data end */
  } >RAM  AT >FLASH_APP

  /* used by the main stub C code to initialize data */
  _si_nonboot_data = LOADADDR(.all_nonboot_data);

Looking at other KEEP usage, it seems to be used universally for code (TEXT) and ambiguously for other stuff.

The b_*.o etc wildcards do work but you have to be extremely careful with them because if you have a module containing any b_* function inside, and you forgot to include that module earlier (due to e.g. a typo in its name), the linker will pick out the b_*() functions out of it and will dump them somewhere, anywhere, in the remaining code. Just spent a few hours chasing what was basically a typo, but the whole thing compiled with no errors because the linker found those b_*() functions. The .map file eventually revealed boot block functions at addresses outside the boot block.

It's obvious to me why the absolutely crap .ld file syntax is acceptable to people. They waste their life on the linker script precisely once and never again.
« Last Edit: March 13, 2023, 05:24:21 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline abyrvalg

  • Frequent Contributor
  • **
  • Posts: 824
  • Country: es
Re: A question on GCC .ld linker script syntax
« Reply #7 on: March 13, 2023, 12:50:40 am »
Linker is designed to link and deduplicate, why blame it for not being tailored for the opposite task again and again? The intended way of performing your two call graphs separation task is as simple as invoking the linker two times - one for the bootloader, another one for the main app, that would guarantee bootloader isolation automatically, without ever looking into .map.
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: A question on GCC .ld linker script syntax
« Reply #8 on: March 13, 2023, 02:21:30 am »
Linker is designed to link and deduplicate, why blame it for not being tailored for the opposite task again and again? The intended way of performing your two call graphs separation task is as simple as invoking the linker two times - one for the bootloader, another one for the main app, that would guarantee bootloader isolation automatically, without ever looking into .map.
I must say I agree.  I do not understand why you insist on combining both the bootloader and the normal code in the same target binary.

If the bootloader part provides symbols useful to the application part, perhaps functions like memcpy32(), you can always pick their addresses from the target binary (using objdump and a bit of scripting) or from the .map file, and then declare them in a header file and define in the linker script.  It is easily automated using bash/sed/awk or python or a number of other scripting tools; just make sure you use export LANG=C LC_ALL=C so all tools use the default character set.  I do such stuff (multi-stage builds with Makefiles running external helper scripts) all the time, and it just makes the builds easier to control, and more robust.  More steps, yes, but fewer inter-dependencies.  That's also why I'm so rusty with the linker script syntax: I rarely need to do the weird stuff, so I always need to consult the LD manual!

I mean, Teensy 4.0 linker script is 109 lines, Teensy 4.1 linker script is 118 lines, and MicroMod Teensy linker script is 109 lines.  The NXP i.MX RT1062 processor in it implements a very similar Cortex-M7 (with hardware single- and double-precision floating point).
Of course, that is just the application part, because the bootloader on these Teensies is actually on a separate NXP MKL02 chip in control of the RT1062, and is not field-updateable.  In practice, it can be considered a pre-set JTAG loader or something like that.  But it shows how simple typical linker scripts are.
(In case you wonder, 4.1 supports one or two 8 Mbyte PSRAM chips, which are mapped at ERAM if soldered in.  It is detected by startup code in the Teensyduino core at runtime.)

For each bootloader version, application developers would have a corresponding header file (in addition to the CMSIS headers or whatever you provide for the board functionality), describing the additional interfaces the bootloader provides; plus the linker script to be used for the application part (and defining the addresses of e.g. function symbols accessible to the application part).
To target different bootloader versions, application developers would build their code against each header file and linker script.

Do note that we're only telling you how we'd do things, exactly because doing them the way you are doing now leads to the kinds of problems (and much increased complexity) you are having now; and not telling you what to do.
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8172
  • Country: fi
Re: A question on GCC .ld linker script syntax
« Reply #9 on: March 13, 2023, 06:11:53 am »
I do not understand why you insist on combining both the bootloader and the normal code in the same target binary.

Even if you want the bootloader and application in same binary, you don't need to use linker for that.

Keep bootloader as a completely separate project and just merge the binaries before programming, for example this is what I use lately:
Code: [Select]
srec_cat ../bootloader/bootloader.hex -intel fw_a.hex -intel meta_a.hex -intel -o whole.hex -intel
 
The following users thanked this post: Nominal Animal

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3697
  • Country: gb
  • Doing electronics since the 1960s...
Re: A question on GCC .ld linker script syntax
« Reply #10 on: March 13, 2023, 07:25:47 am »
I am using two separate linkfiles. One makes one project (including the boot loader) and the other makes one project (which runs from base+32k).

This issue is nothing to do with that. It is to do with LD file syntax and how the flow operates along the file.
« Last Edit: March 13, 2023, 07:37:47 am by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: A question on GCC .ld linker script syntax
« Reply #11 on: March 13, 2023, 08:43:10 am »
It is to do with LD file syntax and how the flow operates along the file.
Your link files are much more complicated than I'm used to seeing.

Take a gander at the link files I linked to in my last message.  The linked ones are for a very comparable NXP i.MX RT1062 Cortex-M7 processor, and they're all (one per variant) less than 120 lines long, even though they are used from the Arduino environment (which has all those wonkinesses wrt. strings being copied from Flash to RAM because some code expects them to be mutable because they are on AVRs because of the lack of address space support in g++).

As to the operation of the link scripts, they remind me a bit of awk.  Not syntactically, but logically; how the selection/filtering works, with awk input records corresponding to source sections to be processed by a linker script.  I can definitely see similarities, so perhaps playing with awk a bit might help?  :-//
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3697
  • Country: gb
  • Doing electronics since the 1960s...
Re: A question on GCC .ld linker script syntax
« Reply #12 on: March 13, 2023, 09:09:27 am »
Maybe that's because I am trying to load specific modules in specific places.

If you are just building a single monolithic project then the linkfile can be very simple.

The linkfile which builds the combined (boot+rest) project could have been monolithic but without a "boot block" section you don't get a brick recovery option.

This is too complicated to explain without a lot of detail but I am grateful for the help so far. It seems to be running OK. In fact it's been running fine for 2+ years, 24/7, on several boards, until I did some changes and then I had to revisit a load of stuff :)
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3697
  • Country: gb
  • Doing electronics since the 1960s...
Re: A question on GCC .ld linker script syntax
« Reply #13 on: March 13, 2023, 11:30:56 am »
I have one more Q:

Does the label "code_constants_etc" mean anything here:

Code: [Select]

/* This collects all other stuff, which gets loaded into FLASH after main.o above */
  .code_constants_etc :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
*(.eh_frame)

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _e_code_constants_etc = .;        /* define a global symbol at end of code */
} >FLASH_APP

In other places I have seen "text" used but obviously not just TEXT (executable code) is being collected into FLASH_APP here...

Excessive complexity was mentioned. This is an example of an ex-ST Cube bit which nobody I ever found knew anything about, except that it refers to C++.

Code: [Select]
/* this is for __libc_init_array() */
/* Not sure it is actually used for anything */

.preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  } >FLASH_APP
  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
  } >FLASH_APP
  .fini_array :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(.fini_array*))
    KEEP (*(SORT(.fini_array.*)))
    PROVIDE_HIDDEN (__fini_array_end = .);
  } >FLASH_APP
« Last Edit: March 13, 2023, 05:24:07 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline abyrvalg

  • Frequent Contributor
  • **
  • Posts: 824
  • Country: es
Re: A question on GCC .ld linker script syntax
« Reply #14 on: March 13, 2023, 12:55:32 pm »
Section names like your .code_constants_etc are just section names in output ELF file, nothing relevant for final bin/hex (unless you are doing some more complicated ELF-to-something conversion).

*init_array* stuff is for calling static initializers (functions declared with __attribute__((constructor)), C++ constructors of static objects) from the startup code. They are arrays of function pointers and a full-featured startup code (not ST's half-baked one) iterates through these arrays, calling each function before entering main(). An example:
Code: [Select]
#include <stdio.h>

static int a=5;

//static void before_main() //normal function
static void __attribute__((constructor)) before_main() //executed before main
{
    a = 9;
}

int main(void)
{
    printf("a=%d\n", a);
    return 0;
}
would print "a=9"

> without a "boot block" section you don't get a brick recovery option.
What we are trying to say is this (exactly this, creating an independent recovery boot block) could be done with much less effort just by linking it separately. You've spent lots of time tracking down and separating every function called from the boot block code path I guess, and yet all this can be broken easily by a minor change and needs to be verified again and again.
I've seen several quite funny security vulnerabilities rooting from similar approach - a signed "secure boot block" (verified by BootROM) built together with the "app" it supposed to verify before running, was calling some lib function from that not-verified-yet code :)
« Last Edit: March 13, 2023, 01:12:38 pm by abyrvalg »
 
The following users thanked this post: peter-h

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3697
  • Country: gb
  • Doing electronics since the 1960s...
Re: A question on GCC .ld linker script syntax
« Reply #15 on: March 13, 2023, 02:46:24 pm »
Partly the reason I work the way I do is because I run my business on a very long term basis, the buck stops with me totally, I have frequently needed to revisit projects 10 or more years later, and Cube is immensely complicated (to me, not to you experts who I think mostly do this in company time) so I try hard to use it in a simple way which is easy to set up and document.

I know one could set up two projects in Cube, one for say a boot block and the other for the rest, but when I was doing this kind of stuff I found really nasty cases of file references pointing to files to another project, etc. This was an absolute bastard to track down.

I suppose one could have two separate Cube installs but Cube doesn't like that. The 2nd one would probably want to be in a VM - which is exactly what I am doing now actually but for other reasons.

So those constructors are of no relevance to a C project?
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 3719
  • Country: us
Re: A question on GCC .ld linker script syntax
« Reply #16 on: March 13, 2023, 03:05:00 pm »
Maybe that's because I am trying to load specific modules in specific places.

If you are just building a single monolithic project then the linkfile can be very simple.

The linkfile which builds the combined (boot+rest) project could have been monolithic but without a "boot block" section you don't get a brick recovery option.

When I did this the two linker scripts were both quite simple: mostly just the vendor link script with a different memory offset.  Instead of sorting the objects in the linker I just used two separate projects with their own list of source files.  The default KEEP options worked fine. I didn't need any special section attributes and the resulting hex files are disjoint and easily merged. 
 

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 3719
  • Country: us
Re: A question on GCC .ld linker script syntax
« Reply #17 on: March 13, 2023, 03:37:58 pm »

I know one could set up two projects in Cube, one for say a boot block and the other for the rest, but when I was doing this kind of stuff I found really nasty cases of file references pointing to files to another project, etc. This was an absolute bastard to track down.

It seems much less trouble than what you are doing.  I used two *completely* separate projects.  Different directories, different main.c, different configuration files, different everything exactly as if they were for two different microcontrollers entirely.  This also means I can reuse the same bootloader for different projects.

I do have a common directory with utility files and such shared by multiple projects.  Getting that set up right did take a bit of work, but it's something that I needed to do anyway to share code between projects.  Once that worked using it with the bootloader was easy.

 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3697
  • Country: gb
  • Doing electronics since the 1960s...
Re: A question on GCC .ld linker script syntax
« Reply #18 on: March 13, 2023, 04:03:07 pm »
I have a funny one which may ring a bell with someone.

In the vector table I have

.word     TIM6_DAC_IRQHandler               /* TIM6 and DAC1&2 underrun errors */

and under it I have

   .weak      TIM6_DAC_IRQHandler
   .thumb_set TIM6_DAC_IRQHandler,Default_Handler

This is standard ST code.

Then the real ISR is


/**
  * @brief This function handles TIM6 update interrupt.
  */
void TIM6_DAC_IRQHandler(void)
{
   TIM6_ISR();      //HAL_TIM_IRQHandler(&htim6);
}

and that is supposed to override the "weak" above.

But it isn't happening. The vector points to Default_Handler instead.

If I comment out the two weak & thumb_set lines, it all works.

There is something in the linker script which is failing to override the weak symbol.

Even explicitly linking in the .o file containing the ISR doesn't help.

Maybe I should just delete all the weak vectors to ISRs which do actually exist. After all, anything hitting the default handler won't do anything useful.

I have two projects, one is ok and the other one exhibits the above behaviour. Of course there are differences and I am working through them. The main one is that the working one has some code in the same .s file (in asm) but in a different section. The nonworking one is just a vector table. Both sections are pulled in by the linker script.

The Q is: what could prevent a weak symbol getting overriden, and it being used instead? Is there an order in which the two sets of symbols are to be linked?

The linker script for the vector module is

Code: [Select]

  .isr_vector2 :
  {
    . = ALIGN(0x200);
    KEEP(*(.isr_vector2.o))
    *isr_vector2.o (.text .text* .rodata .rodata*)
    . = ALIGN(4);
  } >FLASH_APP


and the vector file is

Code: [Select]


   .section  .isr_vector2,"a",%progbits
  .type  g_Vectors2, %object
  .size  g_Vectors2, .-g_Vectors2

  .global  g_Vectors2 /* does nothing */
  .global  Default_Handler /* does nothing */


/* The first two will never get used. This vector table is activated by setting VTOR to point to it
   and a reset will reset VTOR to 0.
   The _estack word should be redundant since SP is already set to top of RAM from b_main, but is
   required because the RTOS fetches it for its internal use.
   The 2nd word is unused because b_main jumps directly to main_stub() at base+32k with a jmp.

*/

g_Vectors2:
  .word  _estack /* in final image, SP is loaded directly before entering main_stub */
  .word  0 /* in final image, this is not used (reset resets VTOR, etc) */
  .word  NMI_Handler
  .word  HardFault_Handler
  .word  MemManage_Handler
  .word  BusFault_Handler
  .word  UsageFault_Handler
  .word  0
  .word  0
  .word  0
  .word  0
  .word  SVC_Handler
  .word  DebugMon_Handler
  .word  0
  .word  PendSV_Handler
  .word  SysTick_Handler

  /* External Interrupts */
  .word     WWDG_IRQHandler                   /* Window WatchDog              */
  .word     PVD_IRQHandler                    /* PVD through EXTI Line detection */
  .word     TAMP_STAMP_IRQHandler             /* Tamper and TimeStamps through the EXTI line */
  .word     RTC_WKUP_IRQHandler               /* RTC Wakeup through the EXTI line */
  .word     FLASH_IRQHandler                  /* FLASH                        */
  .word     RCC_IRQHandler                    /* RCC                          */
  .word     EXTI0_IRQHandler                  /* EXTI Line0                   */
  .word     EXTI1_IRQHandler                  /* EXTI Line1                   */
  .word     EXTI2_IRQHandler                  /* EXTI Line2                   */
  .word     EXTI3_IRQHandler                  /* EXTI Line3                   */
  .word     EXTI4_IRQHandler                  /* EXTI Line4                   */
  .word     DMA1_Stream0_IRQHandler           /* DMA1 Stream 0                */
  .word     DMA1_Stream1_IRQHandler           /* DMA1 Stream 1                */
  .word     DMA1_Stream2_IRQHandler           /* DMA1 Stream 2                */
  .word     DMA1_Stream3_IRQHandler           /* DMA1 Stream 3                */
  .word     DMA1_Stream4_IRQHandler           /* DMA1 Stream 4                */
  .word     DMA1_Stream5_IRQHandler           /* DMA1 Stream 5                */
  .word     DMA1_Stream6_IRQHandler           /* DMA1 Stream 6                */
  .word     ADC_IRQHandler                    /* ADC1, ADC2 and ADC3s         */
  .word     CAN1_TX_IRQHandler                /* CAN1 TX                      */
  .word     CAN1_RX0_IRQHandler               /* CAN1 RX0                     */
  .word     CAN1_RX1_IRQHandler               /* CAN1 RX1                     */
  .word     CAN1_SCE_IRQHandler               /* CAN1 SCE                     */
  .word     EXTI9_5_IRQHandler                /* External Line[9:5]s          */
  .word     TIM1_BRK_TIM9_IRQHandler          /* TIM1 Break and TIM9          */
  .word     TIM1_UP_TIM10_IRQHandler          /* TIM1 Update and TIM10        */
  .word     TIM1_TRG_COM_TIM11_IRQHandler     /* TIM1 Trigger and Commutation and TIM11 */
  .word     TIM1_CC_IRQHandler                /* TIM1 Capture Compare - used by wavegen */
  .word     TIM2_IRQHandler                   /* TIM2                         */
  .word     TIM3_IRQHandler                   /* TIM3                         */
  .word     TIM4_IRQHandler                   /* TIM4                         */
  .word     I2C1_EV_IRQHandler                /* I2C1 Event                   */
  .word     I2C1_ER_IRQHandler                /* I2C1 Error                   */
  .word     I2C2_EV_IRQHandler                /* I2C2 Event                   */
  .word     I2C2_ER_IRQHandler                /* I2C2 Error                   */
  .word     SPI1_IRQHandler                   /* SPI1                         */
  .word     SPI2_IRQHandler                   /* SPI2                         */
  .word     USART1_IRQHandler                 /* USART1                       */
  .word     USART2_IRQHandler                 /* USART2                       */
  .word     USART3_IRQHandler                 /* USART3                       */
  .word     EXTI15_10_IRQHandler              /* External Line[15:10]s        */
  .word     RTC_Alarm_IRQHandler              /* RTC Alarm (A and B) through EXTI Line */
  .word     OTG_FS_WKUP_IRQHandler            /* USB OTG FS Wakeup through EXTI line */
  .word     TIM8_BRK_TIM12_IRQHandler         /* TIM8 Break and TIM12         */
  .word     TIM8_UP_TIM13_IRQHandler          /* TIM8 Update and TIM13        */
  .word     TIM8_TRG_COM_TIM14_IRQHandler     /* TIM8 Trigger and Commutation and TIM14 */
  .word     TIM8_CC_IRQHandler                /* TIM8 Capture Compare         */
  .word     DMA1_Stream7_IRQHandler           /* DMA1 Stream7                 */
  .word     FSMC_IRQHandler                   /* FSMC                         */
  .word     SDIO_IRQHandler                   /* SDIO                         */
  .word     TIM5_IRQHandler                   /* TIM5                         */
  .word     SPI3_IRQHandler                   /* SPI3                         */
  .word     UART4_IRQHandler                  /* UART4                        */
  .word     UART5_IRQHandler                  /* UART5                        */
  .word     TIM6_DAC_IRQHandler               /* TIM6 and DAC1&2 underrun errors */
  .word     TIM7_IRQHandler                   /* TIM7                         */
  .word     DMA2_Stream0_IRQHandler           /* DMA2 Stream 0                */
  .word     DMA2_Stream1_IRQHandler           /* DMA2 Stream 1                */
  .word     DMA2_Stream2_IRQHandler           /* DMA2 Stream 2                */
  .word     DMA2_Stream3_IRQHandler           /* DMA2 Stream 3                */
  .word     DMA2_Stream4_IRQHandler           /* DMA2 Stream 4                */
  .word     ETH_IRQHandler                    /* Ethernet                     */
  .word     ETH_WKUP_IRQHandler               /* Ethernet Wakeup through EXTI line */
  .word     CAN2_TX_IRQHandler                /* CAN2 TX                      */
  .word     CAN2_RX0_IRQHandler               /* CAN2 RX0                     */
  .word     CAN2_RX1_IRQHandler               /* CAN2 RX1                     */
  .word     CAN2_SCE_IRQHandler               /* CAN2 SCE                     */
  .word     OTG_FS_IRQHandler                 /* USB OTG FS                   */
  .word     DMA2_Stream5_IRQHandler           /* DMA2 Stream 5                */
  .word     DMA2_Stream6_IRQHandler           /* DMA2 Stream 6                */
  .word     DMA2_Stream7_IRQHandler           /* DMA2 Stream 7                */
  .word     USART6_IRQHandler                 /* USART6                       */
  .word     I2C3_EV_IRQHandler                /* I2C3 event                   */
  .word     I2C3_ER_IRQHandler                /* I2C3 error                   */
  .word     OTG_HS_EP1_OUT_IRQHandler         /* USB OTG HS End Point 1 Out   */
  .word     OTG_HS_EP1_IN_IRQHandler          /* USB OTG HS End Point 1 In    */
  .word     OTG_HS_WKUP_IRQHandler            /* USB OTG HS Wakeup through EXTI */
  .word     OTG_HS_IRQHandler                 /* USB OTG HS                   */
  .word     DCMI_IRQHandler                   /* DCMI                         */
  .word     0                                 /* CRYP crypto                  */
  .word     HASH_RNG_IRQHandler               /* Hash and Rng                 */
  .word     FPU_IRQHandler                    /* FPU                          */

  .word     main   /* this merely prevents linker removing main() because nothing *calls* it */



/*******************************************************************************
*
* Provide weak aliases for each Exception handler to the Default_Handler.
* As they are weak aliases, any function with the same name will override
* this definition.
*
*******************************************************************************/



   .weak      NMI_Handler
   .thumb_set NMI_Handler,Default_Handler

   .weak      HardFault_Handler
   .thumb_set HardFault_Handler,Default_Handler

   .weak      MemManage_Handler
   .thumb_set MemManage_Handler,Default_Handler

   .weak      BusFault_Handler
   .thumb_set BusFault_Handler,Default_Handler

   .weak      UsageFault_Handler
   .thumb_set UsageFault_Handler,Default_Handler

   .weak      SVC_Handler
   .thumb_set SVC_Handler,Default_Handler

   .weak      DebugMon_Handler
   .thumb_set DebugMon_Handler,Default_Handler

   .weak      PendSV_Handler
   .thumb_set PendSV_Handler,Default_Handler

   .weak      SysTick_Handler
   .thumb_set SysTick_Handler,Default_Handler

   .weak      WWDG_IRQHandler
   .thumb_set WWDG_IRQHandler,Default_Handler

   .weak      PVD_IRQHandler
   .thumb_set PVD_IRQHandler,Default_Handler

   .weak      TAMP_STAMP_IRQHandler
   .thumb_set TAMP_STAMP_IRQHandler,Default_Handler

   .weak      RTC_WKUP_IRQHandler
   .thumb_set RTC_WKUP_IRQHandler,Default_Handler

   .weak      FLASH_IRQHandler
   .thumb_set FLASH_IRQHandler,Default_Handler

   .weak      RCC_IRQHandler
   .thumb_set RCC_IRQHandler,Default_Handler

   .weak      EXTI0_IRQHandler
   .thumb_set EXTI0_IRQHandler,Default_Handler

   .weak      EXTI1_IRQHandler
   .thumb_set EXTI1_IRQHandler,Default_Handler

   .weak      EXTI2_IRQHandler
   .thumb_set EXTI2_IRQHandler,Default_Handler

   .weak      EXTI3_IRQHandler
   .thumb_set EXTI3_IRQHandler,Default_Handler

   .weak      EXTI4_IRQHandler
   .thumb_set EXTI4_IRQHandler,Default_Handler

   .weak      DMA1_Stream0_IRQHandler
   .thumb_set DMA1_Stream0_IRQHandler,Default_Handler

   .weak      DMA1_Stream1_IRQHandler
   .thumb_set DMA1_Stream1_IRQHandler,Default_Handler

   .weak      DMA1_Stream2_IRQHandler
   .thumb_set DMA1_Stream2_IRQHandler,Default_Handler

   .weak      DMA1_Stream3_IRQHandler
   .thumb_set DMA1_Stream3_IRQHandler,Default_Handler

   .weak      DMA1_Stream4_IRQHandler
   .thumb_set DMA1_Stream4_IRQHandler,Default_Handler

   .weak      DMA1_Stream5_IRQHandler
   .thumb_set DMA1_Stream5_IRQHandler,Default_Handler

   .weak      DMA1_Stream6_IRQHandler
   .thumb_set DMA1_Stream6_IRQHandler,Default_Handler

   .weak      ADC_IRQHandler
   .thumb_set ADC_IRQHandler,Default_Handler

   .weak      CAN1_TX_IRQHandler
   .thumb_set CAN1_TX_IRQHandler,Default_Handler

   .weak      CAN1_RX0_IRQHandler
   .thumb_set CAN1_RX0_IRQHandler,Default_Handler

   .weak      CAN1_RX1_IRQHandler
   .thumb_set CAN1_RX1_IRQHandler,Default_Handler

   .weak      CAN1_SCE_IRQHandler
   .thumb_set CAN1_SCE_IRQHandler,Default_Handler

   .weak      EXTI9_5_IRQHandler
   .thumb_set EXTI9_5_IRQHandler,Default_Handler

   .weak      TIM1_BRK_TIM9_IRQHandler
   .thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler

   .weak      TIM1_UP_TIM10_IRQHandler
   .thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler

   .weak      TIM1_TRG_COM_TIM11_IRQHandler
   .thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler

   .weak      TIM1_CC_IRQHandler
   .thumb_set TIM1_CC_IRQHandler,Default_Handler

   .weak      TIM2_IRQHandler
   .thumb_set TIM2_IRQHandler,Default_Handler

   .weak      TIM3_IRQHandler
   .thumb_set TIM3_IRQHandler,Default_Handler

   .weak      TIM4_IRQHandler
   .thumb_set TIM4_IRQHandler,Default_Handler

   .weak      I2C1_EV_IRQHandler
   .thumb_set I2C1_EV_IRQHandler,Default_Handler

   .weak      I2C1_ER_IRQHandler
   .thumb_set I2C1_ER_IRQHandler,Default_Handler

   .weak      I2C2_EV_IRQHandler
   .thumb_set I2C2_EV_IRQHandler,Default_Handler

   .weak      I2C2_ER_IRQHandler
   .thumb_set I2C2_ER_IRQHandler,Default_Handler

   .weak      SPI1_IRQHandler
   .thumb_set SPI1_IRQHandler,Default_Handler

   .weak      SPI2_IRQHandler
   .thumb_set SPI2_IRQHandler,Default_Handler

   .weak      USART1_IRQHandler
   .thumb_set USART1_IRQHandler,Default_Handler

   .weak      USART2_IRQHandler
   .thumb_set USART2_IRQHandler,Default_Handler

   .weak      USART3_IRQHandler
   .thumb_set USART3_IRQHandler,Default_Handler

   .weak      EXTI15_10_IRQHandler
   .thumb_set EXTI15_10_IRQHandler,Default_Handler

   .weak      RTC_Alarm_IRQHandler
   .thumb_set RTC_Alarm_IRQHandler,Default_Handler

   .weak      OTG_FS_WKUP_IRQHandler
   .thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler

   .weak      TIM8_BRK_TIM12_IRQHandler
   .thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler

   .weak      TIM8_UP_TIM13_IRQHandler
   .thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler

   .weak      TIM8_TRG_COM_TIM14_IRQHandler
   .thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler

   .weak      TIM8_CC_IRQHandler
   .thumb_set TIM8_CC_IRQHandler,Default_Handler

   .weak      DMA1_Stream7_IRQHandler
   .thumb_set DMA1_Stream7_IRQHandler,Default_Handler

   .weak      FSMC_IRQHandler
   .thumb_set FSMC_IRQHandler,Default_Handler

   .weak      SDIO_IRQHandler
   .thumb_set SDIO_IRQHandler,Default_Handler

   .weak      TIM5_IRQHandler
   .thumb_set TIM5_IRQHandler,Default_Handler

   .weak      SPI3_IRQHandler
   .thumb_set SPI3_IRQHandler,Default_Handler

   .weak      UART4_IRQHandler
   .thumb_set UART4_IRQHandler,Default_Handler

   .weak      UART5_IRQHandler
   .thumb_set UART5_IRQHandler,Default_Handler

   .weak      TIM6_DAC_IRQHandler
   .thumb_set TIM6_DAC_IRQHandler,Default_Handler


   .weak      TIM7_IRQHandler
   .thumb_set TIM7_IRQHandler,Default_Handler

   .weak      DMA2_Stream0_IRQHandler
   .thumb_set DMA2_Stream0_IRQHandler,Default_Handler

   .weak      DMA2_Stream1_IRQHandler
   .thumb_set DMA2_Stream1_IRQHandler,Default_Handler

   .weak      DMA2_Stream2_IRQHandler
   .thumb_set DMA2_Stream2_IRQHandler,Default_Handler

   .weak      DMA2_Stream3_IRQHandler
   .thumb_set DMA2_Stream3_IRQHandler,Default_Handler

   .weak      DMA2_Stream4_IRQHandler
   .thumb_set DMA2_Stream4_IRQHandler,Default_Handler

   .weak      ETH_IRQHandler
   .thumb_set ETH_IRQHandler,Default_Handler

   .weak      ETH_WKUP_IRQHandler
   .thumb_set ETH_WKUP_IRQHandler,Default_Handler

   .weak      CAN2_TX_IRQHandler
   .thumb_set CAN2_TX_IRQHandler,Default_Handler

   .weak      CAN2_RX0_IRQHandler
   .thumb_set CAN2_RX0_IRQHandler,Default_Handler

   .weak      CAN2_RX1_IRQHandler
   .thumb_set CAN2_RX1_IRQHandler,Default_Handler

   .weak      CAN2_SCE_IRQHandler
   .thumb_set CAN2_SCE_IRQHandler,Default_Handler

   .weak      OTG_FS_IRQHandler
   .thumb_set OTG_FS_IRQHandler,Default_Handler

   .weak      DMA2_Stream5_IRQHandler
   .thumb_set DMA2_Stream5_IRQHandler,Default_Handler

   .weak      DMA2_Stream6_IRQHandler
   .thumb_set DMA2_Stream6_IRQHandler,Default_Handler

   .weak      DMA2_Stream7_IRQHandler
   .thumb_set DMA2_Stream7_IRQHandler,Default_Handler

   .weak      USART6_IRQHandler
   .thumb_set USART6_IRQHandler,Default_Handler

   .weak      I2C3_EV_IRQHandler
   .thumb_set I2C3_EV_IRQHandler,Default_Handler

   .weak      I2C3_ER_IRQHandler
   .thumb_set I2C3_ER_IRQHandler,Default_Handler

   .weak      OTG_HS_EP1_OUT_IRQHandler
   .thumb_set OTG_HS_EP1_OUT_IRQHandler,Default_Handler

   .weak      OTG_HS_EP1_IN_IRQHandler
   .thumb_set OTG_HS_EP1_IN_IRQHandler,Default_Handler

   .weak      OTG_HS_WKUP_IRQHandler
   .thumb_set OTG_HS_WKUP_IRQHandler,Default_Handler

   .weak      OTG_HS_IRQHandler
   .thumb_set OTG_HS_IRQHandler,Default_Handler

   .weak      DCMI_IRQHandler
   .thumb_set DCMI_IRQHandler,Default_Handler

   .weak      HASH_RNG_IRQHandler
   .thumb_set HASH_RNG_IRQHandler,Default_Handler

   .weak      FPU_IRQHandler
   .thumb_set FPU_IRQHandler,Default_Handler


/**
 * @brief  This is the code that gets called when the processor receives an
 *         unexpected interrupt.  This simply enters an infinite loop, preserving
 *         the system state for examination by a debugger.
 * @param  None
 * @retval None
*/


       .section  .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
  b  Infinite_Loop
  .size  Default_Handler, .-Default_Handler


The linkfile section which is supposed to collect all other code is

Code: [Select]

/* This collects all other stuff, which gets loaded into FLASH after KDE_main.o above */
  .code_constants_etc :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
*(.eh_frame)


    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _e_code_constants_etc = .;        /* define a global symbol at end of code */
} >FLASH_APP


and this does work, but fails to override the weak symbols earlier.

Another clue is that out of ~500k of code I have only a few k left. So the linker is removing a load of stuff. I've had this before. I had a main() to which I was jumping with a non-obvious asm jump so it was never called from C. I solved that one with .word main at the end of the vector table (an unused location).

Maybe there is something extra which needs to be done to get the entry point to be 0x8008000 and not 0x8000000. However I could not get the ENTRY linkfile bit to work. ST GCC doesn't support the various formats online e.g. ENTRY(0x8008000). It needs to be a symbol but only a special class of symbols, not just any old label that follows.
« Last Edit: March 13, 2023, 07:11:05 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline eutectique

  • Frequent Contributor
  • **
  • Posts: 391
  • Country: be
Re: A question on GCC .ld linker script syntax
« Reply #19 on: March 13, 2023, 10:23:29 pm »
Maybe there is something extra which needs to be done to get the entry point to be 0x8008000 and not 0x8000000. However I could not get the ENTRY linkfile bit to work. ST GCC doesn't support the various formats online e.g. ENTRY(0x8008000). It needs to be a symbol but only a special class of symbols, not just any old label that follows.

ENTRY is needed for the ELF loader -- literally, the piece of code that loads ELF file into RAM and jumps to the symbol specified in ENTRY. For your needs it is useless.

Cortex-M CPU, when it gets out of reset, reads PC from the second word of flash, at address 0x8000000 in your case. You can not bend the CPU to jump anywhere else, no matter how hard you try.

Edit: first word is SP, of course.
« Last Edit: March 13, 2023, 10:30:26 pm by eutectique »
 
The following users thanked this post: peter-h

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3697
  • Country: gb
  • Doing electronics since the 1960s...
Re: A question on GCC .ld linker script syntax
« Reply #20 on: March 13, 2023, 10:29:03 pm »
Just so you all can have a laugh, spot the difference between these two. The 1st one is right

Code: [Select]
  .isr_vector2 :
  {
    . = ALIGN(0x200);
    KEEP(*(.isr_vector2))
    *isr_vector2.o (.text .text* .rodata .rodata*)
    . = ALIGN(4);
  } >FLASH_APP

  .isr_vector2 :
  {
    . = ALIGN(0x200);
    KEEP(*(.isr_vector2.o))
    *isr_vector2.o (.text .text* .rodata .rodata*)
    . = ALIGN(4);
  } >FLASH_APP

There is a load of examples out there where they use the KEEP(*(.modulename.o)). With the .o. You know why it works? Because 99% of the time there is nothing to KEEP. The f-ing stupid linkfile syntax has almost no warnings or error checking. It works because unless you are writing complete crap, there isn't any unreachable code that you need. But this breaks with vector tables and such, because the stuff isn't pointed to by anything. So the linker strips it.

And the ".isr_vector2 :" label at the top is just pure BS. It could be "fred". So why do people write linkfiles with this ambiguity all over the place? Like ".text.isr_vector2 :" which is even more BS; the leading "text" is not related to the TEXT segment.

I am still working on the weak symbols not getting overriden...

Quote
ENTRY is needed for the ELF loader -- literally, the piece of code that loads ELF file into RAM and jumps to the symbol specified in ENTRY. For your needs it is useless.

Thanks :)

Quote
Cortex-M CPU, when it gets out of reset, reads PC from the first word of flash, at address 0x8000000 in your case.

The 2nd word actually. The 1st word goes into SP.

The 0x8008000 stuff is an "overlay" which is entered (from fully set up and running code, obviously) with a jmp. So no reset is involved. I also load SP directly before the jmp. That part works fine.

I agree there would be no way to enter an overlay at 0x8008000 via a reset.
« Last Edit: March 13, 2023, 10:31:35 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline eutectique

  • Frequent Contributor
  • **
  • Posts: 391
  • Country: be
Re: A question on GCC .ld linker script syntax
« Reply #21 on: March 13, 2023, 10:43:33 pm »
Quote
Cortex-M CPU, when it gets out of reset, reads PC from the first word of flash, at address 0x8000000 in your case.

The 2nd word actually. The 1st word goes into SP.

Yes, silly me, corrected the reply 3 seconds after hitting send.


Quote from: peter-h
The 0x8008000 stuff is an "overlay" which is entered (from fully set up and running code, obviously) with a jmp. So no reset is involved. I also load SP directly before the jmp. That part works fine.

The usual way of doing this is

Code: [Select]
void jump_to(uint32_t pc, uint32_t sp) __attribute__((naked)) {
    __asm volatile("  msr msp, r1    \n"
                   "  bx  r0         \n");
}

I am sure you are using something similar. Don't forget to set pc to odd value.
 
The following users thanked this post: peter-h

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 826
Re: A question on GCC .ld linker script syntax
« Reply #22 on: March 13, 2023, 10:52:13 pm »
Quote
And the ".isr_vector2 :" label at the top is just pure BS. It could be "fred".
When you are looking at your section info from something like objdump (size, vma, lma, etc.), it is probably not going to be very helpful if your section names are named after people.
 
The following users thanked this post: peter-h

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 3697
  • Country: gb
  • Doing electronics since the 1960s...
Re: A question on GCC .ld linker script syntax
« Reply #23 on: March 13, 2023, 10:56:24 pm »
Yes:

Code: [Select]

extern char code_base;         // This is base+32k
uint32_t jmpaddr = (uint32_t)&code_base;                 // Weird stuff to get a usable value
jmpaddr |= 1; // Bit 0 = 1 for thumb code
asm("bx %0"::"r" (jmpaddr)); // jmp (bx) etc
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 826
Re: A question on GCC .ld linker script syntax
« Reply #24 on: March 13, 2023, 11:26:13 pm »
No assembly required-
https://godbolt.org/z/PsnGff93o
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf