Force gcc linker to skip a block of flash

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

I'm not sure if the title makes sense, or if this forum is the right place... but here goes!

 

I know this makes no logical sense, but it is an "opportunity" nevertheless.

Given a C program (ATmega328P, Atmel Studio)

 

Assume there is a block of the flash that "MUST" be located at a specific address.
(Don't ask why! Not my requirement, but the client's...)

 

I can add the following to the Project Properties / Linker / Miscellaneous
    -Wl,--section-start=.fixTable=0x2000
to locate the table where it must be placed.

 

And then define the region after the table using something like:
    -Wl,--section-start=.afterTable=0x2400

And place functions in this region after the table as:

__attribute__ ((used, section(".afterTable")))
void Function01(void) {
}

__attribute__ ((used, section(".afterTable")))
uint8_t Function02(uint8_t f2_data) {
}

Questions:
 A) Is there another way to have the linker skip the fixed table section of the flash?
 B) Is there a way to define that all functions in a file should be placed after the fixed table?
      (Without doing the attribute before each function)
 C) Is there a "better" way to do this? (Yes, I know eliminate the fixed table requirement!)
 

David

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

I'm not an expert but since nobody else have answered:

 

I would take a look of the memory model of the chip and tell the compiler/linker that you have memory from 0x0000 to 0x1ffff and from 0x2400 to <end> 

There should be a way to do that.

 

And then add the table later.

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

Is this block AFTER all the other stuff in the flash or in the MIDDLE?

 

When you say "skip" to me that sounds like "in the middle". In that case you aren't going to achieve this with -section-start's alone. The linker will issue "section .xxx overlaps section .yyy" warnings if you attempt that. The only way to insert something in the "middle" is to add an entry for it in the linker script. In that case you take the avrN.x file that is being used from the .../ldscripts directory and make a local copy, modify it then use -Wl,-T,modifiedavrN.x to tell the linker to now use it.

 

frog_jr wrote:
A) Is there another way to have the linker skip the fixed table section of the flash?

You see there is that "skip" word again. So this table is "in the middle"?

frog_jr wrote:
B) Is there a way to define that all functions in a file should be placed after the fixed table? (Without doing the attribute before each function)

Not really - you see this is what happens when you try to share the responsibility of setting the layout with the linker. The linker has it's own ideas about where it would like to place things both from the .x file and the section-start's. As soon as you start placing things manually you "get in its way". The best solution is to "work with it" and that means modifying its "core recipe" for the layout which is the linker script in the .x file.

frog_jr wrote:
C) Is there a "better" way to do this? (Yes, I know eliminate the fixed table requirement!)

I know the "customer is always right" but the only possible reason I can think why an AVR would need such a data table at a fixed address is because at some stage in the future a new copy will be delivered and SPM code in the bootloader section will write the new version to this fixed address. But WHY does the address have to be fixed? If it really does the common solution is to put such data at the END of flash - out of the way of what the linker is positioning. Your only requirement then is that the linker placed stuff does not grow and bump into your fixed data up at the top end of the flash.

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

clawson wrote:
Is this block AFTER all the other stuff in the flash or in the MIDDLE?

It is in the middle.

The original code was done with the block after all other code. There is now a need to add functionality which will need to utilize flash after the block.

 

I have never looked at the linker scripts previously, so this is my "opportunity"!

 

I have looked at the ldscripts directory filled with avrN.x (as well as avrN.xbn, avrN.xn, avrN.xr, & avrN.xu) files. In reviewing the makefile (generated by Atmel Studio for the project, ATmega328P), I have thus far found no info as to which of those avrN.x files is being used (I suspect avr2.x, avr4.x or avr25x).

 

And in looking at the files I am not sure how I would specify an interrupted text section.

So, how do determine which of those .x files to use as a basis for the local copy, and

 how do I specify the changes to make a region unusable by the linker.

 

(Pointers to tutorials on this welcome!)

David

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

frog_jr wrote:
I have looked at the ldscripts directory filled with avrN.x (as well as avrN.xbn, avrN.xn, avrN.xr, & avrN.xu) files. In reviewing the makefile (generated by Atmel Studio for the project, ATmega328P), I have thus far found no info as to which of those avrN.x files is being used (I suspect avr2.x, avr4.x or avr25x)

OK I have typed this same post a few times in my life but here goes again. First about the .xbn's and so on. read this:

 

https://www.sourceware.org/ml/cr...

 

Bottom line. For the purposes of AVR ignore everything but the plain avrN.x file. While the others are created as part of the AVR toolchain build they are never used for AVR.

 

So then it's just a question of which one is used for any particular AVR. There are various ways to determine this. Perhaps easiest is to build a project and have a .map file generated then take a look at it:

$ avr-gcc -mmcu=atmega16 -Wl,-Map,avr.map avr.c -o avr.elf
$ grep crt avr.map
                              /usr/lib/gcc/avr/4.5.3/../../../avr/lib/avr5/crtm16.o (exit)
