Get address of ram sections during runtime - c

I want to implement a stack usage monitor for my NRF52840-Mikrocontroller with Segger Embedded Studio.
To monitor the maximum stack usage, I need some information during runtime like the end address of the .bss segment, which is the start of my free memory.
My approach is, to fill the ram from the .tbss section until to the stackpointer with a magic word.
During runtime, the stack will grow and will overwrite my magic words with data.
In a cyclic check, I am able to dedicate the end of the stack. From that information, I can derive the approximate stack usage.
Is it possible to get the addresses from the picture below during runtime in my c (or ASM) Program?
Here is a part of my .map file, where for example the symbol __bss_start is defined. Is it possible to access this symbol from c code?
*(COMMON)
0x0000000020020ec4 __bss_end__ = (__bss_start__ + SIZEOF (.bss))
0x000000000001b8c8 __bss_size__ = SIZEOF (.bss)
0x0000000020020ec4 __bss_load_end__ = __bss_end__
0x0000000000000001 . = ASSERT (((__bss_start__ == __bss_end__) || ((__bss_end__ - __RAM_segment_start__) <= __RAM_segment_size__)), error: .bss is too large to fit in RAM memory segment)
0x0000000020020ec4 __tbss_load_start__ = ALIGN (__bss_end__, 0x4)

Thanks for your help, I have solved the Problem now.
To access the symbols of the .map file during runtime, I used the following code:
extern char __bss_end__;
int main()
{
char * bss = &__bss_end__;
}
After this line of code, the bss variable contains the start address of the bss-section.
With this code, I am able to get the addresses of the RAM segments during runtime to monitor my stack usage.

Related

Is writing to a section not defined in linker file allowed?

In my linker file, I have the following memory sections.
MEMORY
{
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64k
mram (rwx) : ORIGIN = 0xA0000010, LENGTH = 1M
}
The actual address of the mram peripheral starts at 0xA0000000. In a C file I can write to a specific memory address as
(*(uint32_t *)(void *)0xA0000000) = 0xaabbccdd;
Will this cause any problems?
The space specified in the linker script defines where and what the linker can locate in the defined memory regions. It will not locate anything to the "hole" you have left at 0xA0000000 to 0xA000000F because it is not aware of it.
In that sense it is "safe" in that the linker will not attempt to use that space. It is entirely in the control of your code - you have taken responsibility for that region by not giving it to the linker. And indeed the statement:
(*(uint32_t *)(void *)0xA0000000) = 0xaabbccdd;
will write a 32 bit value to that location. The point is neither the compiler nor the linker will prevent you from doing what you will in that region.
What is less plausible is LENGTH = 1M. That would make your mram 0x100010 bytes long (i.e. 1M + 0x10). That is a problem because the linker is free to locate objects in the region 0x100000 to 0x10000F. The consequences of that depend on your hardware, but quite possibly it will wrap into the region 0x100000 to 0x10000F that you have attempted to hide from the linker. I would imagine that you need LENGTH = 1M - 0x10 or LENGTH = 0x0FFFF0.
Now while you can absolve the linker from managing that region in order to manage it in your code, it may not be the best approach. It would be better to create a linker symbol at the required absolute address.
So given:
MEMORY
{
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64k
mram (rwx) : ORIGIN = 0xA0000000, LENGTH = 1M
}
Your would create a linker symbol in mram:
SECTIONS
{
...
.mram :
{
reserved_mram = 0 ; /* address is zero offset from .mram1 */
. += 0x10 ; /* create 16 byte "hole" at address reserved_mram */
... /* other mram linker assignment follows */
} < mram
...
}
Then in your code you should be able to declare:
extern uint32_t reserved_mram[] ; // 4 x 32-bit word array.
And through reserved_mram you can access the memory at 0xA00000000 symbolically and the code is always in sync with the linker script so you can relocate the space easily without introducing a conflict.
Of course there is no bounds checking and no size information - you still need to confine your access to reserved_mram[0] to reserved_mram[3].
You might alternatively create a separate symbol for each location (with meaningful names specific to your application):
.mram :
{
reserved_mram1 = 0 ;
. += 4 ;
reserved_mram2 = . ;
. += 4 ;
reserved_mram3 = . ;
. += 4 ;
reserved_mram4 = . ;
. += 4 ;
... /* other mram linker usage follows */
} < mram
Then in your code:
extern uint32_t reserved_mram1 ;
extern uint32_t reserved_mram2 ;
extern uint32_t reserved_mram3 ;
extern uint32_t reserved_mram4 ;
Another alternative; you might create an independent section for the region, then create variables within it using __attribute__((section(.xxx))) directives in the code. e.g:
MEMORY
{
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64k
mram1 (rwx) : ORIGIN = 0xA0000000, LENGTH = 0x10
mram2 (rwx) : ORIGIN = 0xA0000010, LENGTH = 0xFFFF0
}
SECTIONS
{
...
.mram1 :
{
*(.mram1)
} < mram1
...
}
Then in your code:
uint32_t my_mram_data __attribute__ ((section (".mram1"))) ;
The variable my_mram_data will be created somewhare in .mram1, but the linker decides where. The advantage here is that you can create arbitrary variables in the code without modifying the linker script, and if you attempt to allocate to .mram1 more data than is available you will get a linker error.
Note that linker script syntax is arcane and varies between linkers - I am unassuming this relates to the GNU linker? But my linker foo is strictly on demand (i.e. I figure it out when I need to) and I make no claim that any of the above is complete or correct, or even the only possible solutions - regard it as illustrative, and refer to the linker documentation for accurate information.

