Using conditional logic with env variable in GCC Linker script - c

I have a build environment variable available, let's say for differentiating 2 hardware variants: HW_VER1
We are using linker script in build.
So, we want something like this. But, of course below gives error "invalid syntax in flags"
MEMORY
{
ifeq ($(HW_VER1),YES)
iram0_0_seg : org = 0x00080400, len = 0x21C00
else
iram0_0_seg : org = 0x00080400, len = 0xf1C00
endif
}
The requirement is not to use 2 linker scripts with the same exact contents, except the len value above, and let build system decide which one based on the env variable (the one with bigger len or smaller len based on the hw ver)

Related

Is it possible to auto split the .text section across mulitple memory areas?

I am currently writting a embedded program for a microcontroller with a splitted flash region. Something like
MEMORY {
flash1 : ORIGIN = 0x1000, LENGTH = 0x1000
/* 1K gap */
flash2 : ORIGIN = 0x3000, LENGTH = 0x1000
}
My application has grown to a point where the ".text" section is larger than the area of flash1.
Is it possible to automatically split the text section accross the two areas?
Something like:
SECTIONS {
.text0 : { *(.text) } > flash1
.text1 : { *(.text) } > flash2
}
If I am doing something like the one above. I get a error message telling me that
ld: a.out section `.text' will not fit in region `flash1'
ld: region `flash1' overflowed by 240 bytes
I know I could do something like:
SECTIONS {
.text0 : { mylargefile (.text) } > flash1
.text1 : { *(.text) } > flash2
}
but I don't have a single large file. So the question is is it possible to tell the linker to fill the first output section until the it is full and afterwards continue with the next section?
Further information:
I do not provide a full example as my actually application is much more complex as I am although using LMA and VMA section and special section for some special files. But this answer should provide me enough help to continue or ask further questions.
I am looking forward for answers. I hope my question is clear enough without a full sample
EDIT1:
The solution of the question Splitting embedded program in multiple parts in memory is not correct in my case as the solution uses wildcards to spilt the files manually. I ask if there is a automatically solution. Otherwise I would be forced to manually adjust the linker file every time I do larger changes on the code base.
EDIT2:
Changed example to make sure there is a gap as remarked by Tom V
Well, some compilers allow in their linkers (and the scripts) to overflow to another section, if the first one is full.
But also these have sometimes problems, that it depends on the order of when the section is full and when it switches, it does not come back to fill the first one up, e.g. you have in the beginning or the middle a very big part. When the linker skips now to the next section due to overflow, the first was maybe just filled to the half. It does not use smaller parts AFTER that big part, to go back to the first sections to fill in.
So, it seems, the GNU LD, the Gold-linker and also the LLVM/CLANG linker (which state at their site, that the linker script is the same as GNU LD, and the exceptions page does not state anything), do not support to overflow into another section.
So, to make it easier, maybe some hints:
Filtering by filename patterns can get tedious over time. And, it does not allow you to filter by symbol, only by filename pattern and input section names.
If you have a approach like AUTOSAR MemMap, you could split the code by placing them into new sections with a different naming than default .text, .rodata, .data, .bss. (Unfortunately, GCC + CLANG went that extra way of using attribute(( )) instead of the #pragma or _Pragma() way of the C-standard). (AUTOSAR MemMap example at the end)
you could split into fast and slow code, e.g. introducing new sections like .codefast, .codeslow (e.g. ISR code as fast, task level code as "slow")
you could split into QM vs ASIL partitions introducing sections like .code_qm, .code_asila, .code_asilb
you could split code and config / const data , where the configuration is put into a separately flashable section, e.g. sections .text, .rodata could be split into .text, .const .postbuildconfig, .calib .. with a partial different coding style, you could here keep the code the same, but project config (e.g. CAN config, filter chains etc) could use a configurable config, where you just flash a new config, without updating the code itself
.text --> flash memory sector 1
.postbuildconfig --> flash memory sector 2
.calib --> flash memory sector 3
Before starting actually to really compile and link, maybe an inbetween build phase could use tools like objdump / nm / size / readelf to first scan over the .o files to give out the object / section sizes and summarize them by a script according to certain criterias e.g. input memory layout and by above special sections or by ordering by size and .o file name patterns, which you could use to update the linker script to adapt the memory definitions. SO, the script could try to fit until the gap as much as fits, then switches to the other memory. It could even try to generate the linker script parts which is later passed to the linker. You could write such a linker preprocess script in perl or python.
Together with the memory mapping to different sections, you could filter now like this:
MEMORY {
// Maybe you have two 128k areas separate by a gap
// Put the code in the first big part, and the more
// "configurable part" in the second area might even
// allow you to flash/update the separately
CODE1 : origin = 0x10000, len = 128k
// maybe here is a gap
CODE2 : origin = 0x10000, len = 64k
CONST : origin = 0x20000, len = 4k
CALIB : origin = 0x40000, len = 4k
POSTBUILD : origin = 0x50000, len = 48k
}
SECTIONS {
.code1 : {
*(.text) // default .text
*(.code_fast) // fast/slow code split
*(.code_qm) // qm/asil code split
} > CODE1
.code2 : {
*(.code_slow) // fast/slow code split
*(.code_asila) // qm/asil code split
*(.code_asilb) // qm/asil code split
} > CODE2
.const : {
*(.const)
*(.rodata)
} > CALIB
.calib : {
*(.calib)
*_calib.o(.calib) // in case you separate them also out into xxx_Calib.c files compiled to xxx_Calib.o files
} > CALIB
.postbuild : {
*(.postbuild)
*_PBCfg.o(.postbuild) // in case you separate them also out into xxx_PBCfg.c files compiled to xxx_PBCfg.o files
} > POSTBUILD
}
This approach allows you also to prepare the layout in a header, and generate that configs and calibs by certain external tools.
Additionally, it also allows a bit better to estimate your memory resource consumptions and therefore also, how and where to place/link them to the memory sections.
Here is an AUTOSAR MemMap usage example in code
#define XXX_START_SEC_CODE_FAST
#include "MemMap.h"
void XXX_Channel0_Notification(void) {
// Channel0 Finished Notification e.g. called from ISR
}
#define XXX_STOP_SEC_CODE_FAST
#include "MemMap.h"
The MemMap.h can configure this like:
#if defined(XXX_START_SEC_CODE_FAST)
#undef XXX_START_SEC_CODE_FAST
// open section .codefast
#pragma section code ".codefast"
#elif defined(XXX_START_SEC_CODE_SLOW)
#undef XXX_START_SEC_CODE_SLOW
// open section .codeslow
#pragma section code ".codeslow"
#endif
#if defined(XXX_STOP_SEC_CODE_FAST)
#undef XXX_STOP_SEC_CODE_FAST
// back to default section
#pragma section code
#elif defined(XXX_STOP_SEC_CODE_SLOW)
#undef XXX_STOP_SEC_CODE_SLOW
// back to default section
#pragma section code
#endif
The MemMap.h file could be generated, by scanning all the start/stop section defines, and generating the pragmas by a config tool (could be even Excel).
The advantage is, that you do not spill all the pragmas possible (e.g. different compilers <-> different dialects), or if to map or not to map. So you can actually reuse the code in different projects if possible.

How to read the absolute load address of the beginning of shared library data section on runtime?

Lets consider this example:
glob.c source code is linked to shared library named glob.so. From main.c that link against glob.so I want to read the value of 'global_offset' variable at runtime(I don't think it's possible to do at compile time). My compiler is gcc 4.8.5 MinGW.
glob.c:
int glob_shared_var = 69;
main.c:
size_t global_offset = // read shared library load offset
size_t relative_glob_shared_var_offset = // read offset value from e.g. nm glob.a symbols table
printf("glob_shared_var value: %d \n", *(int *)(global_offset + relative_glob_shared_var_offset));
console output:
glob_shared_var value: 69
Ok so I read little bit more about GNU LD linker scripts and I learned about __data_start__ and __data_end__ symbols which are added to the beginning and ending of each consolidated binary by default. What I guess could work is create custom linker script rule that creates __data_start_glob__ and __data_end_glob__ symbols accordingly next to them to uniquely identify each shared library while producing them.
After library creation I would produce text file dump from it and grep offset addresses of each symbol in the library and put it to simple flat text file which will be then read at runtime.
reading address of shared library data section at runtime would look like this:
usize_t = glob_offset = &__data_start_glob__;
usize_t = glob_shared_var_offset; // read from flat file
int val = *(int *)(glob_offset + glob_shared_var_offset);
I know that use-case for this will be very limited, but maybe someone will have similar crazy idea in the future.

_f_data_rom linker script symbols

I am working on startup code of micro controller 32 bit and codewarrior compiler , As we have to deal with linker script. Certain variables that used in startup code for initilization of RAM and stack come from linker script.
Linker Script initialize these variables with different address. Problem is one variable have wrong address. In linker script it initialized by following command _f_data_rom.
Could any one tell me how linker initiazed variables that provides
address for stack , RAM initialization?
What this command means _f_data_rom ?
it looks like
RC_SDATA_SRC = _f_sdata_rom;
RC_SDATA_DEST = _f_sdata;
RC_SDATA_SIZE = (SIZEOF(.sdata)+3) / 4;
RC_DATA_SRC = _f_data_rom;
RC_DATA_DEST = _f_data;
RC_DATA_SIZE = (SIZEOF(.data)+3) / 4;
Looking at this document at page 69 you have to look at __ppc_eabi_linker.h how those variable are exported, e.g: this link to file
Into the same document, at page 65 you can find the form that configure addresses for linker. Take a look at that and check those settings.

Is it possible to pass command line arguments to GNU LD to create a section, define size and place it in a specific memory?

I want to create a section in RAM, allocate a specific size and place it an an address? Is it possible to do all these operations without passing a linker script "file" or w/o modifying the existing linker script ?
.myspace :
{
. = 0x10000;
. = . + STACK_SIZE;
} > ram
Is it possible to do all the operation done by linker script in command line with GNU LD/GCC ?
Seems like a way outdated answer, but anyway.
It's not possible to reserve the section size via ld command line options, but if the next section starts at the end of your special section, you can try something like this:
ld --section-start=.myspace=0x10000 -Ttext=0x11000 ...

Make or Shell Variables In Linker Script

Is it possible for a linker script to access makefile/shell variables and make a decision based on the said variable?
For example, suppose I want to change the start of the RAM area below without using a different linker script, would it be possible to use a make variable to do this?
MEMORY
{
ifeq ($(SOME_VAR),0)
RAM (wx) : ORIGIN = 0x100000, LENGTH = 128K
else
RAM (wx) : ORIGIN = 0x200000, LENGTH = 128K
endif
}
ld does not import any variables from the environment, so it cannot use them directly. The best way to do this is to create your own linker script with the environment variables you want to export, and have the original linker script include it as so:
makefile:
foo:
echo SOMEVAR=$(SOMEVAR) > environment_linker_script
ld ...
enviroment_linker_script:
SOMEVAR=xxx
master_linker_script:
include environment_linker_script
ifeq ($(SOME_VAR),0) ...
I doubt it although it would depend on the linker you're using. It might be possible for the linker to read some exported variables from make but as I said all this would be dependent on specific linker functionality. Another idea could be as long as the linker file isn't going to be too complicated one option would be to actually generate it using your makefile. There are many examples you could google for about having makefiles generate header files, you could follow the same path for generating a linker file.
If the solution of deciding within the makefile which one of several linker scripts is not sufficient, then generating the script within make might be a viable option.
This solution relies on GNU make's verbatim variables, which get exported to environment variables and are referenced with $$.
I am using this for defining a path variable to be used to include a linker script in another linker script like so:
define LDSCRIPT_DEF
/* Include the original libopencm3 linker script */
INCLUDE $(OPENCM3_DIR)/lib/stm32/f4/stm32f405x6.ld
/* Do custom stuff */
endef
export LDSCRIPT_DEF
$(LDSCRIPT):
#echo "$$LDSCRIPT_DEF" > $(LDSCRIPT)
Explanation: this is a part of the makefile and only works if the makefiles use $(LDSCRIPT) as a prerequisite in all relevant rules.

Resources