LOAD /usr/lib/gcc/avr/4.5.3/../../../avr/lib/avr5/crtm16.o
 .vectors       0x0000000000000000       0x54 /usr/lib/gcc/avr/4.5.3/../../../avr/lib/avr5/crtm16.o
 .init0         0x0000000000000054        0x0 /usr/lib/gcc/avr/4.5.3/../../../avr/lib/avr5/crtm16.o
 .init2         0x0000000000000054        0xc /usr/lib/gcc/avr/4.5.3/../../../avr/lib/avr5/crtm16.o
 .init9         0x0000000000000060        0x8 /usr/lib/gcc/avr/4.5.3/../../../avr/lib/avr5/crtm16.o
 .text          0x0000000000000068        0x4 /usr/lib/gcc/avr/4.5.3/../../../avr/lib/avr5/crtm16.o
 .data          0x0000000000800060        0x0 /usr/lib/gcc/avr/4.5.3/../../../avr/lib/avr5/crtm16.o
 .bss           0x0000000000800060        0x0 /usr/lib/gcc/avr/4.5.3/../../../avr/lib/avr5/crtm16.o
 .stab          0x0000000000000000      0x6cc /usr/lib/gcc/avr/4.5.3/../../../avr/lib/avr5/crtm16.o
 .stabstr       0x0000000000000000       0x54 /usr/lib/gcc/avr/4.5.3/../../../avr/lib/avr5/crtm16.o

As you can see this file is telling me in several places that atmega16 that I built the code for is in the AVR5 group.

 

Another way to determine that is to give in and read the user manual:

 

http://www.nongnu.org/avr-libc/u...

 

If I search "atmega16" on that page I find:

 

avr5 atmega16 __AVR_ATmega16__

 

An alternative way to approach that is to go to the horses mouth (where that same entry came from I'm guessing) and look at the source code of the C compiler. Specifically:

 

https://gcc.gnu.org/viewcvs/gcc/...

 

Within that you will find line 148:

 

https://gcc.gnu.org/viewcvs/gcc/...

 

which tells you that mega16 is "ARCH_AVR5".

 

So all that tells you that a particular AVR is "AVR5". Thus in the ldscripts directory knowing it is just the plain.x file and avr5 you can put the two together to know that for mega16 the linker script used is avr5.x:

/usr/lib/ldscripts$ head avr5.x
/* Default linker script, for normal executables */
OUTPUT_FORMAT("elf32-avr","elf32-avr","elf32-avr")
OUTPUT_ARCH(avr:5)
MEMORY
{
  text   (rx)   : ORIGIN = 0, LENGTH = 128K
  data   (rw!x) : ORIGIN = 0x800060, LENGTH = 0xffa0
  eeprom (rw!x) : ORIGIN = 0x810000, LENGTH = 64K
  fuse      (rw!x) : ORIGIN = 0x820000, LENGTH = 1K
  lock      (rw!x) : ORIGIN = 0x830000, LENGTH = 1K
etc.

As to how you can add a section in the "middle". The fact is that you need to interrupt:

...
    *(.init9)  /* Call main().  */
    KEEP (*(.init9))
    *(.text)
    . = ALIGN(2);
    *(.text.*)
    . = ALIGN(2);
    *(.fini9)  /* _exit() starts here.  */
    KEEP (*(.fini9))
...

It's that bold line that collects together all the "normal" code you write (it's all in some .text.* section) and plonks it in the middle of the flash image here. What I guess you need to do is read the manual:

 

https://sourceware.org/binutils/...

 

and find some way to put a limit on .text so it won't impinge on the reason you are trying to protect but I don't immediately see an obvious way to actually split it but then again, I've wanted to do this and somewhere in that manual (or perhaps on stackoverlfow?) you may find some mechanism that can be used to split a section.

 

I just tried a Google for "split a section in gcc linker script" and the two top hits were (stackoverflow of course!):

 

http://stackoverflow.com/questio...

http://stackoverflow.com/questio...

 

The great thing about using the GNU tools is that the way you'd do this would be the same for AVR, ARM, x86 or any of the GCC targets so any answers you find don't have to be about AVR specifically. But looking at those I'm not seeing someone jumping up saying "it's easy you just do X, Y and Z". So I fear that while you can insert a section into the middle of the linker placed stuff using the linker script it may not be that easy to actually SPLIT one section. So you may need a sort of ".code2" to which you assign some of your code that will go above the fixed table so you still need the __attribute__ on some functions. But at least this will work easier than trying to use -section-start's.

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

Thanks clawson, I have my lesson plan for the day!

 Somehow, I had known of some of these links in the past, but my mind just wouldn't go there.

Thanks again for the refresher...

David

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

To do what you want, you need one new section that you can manipulate with --section-start .

Call it .after .

Use the function attribute section(".after") .

.after will be an orphan input section that will become an output section with the same name.

The real issue seems to be how to apply all these attributes with a minimum of changes to the original code.

Make a new header file with the appropriate function declarations.

You make call it sections.h .

Compile with the option -include sections.h .

Use avr-nm to check whether you have enough functions in .after .

Moderation in all things. -- ancient proverb

Last Edited: Wed. May 18, 2016 - 02:02 PM