What exactly does ". = 0x7c00" in a linker script do?

What exactly does ". = 0x7c00" in a linker script do?
More specifically, when I place . = 0x7c00 at the beginning of a linker script, why doesn't the resulting output file begin with 0x7c00 = 31,744 zeros?
I understand that when a PC boots, the BIOS places the 512 byte MBR at memory address 0x7c00. However, I am confused as to how exactly the linker's location counter affects how the output file is laid out.
(For context, I'm trying to thoroughly understand the sample code from the "x86 bare metal" project. https://github.com/cirosantilli/x86-bare-metal-examples. I've included the entire linker script below for context.)
SECTIONS
{
/*
We could also pass the -Ttext 0x7C00 to as instead of doing this.
If your program does not have any memory accesses, you can omit this.
*/
. = 0x7c00;
.text :
{
__start = .;
/*
We are going to stuff everything
into a text segment for now, including data.
Who cares? Other segments only exist to appease C compilers.
*/
*(.text)
/*
Magic bytes. 0x1FE == 510.
We could add this on each Gas file separately with `.word`,
but this is the perfect place to DRY that out.
*/
. = 0x1FE;
SHORT(0xAA55)
*(.stage2)
__stage2_nsectors = ABSOLUTE((. - __start) / 512);
. = ALIGN(512);
__end = .;
__end_align_4k = ALIGN(4k);
}
}
It looks like the ". = 0x7c00" is not meaning a length but an absolute address. It reads to me as 'set the current value of the special variable "." to be the hex value 0x7c00 and then it plans to use that address as an offset later in the script like with the . = ALIGN(512) it is also why it saves that address off as __start so it can then do math on the resulting image. If you manipulate . during the script so it points to the last chunk of memory added to the image then you can use it to determine the total size:
__stage2_nsectors = ABSOLUTE((. - __start) / 512);
in English would be
The difference between the starting place and wherever I ended divided by sector size.

STM32, variable stored in flash could not be updated in other file

