Getting C information to the linker

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When writing a bootloader, I end up specifying in the linker command line "--section-start=.text=0x7E00" to locate the code in the bootloader section.
But the 0x7E00 part is chip dependent, and what I REALLY want to do is have .text start at (FLASHEND-512), where FLASHEND comes from the the io.h chain of source-code include files.

 

Is there some way to do that?  Perhaps with a custom linker script?  I can't see a way for C source to define the values of the linker variables that the linker scripts use, and I can't see a way for the linker scripts to access values defined in C source files...  (I mean, __TEXT_REGION_LENGTH__ in the avr linker scripts is given a generic value for each "family", rather than an accurate value for the particular chip, which is something I would have expected.)

 

The best I can think of is to dynamically create a custom linker script, based on some sort of dummy program with the same -mmcu option, but that would require some sort of native programming language that understands C #include files. and it's gross.

 

This doesn't seem like it should be impossible :-(

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Assuming flashend.c simply includes avr/io.h, you could feed the output of the following avr-gcc invocation (substituting the appropriate chip) to the text processing tool of your choice as part of the build process to extract the value of FLASHEND:

avr-gcc -mmcu=atmega328p -E -dM flashend.c

 

build-avr-gcc: avr-gcc build script

toolchain-avr-gcc: CMake toolchain for cross compiling for the AVR family of microcontrollers

avr-libcpp: C++ standard library partial implementation (C++17 only) for use with avr-gcc/avr-libc

picolibrary: C++ microcontroller driver/utility library targeted for use with resource constrained microcontrollers

picolibrary-microchip-megaavr: picolibrary HIL for megaAVR microcontrollers

picolibrary-microchip-megaavr0: picolibrary HIL for megaAVR 0-series microcontrollers

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am making progress using inline ASM, which will produce symbols that the linker CAN see and apparently use...

 

void pre_main(void) {
asm(" .global __TEXT_REGION_LENGTH__\n"
    " .equ __TEXT_REGION_LENGTH__, (%0+1)\n"
    " .global __BOOT_START__\n"
    " .equ __BOOT_START__, (%0-511)\n"
    ::"i"(FLASHEND));

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Assembler example:
Program data can be stored in a visible place, i.e at the begin (or, perhaps, end) of code.

Here it will be stored at begin, after the Interrupt table, and can be located on Code print.

 

#define zRevision "211027 bdm8i  "

...

.cseg
.org $0000

    rjmp   jqs1RESET      ; 00 RESET      Reset (Reset Handler)
    rjmp   jQPQS_EXT_INT0 ; 01 EXT_INT0   External Interrupt Request 0 (IRQ0 Handler)
 / other Interrupts table /
 
; This data will be printed, when read by JTAGICE 
tRevision:  .db  zRevision

jqs1RESET:  
    ldi    r16, high(RAMEND)
    out    SPH, r16
    ldi    r16, low(RAMEND)
    out    SPL, r16
...

 

Edit: Information for humans, though...

Last Edited: Wed. Oct 27, 2021 - 05:49 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The inline ASM is doing nicely, I think.

I have

  asm(" .global __BOOT_SIZE__, __BOOT_START__, __VERSION_START__\n"
      " .equ __BOOT_SIZE__, 1024\n"
      " .equ __BOOT_START__, (%0-1023)\n"
      " .equ __VERSION_START__, (%0-1)\n"
      ::"i"(FLASHEND));

In the C code, and then a custom linker script with (among other things):

MEMORY
{
  text   (rx)   : ORIGIN = __BOOT_START__, LENGTH = __BOOT_SIZE__
  version   (rx)   : ORIGIN = __VERSION_START__, LENGTH = 2
  fuse      (rw!x) : ORIGIN = 0x820000, LENGTH = __FUSE_REGION_LENGTH__
}

Since the bootloader doesn't use .data or .bss, I've also stripped down the linker script so that it is "generic" across "many" AVR chips, instead of a particular sub-architecture (like AVR6)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw wrote:
Getting C information to the linker

It usually works the other way around - the Linker provides the information to C.

 

eg, the ARM Linker defines symbols which the C code can access:

 

https://developer.arm.com/documentation/dui0474/m/accessing-and-managing-symbols-with-armlink/region-related-symbols

 

I guess GCC would have a similar mechanism ... ?

 

EDIT

 

This: https://mcuoneclipse.com/2016/11/01/getting-the-memory-range-of-sections-with-gnu-linker-files/ ?

 

The trick is that you need to use the address of these symbols - not their values.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Wed. Oct 27, 2021 - 08:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've also stripped down the linker script

 

If you are already using your own linker script, then simply do-

 

text   (rx)   : ORIGIN = __TEXT_REGION_LENGTH__ - 512, LENGTH = 512

 

I'll also imagine vectors are not wanted, so you can also simply comment out the *(.vectors) and/or the KEEP(*(.vectors)) if you wanted startup code to remain (you still get the startup code, just not the vectors). One advantage to doing this even if not using data/bss is you can keep the crt startup code and will notice if you accidentally start using vars that expect to be init (the copy/clear data functions will show up). You then specify the .noinit section for vars that you know do not need initialization. The cost is the code to clear r1 and set the stack (from the crt), but probably is being done 'manually' anyway.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

If you are already using your own linker script, then simply do-

 

text   (rx)   : ORIGIN = __TEXT_REGION_LENGTH__ - 512, LENGTH = 512
 

yeah, except the length isn’t quite constant, depending on other options.

 

and yes, deleting .data and .bss generates nice error message if you try to use them…

 

now to handle the Tony’s that lack bootloader sections, and therefore have bootloader sizes that are a multiple of the page size, rather than a power of 2 times some multiple of 256…

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw wrote:
Tony’s

laugh

 

(that should catch on...)

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When I need to do something interesting with symbols,

I often resort to python.

cpp -imacros fred.h oneprint.py | python - > option.ld
gcc -Wl,@option.ld ...

option.ld  should become a file in the object directory.

fred.h contains, directly or otherwise, the necessary #defines .

oneprint.py, after preprocessing, emits the desired option.

 

-imacros is handy here, but if necessary,

one can work around its absence.

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

This has worked out nicely, and Optiboot no longer contains any references to section-start addresses in any of the Makefiles; instead, it derives this info from the avr/io.h values and some occasionally-messy estimates of bootloader size in the C code, and passes that to a custom linker script via inline asm.  I'm sort-of kicking myself for not figuring this out a long time ago; it would have made the addition of chip support for "many" chips a lot easier...

 

https://github.com/Optiboot/opti...