I use a STM32F411RE.
Since I've no more memory in my RAM. I decided to store large variable in my flash. For that I created a section in section.ld.
.large_buffer: ALIGN(4)
{
. = ALIGN(4) ;
*(.large_buffer.large_buffer.*)
. = ALIGN(4) ;
} >FLASH
In the main.c file, I declare the variable as follow :
uint8_t buffer[60 * 200] __attribute__ ((section(".large_buffer"), used));
At this point everything is OK, the buffer is not stocked in the RAM (bss), I can access it and rewrite it.
buffer[25] = 42;
printf("%d\n", buffer[25]); // 42
The problem comes when I want to edit the variable from an other file.
main.c
uint8_t buffer[60 * 200] __attribute__ ((section(".large_buffer"), used));
int main()
{
myFunc(buffer);
}
other.c
myFunc(uint8_t* buffer)
{
buffer[25] = 42;
printf("%d\n", buffer[25]); // 0
}
buffer never change in another file (passed as parameter).
Did I miss something ?
You cannot write to flash memory the same way as you write to RAM, because of physical design of flash memories. To be exact you need to erase sector/page (let's say ~ 1-4kB, it's specified in your MCU datasheet). The reason is that flash are made that they retain state even if they're not powered, whenever you want to change any bit from value 0 -> 1 you need to erase whole sector (After erase all of bits will be set to 1).
So you cannot use Flash as data memory, what you could do is use Flash as storing variables that are const (read-only) value, so any look-up tables will perfectly fit in there (usually compilers when you stat variable to const will put them inside of flash). How to write to flash you can read in Reference Manual of your MCU.

Copying a function to RAM

I am working on a Embedded application which has a power on initialization routine which is followed by a infinite loop which contains resident program(residing in ROM memory) and a remote program(residing in RAM memory). something like below shown code
main()
{
por(); // power on initialization
while(1)
{
if(Flag == Resident) // enum Flag{Resident,Remote};
ResidentProgram();
else
RemoteProgram(); // program which needs to placed in RAM area of memory
}
}
Here in the por() routine i want to copy the RemoteProgram() in to RAM. later if needed i will call the change to the Flag to Remote. After that i want the program to be executed from RAM location
Here i am not able to place RemoteProgram() function in RAM. kindly help
Using linker scripts we will be able to place the function at desired address only before runtime but not during runtime. here i want to place it during runtime. can anyone help solving this problem. also i dont want to disturb locations occupied by other variables.
My suggestion (from a position of at least some experience) is to do it the way operating systems do it: use a proper object file format, and load it yourself.
You cannot expect to be able to, in C, copy an already-compiled function around, since you won't have access to any basic information about the code in the function (not even its size, much less any relocation information).
So, embed the code as an ELF or some other format, and write an ELF loader which can load the data to the desired location. Being "on the inside" of the format will let you properly implement relocation if needed.
Your program needs to know where in read only memory your function code is located. One way to do that is to place the code in a 'named section'. Your function needs to be compiled so that it will run correctly at the ram location you are going to copy it to. So the function has a load location and a runtime location. Then you can write some C code to copy the function code from it's load location to it's runtime location.
Precisely how you do all of this, as has already been said, is platform and tool chain specific. Here is one method:
C Source code:
#include <string.h>
void copy_code_to_ram()
{
extern char _my_code_in_ram_start_space[];
extern char _my_code_in_ram_end_space[];
extern char _my_code_in_ram_start[];
memcpy(_my_code_in_ram_start, _my_code_in_ram_start_space, _my_code_in_ram_end_space - _my_code_in_ram_start_space);
}
int i;
#pragma code_seg("my_code_in_ram")
void function_in_sram()
{
i++;
}
#pragma code_seg()
void main()
{
copy_code_to_ram();
function_in_sram();
while (1);
}
Linker script extracts:
MEMORY
{
rom: origin = 0XFFF00000, length = 0X100000
ram: origin = 0X00000000, length = 0X800000
}
SECTIONS
{
____CODE :
{
<additional script here>
__my_code_in_ram_start_space = .;
. += sizeof(my_code_in_ram);
__my_code_in_ram_end_space = .;
} > rom
__my_code_in_ram LOAD(__my_code_in_ram_start_space) :
{
__my_code_in_ram_start = .;
*(my_code_in_ram)
__my_code_in_ram_end = .;
} > ram
<additional script here>
}
A video which shows a toolchain specific way of generating this C code and linker script might be useful:
http://www.crossware.com/coldfire/CCodeInRamVideo

Where is function pointer stored?

In an embedded environment, for example, I declared a look-up table in following way:
const Operation Vehicle_OpNotification[] =
{
{ OP_SET , &Vehicle_Notification_Set},
{ OP_GET , &Vehicle_Notification_Get}
};
May I know this function pointer is pointing to RAM address or ROM address?
That depends on your compiler and target environment, but most likely it points to ROM—executable code is almost always placed in read-only memory when available. You have to take special steps (like using a custom linker script or allocating dynamic memory and asking the operating system to mark it as writable and executable) to get executable code in non-read-only memory.
The linker should be able to generate a map-file where the location of each variable/constant defined at file-scope is shown. Example:
main.c:
static const int constant = 42;
static int volatile variable = 43;
int main (int argc, const char * argv[]) {
variable = constant;
return 0;
}
Generated map-file: (notice how the constant constant is placed in the same memory segment as the read-only code (TEXT), while the variable variable is placed in the DATA section)
# Sections:
# Address Size Segment Section
0x00001F6C 0x0000008F __TEXT __text
0x00001FFC 0x00000004 __TEXT __literal4
0x00002000 0x00000018 __DATA __data
0x00002018 0x0000001C __DATA __dyld
0x00003000 0x00000008 __IMPORT __pointers
0x00003040 0x00000005 __IMPORT __jump_table
# Symbols:
# Address Size File Name
0x00001F6C 0x00000040 [ 1] start
0x00001FAC 0x00000014 [ 1] dyld_stub_binding_helper
0x00001FC0 0x0000000E [ 1] __dyld_func_lookup
0x00001FCE 0x0000002D [ 2] _main
0x00001FFC 0x00000004 [ 2] _constant
0x00002000 0x00000004 [ 1] ___progname
0x00002004 0x00000004 [ 1] _environ
0x00002008 0x00000004 [ 1] _NXArgv
0x0000200C 0x00000008 [ 1] _NXArgc
0x00002014 0x00000004 [ 2] _variable
0x00002018 0x0000001C [ 1] __dyld#0
0x00003000 0x00000004 [ 2] _variable$non_lazy_ptr
0x00003004 0x00000004 [ 2] _constant$non_lazy_ptr
0x00003040 0x00000005 [ 0] _exit$stub
Its address will be entirely dependent on the location of the function pointed to; the linker will tell you that. Most often application code ie either all in RAM or all in ROM, though for some targets, it may be split for performance reasons if RAM execution is faster as it typically is on processors faster than around 100MHz.
If you need to determine this at runtime (if for example it may be either, and is dynamic), you can simply compare the address with the start and end of the relevant memory sections. These addresses may be emitted as symbols by the linker given an appropriate linker script, or they may be defined constants in a target header file or BSP for example.
All that said, I can think of few reasons you should need to know at run-time (unless you are considering self modifying code!).
This is dependent on architecture.
However, if you have some idea of the address ranges for ROM and RAM, you can check the function pointer to the address.
Or, as Mehrdad says, just try writing to the address, it should be easy to figure out from there.
It all depends on which compiler/processor you use. If I put your definition in Keil Uvision for 8051, surely would point to ROM because of the "CONST" definer although I could modify with XRAM or CODE. But for ARM depends on address and not definer.
Keil Uvision Example:
// For 8051 Keil
const char code romdata[2] = {0,1}; //<< this point to ROM/FLASH
const char xram ramdata[2] = {0,1}; // this point to external RAM
const char romdata[2] = {0,1}; // this point to ROM/FLASH

Resources