Author Topic: ARM Memory Layout C  (Read 37963 times)

0 Members and 1 Guest are viewing this topic.

Offline Mtech1Topic starter

  • Contributor
  • Posts: 28
  • Country: in
ARM Memory Layout C
« on: January 08, 2024, 10:03:38 am »
Hello there,

I've been studying the C memory layout, categorized into text, data, stack, and heap. In this model, the text section holds program instructions, the data section stores global variables, the stack handles local variables, and the heap manages dynamic memory allocation.

I'm particularly interested in understanding how the memory layout works for the ARM Cortex M series, especially for Cortex M0. Do variables get stored in a similar manner as I explained in the general terms at the beginning of this message?

I'd appreciate any information regarding this.

Thanks a lot
 

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 4137
  • Country: gb
  • Doing electronics since the 1960s...
Re: ARM Memory Layout C
« Reply #1 on: January 08, 2024, 11:50:09 am »
This is to some extent compiler dependent. This is from my project notes on a 32F417:

The linker script LinkerScript.ld shows the basic config, which looks like this:

FLASH_BOOT (rx)     : ORIGIN = 0x08000000, LENGTH = 32K
FLASH_APP (rx)      : ORIGIN = 0x08008000, LENGTH = 1024K-32K
RAM (xrw)           : ORIGIN = 0x20000000, LENGTH = 128K
CCMRAM (rw)         : ORIGIN = 0x10000000, LENGTH = 64K

The FLASH is organised as a 32k boot block at the bottom (boot loader and supporting code) and then all the rest (1MB-32k) for the application.

The start vectors are always at the bottom and are never moved. Initial control goes to startup_stm32f417xx.s which sets up initialised data, clears BSS and COMMON, and transfers control to main() in main.c.

With ARM GCC, statics are categorised thus:
int fred; goes into BSS (used to go into COMMON in GCC versions before v10)
int fred=0; goes into BSS (which by definition is zeroed by startup code)
int fred=1; goes into DATA (statics initialised to nonzero, copied to RAM at start)

The stack pointer is initialised to top of RAM, and the general stack grown down from there.

The heap base is after the end of DATA BSS COMMON (maybe these not in that order) and it grow upwards until it hits the lowest stack address.

This is standard and has been in use since the 1970s.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 8831
  • Country: fi
Re: ARM Memory Layout C
« Reply #2 on: January 08, 2024, 01:47:29 pm »
"text", "data", "bss" are just naming conventions for sections - widely followed, sure, but still just names. With a linker script, you can do whatever you want, define own sections that map into different address space. You would then use compiler attributes to direct the compiler to put variables or code (functions) into those sections.

Stack and heap on the other hand, are concepts; two slightly different ways for dynamic memory management. You almost always have stack, but not necessarily heap.
 

Offline Mtech1Topic starter

  • Contributor
  • Posts: 28
  • Country: in
Re: ARM Memory Layout C
« Reply #3 on: January 08, 2024, 05:34:54 pm »
This is to some extent compiler dependent. This is from my project notes on a 32F417:

How would  you determine where your variables are placed in memory? Especially when dealing with pointer variables, static local variables, static global variables, and extern variables in your code. How does one identify which memory section your compiler is utilizing for these variables?
 

Offline Tation

  • Regular Contributor
  • *
  • Posts: 82
  • Country: pt
Re: ARM Memory Layout C
« Reply #4 on: January 08, 2024, 06:31:41 pm »
constants go into .rodata.
static variables go into .bss if zero initialized, in .data otherwise.
automatic variables into the stack or in registers.
dynamically allocated variables in the heap.

But, as stated before, this can be modified.
 

Offline Karel

  • Super Contributor
  • ***
  • Posts: 2267
  • Country: 00
Re: ARM Memory Layout C
« Reply #5 on: January 08, 2024, 06:44:49 pm »
  high memory address -> +--------------------------------+
                         |  stack (grows downwards)       |
                         +--------------------------------+
                         |                                |
                         .                                .
                         .                                .
                         .                                .
                         |                                |
                         +--------------------------------+
                         |  heap (grows upwards)          |
                         +--------------------------------+
                         | .bss  (uninitialized data)     |
                         +--------------------------------+
                         | .data (initialized data)       |
                         +--------------------------------+
                         | .text (code and constant data) |
   low memory address -> +--------------------------------+


Size - GNU Utility

> size release/stm32_xxxx.elf

   text    data     bss     dec     hex filename
  16520      24   18496   35040    88e0 release/stm32_xxxx.elf


'text' is your code, and constants (and also the vector table).

'data' is for initialized variables. This is count towards both RAM and FLASH. The initialized value
  allocates space in FLASH which then is copied from ROM to RAM in the startup code.

'bss' is for the uninitialized data in RAM which is initialized with zero in the startup code.

'dec' and 'hex' are the total of the above
 

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 3922
  • Country: us
Re: ARM Memory Layout C
« Reply #6 on: January 08, 2024, 06:55:22 pm »
This is to some extent compiler dependent. This is from my project notes on a 32F417:

How would  you determine where your variables are placed in memory? Especially when dealing with pointer variables, static local variables, static global variables, and extern variables in your code. How does one identify which memory section your compiler is utilizing for these variables?

Normally you should try not to care.  These sections are not part of the C standard, and are instead platform implementation rules for implementing C.  As such they may vary from platform to platform.  For instance, pretty much any platform will have .text, .bss, and .data.  Many but not all platforms will have .rodata, others will just throw read only data into the main .data section. RISC-V has .sdata, .sbss, and similar sections for "small" objects to optimize references relative to the global pointer register.

The cases where you care are where the platform provides specific sections with special behavior.  For instance, in a microcontroller you might reserve a section of flash for non-volatile configuration data.  You would create a section for that, add the linker rules to handle it, and then put your variables into that section.  You do this by using compiler attributes __attribute__((section("nvcfg")))
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4314
  • Country: us
Re: ARM Memory Layout C
« Reply #7 on: January 09, 2024, 01:10:11 am »
Quote
How would  you determine where your variables are placed in memory?
There are tools that will let you look at things.  "objdump" and "nm" are two common ones.
Here's some output from arm-none-eabi-objdump -xC <someArduinoExampleSketch.elf>
Code: [Select]
20000478 l     O .bss   000001c0 udd_ep_in_cache_buffer
20000638 l     O .bss   000001c0 udd_ep_out_cache_buffer
00003bdc l     F .text  00000010 _GLOBAL__sub_I_usbd
20000450 l     O .bss   0000001c epHandlers
2000046c l     O .bss   00000001 isEndpointHalt
2000046d l     O .bss   00000007 LastTransmitTimedOut
20000474 l     O .bss   00000001 isRemoteWakeUpEnabled
000047fc l     O .text  00000009 STRING_PRODUCT
00004806 l     O .text  00000004 STRING_LANGUAGE
0000480a l     O .text  00000009 STRING_MANUFACTURER
00004813 l     O .text  00000012 USB_DeviceDescriptorB
20000a08 l     O .bss   00000004 guard variable for PluggableUSB()::obj
20000a0c l     O .bss   00000008 PluggableUSB()::obj
000041d8 l       .text  00000000 .udivsi3_skip_div0_test
20000a1c l     O .bss   00000004 heap_end.4144
0000485c l     O .text  00000000 __FRAME_END__
2000008c l     O .data  00000060 impure_data
200000fc l       .data  00000000 __init_array_end

 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1769
  • Country: se
Re: ARM Memory Layout C
« Reply #8 on: January 09, 2024, 08:02:39 am »
Quote
How would  you determine where your variables are placed in memory?
There are tools that will let you look at things.
Another helpful tool is the .map file generated by the linker:
Code: [Select]
     VMA      LMA     Size Align Out     In      Symbol
       0        0        0     1 _estack = ORIGIN ( DTCMRAM ) + LENGTH ( DTCMRAM )
       0        0        0     1 _Min_Heap_Size = 0x200
       0        0        0     1 _Min_Stack_Size = 0x400
 8000000  8000000      298     1 .isr_vector
 8000000  8000000        0     1         . = ALIGN ( 4 )
 8000000  8000000      298     1         CMakeFiles/DaisySeed.dir/startup_stm32h750xx.s.obj:(.isr_vector)
 8000000  8000000        0     1                 g_pfnVectors
 8000298  8000298        0     1         . = ALIGN ( 4 )
[---8<---]
 800243e  800243e       24     2         CMakeFiles/DaisySeed.dir/freertos/tasks.c.obj:(.text.xTaskGetSchedulerState)
 800243e  800243e        0     1                 $t.39
 800243f  800243f       24     1                 xTaskGetSchedulerState
 8002462  8002462       c4     2         CMakeFiles/DaisySeed.dir/freertos/tasks.c.obj:(.text.ulTaskGenericNotifyTake)
 8002462  8002462        0     1                 $t.45
 8002463  8002463       c4     1                 ulTaskGenericNotifyTake
 8002526  8002526       1a     2         CMakeFiles/DaisySeed.dir/freertos/tasks.c.obj:(.text.vApplicationGetTimerTaskMemory)
 8002526  8002526        0     1                 $t.57
 8002527  8002527       1a     1                 vApplicationGetTimerTaskMemory
[---8<---]
200000e0 200000e0       14     4         CMakeFiles/DaisySeed.dir/freertos/tasks.c.obj:(.bss.xSuspendedTaskList)
200000e0 200000e0       14     1                 xSuspendedTaskList
200000f4 200000f4        4     4         CMakeFiles/DaisySeed.dir/freertos/tasks.c.obj:(.bss.xYieldPendings)
200000f4 200000f4        4     1                 xYieldPendings
200000f8 200000f8       14     4         CMakeFiles/DaisySeed.dir/freertos/tasks.c.obj:(.bss.xPendingReadyList)
200000f8 200000f8       14     1                 xPendingReadyList
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline Karel

  • Super Contributor
  • ***
  • Posts: 2267
  • Country: 00
Re: ARM Memory Layout C
« Reply #9 on: January 09, 2024, 09:45:34 am »
For instance, in a microcontroller you might reserve a section of flash for non-volatile configuration data.  You would create a section for that, add the linker rules to handle it, and then put your variables into that section.  You do this by using compiler attributes __attribute__((section("nvcfg")))

And in the linkerscript, you need to pay attention to let that section start at the beginning of a flashpage.
I usually use the last flashpage of the mcu to store a serialnumber of the device.
In the firmware the serial number is set to 000000 and after the factory test has been successful, a command will be send to
the device to program the unique serial number.
Do NOT use the flash memory to store settings that can change often. For that you MUST use an (external or internal) eeprom.
 

Offline DiTBho

  • Super Contributor
  • ***
  • Posts: 4247
  • Country: gb
Re: ARM Memory Layout C
« Reply #10 on: January 09, 2024, 10:24:28 am »
"text", "data", "bss" are just naming conventions for sections - widely followed, sure, but still just names. With a linker script, you can do whatever you want, define own sections that map into different address space. You would then use compiler attributes to direct the compiler to put variables or code (functions) into those sections.

In daily experience, I have seen several commercial toolchains that do not allow you to define anything different from the standard, and when you really  need it you have to call the softwarehouse (e.g. GreenHills, ... ), commit your request to someone who is willing to handle it, and let both your sale and project manager manage the whole business since every change to the toolchain involves testing and verification activities before the dev_team can be allowed to actually *USE* it, so a lot of work, therefore a lot of people to be allocated for the activity.

In short, don't touch the linker script unless it is strictly necessary, for example because you need to define a menory layout that includes "trmem" (not standard), and for each change you expect at least a two-three month delay to get written permission to use that stuff in production.

In this, open source toolchains { GNU, clang/llvm, ... } are the most permissive about what you can re-define without money implication.
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline Karel

  • Super Contributor
  • ***
  • Posts: 2267
  • Country: 00
Re: ARM Memory Layout C
« Reply #11 on: January 09, 2024, 11:24:13 am »
In short, don't touch the linker script unless it is strictly necessary, for example because you need to define a menory layout that includes "trmem" (not standard), and for each change you expect at least a two-three month delay to get written permission to use that stuff in production.

Depends on where you work. My impression is that most people use the GNU toolchain and they do whatever they want/like with it.
All the info and manuals are out there:

https://sourceware.org/binutils/docs-2.41/ld.html

Apart from the above example about storing and flashing a serialnumber in a device, I modify the linkerscript also in order to be able to
calculate a crc checksum of the firmware and insert it into that same firmware when running make.
That way, when the device boots, it checks the crc of it's own firmware and it will refuse to operate if there's a mismatch.


 

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 4137
  • Country: gb
  • Doing electronics since the 1960s...
Re: ARM Memory Layout C
« Reply #12 on: January 09, 2024, 12:44:18 pm »
Quote
How would  you determine where your variables are placed in memory? Especially when dealing with pointer variables, static local variables, static global variables, and extern variables in your code. How does one identify which memory section your compiler is utilizing for these variables?

You look in the map file :)

Code: [Select]
.data          0x0000000020000000        0x4 ./src/b_88dbxx.o
 *aes_tiny.o(.data .data*)
                0x0000000020000004                . = ALIGN (0x4)
                0x0000000020000004                _e_boot_data = .
                0x00000000080022f4                _si_boot_data = LOADADDR (.boot_data)

.boot_bss       0x0000000020000004      0x570 load address 0x00000000080022f8
                0x0000000020000004                . = ALIGN (0x4)
                0x0000000020000004                _s_boot_bss = .
 *main.o(.bss .bss* .COMMON .common .common*)
 .bss           0x0000000020000004      0x564 ./src/main.o
                0x000000002000000c                g_dev_id
                0x0000000020000010                ctx
                0x0000000020000110                hspi2

but yes in general you don't care much. There are considerations of efficiency e.g.

 int fred = 0;

places fred in bss and that's it, whereas

 int fred = 1;

places fred in data and then you also have some flash storage for the 1 value (4 bytes). So having zero init values is more efficient; they all get placed in bss which by definition gets zeroed at startup.

Yes one can change all these names but why would you, only to confuse the hell out of everybody else, and out of yourself when you revisit the project 10 years later :)
« Last Edit: January 09, 2024, 12:45:55 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline DiTBho

  • Super Contributor
  • ***
  • Posts: 4247
  • Country: gb
Re: ARM Memory Layout C
« Reply #13 on: January 09, 2024, 03:39:08 pm »
storing and flashing a serialnumber in a device, I modify the linkerscript also in order to be able to calculate a crc checksum of the firmware and insert it into that same firmware when running make.

Code: [Select]
checksum:
        @am checksum -p ${TargetChip} -d obj/app.bin

serialnumber:
        @am serialnumber -p ${TargetChip} -d obj/app.bin -i $sn

GreenHills has dedicated tools for that.
It means nobody is allowed to change the linker script for that.
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline Karel

  • Super Contributor
  • ***
  • Posts: 2267
  • Country: 00
Re: ARM Memory Layout C
« Reply #14 on: January 09, 2024, 03:58:12 pm »
storing and flashing a serialnumber in a device, I modify the linkerscript also in order to be able to calculate a crc checksum of the firmware and insert it into that same firmware when running make.

Code: [Select]
checksum:
        @am checksum -p ${TargetChip} -d obj/app.bin

serialnumber:
        @am serialnumber -p ${TargetChip} -d obj/app.bin -i $sn

GreenHills has dedicated tools for that.
It means nobody is allowed to change the linker script for that.

That's nice for a company that has a group of untrusted developers.

Personally, I fail to see the added value to pay for a toolchain and receive limitations/restrictions in return.
I'll stick with the GNU toolchain.
 

Offline Tation

  • Regular Contributor
  • *
  • Posts: 82
  • Country: pt
Re: ARM Memory Layout C
« Reply #15 on: January 09, 2024, 04:55:18 pm »
Personally, I fail to see the added value to pay for a toolchain and receive limitations/restrictions in return.

Maybe it is that you do not need certification in your products.
 

Offline DiTBho

  • Super Contributor
  • ***
  • Posts: 4247
  • Country: gb
Re: ARM Memory Layout C
« Reply #16 on: January 10, 2024, 09:34:25 am »
That's nice for a company that has a group of untrusted developers.

Personally, I fail to see the added value to pay for a toolchain and receive limitations/restrictions in return.
I'll stick with the GNU toolchain.

Sure, like when, after touching the toolchain, a guy introduced a bug so terrifying and sneaky that for 6 months all Gentoo HPPA2/Stage{1..3}s were basically rubbish as by enabling -o3 every single "if (...) then" had a 0.5 chance of being compiled incorrectly, resulting in if then else branches executing completely random.

Or just when in recent Linux Kernel v5 a dude messed up an a #define line in a .h file in the MIPS profile, resulting the PCI completly messed up!

Lot of examples, and they all cost months and months of debugging, a lot of hours wasted.

So,
  • if don't know what you're doing, don't touch anything!
  • if you know what you are doing, don't touch it unless it's strickly neccessary
  • if you know what you are doing && you modify something, you MUST spend some time making sure your changes don't silently screw everything up other things

Do OpenSource-mind people do it? Typically NO!

All full of ego - I do this, I do that, blablabla -  then in reality, when everyone can modify everything without any restriction and supervision, it's like public toilets: everyone wants to defecate in them, some keeps it clean, others make it dirty, and when "shits happen", no one ever wants to clean it, and well, here that's precisely the point: as a tester, and then developer, after 15 years I am really tired of having to clean up other people's shit, in fact I would like to become a full project manager, so I can kick out anyone who dares to touch any point of the code, the toolchain, or anything else, without my written authorization/discussion/documentation and especially when it is not even strictly necessary.
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1769
  • Country: se
Re: ARM Memory Layout C
« Reply #17 on: January 10, 2024, 09:45:48 am »
So,
  • if don't know what you're doing, don't touch anything!
  • if you know what you are doing, don't touch it unless it's strickly neccessary
  • if you know what you are doing && you modify something, you MUST spend some time making sure your changes don't silently screw everything up other things

Do OpenSource-mind people do it? Typically NO!
"given enough bugs, all eyeballs are shallow", myself, paraphrasing Eric Raymond.
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline Karel

  • Super Contributor
  • ***
  • Posts: 2267
  • Country: 00
Re: ARM Memory Layout C
« Reply #18 on: January 10, 2024, 09:56:33 am »
I don't know what you are talking about.
Both open and closed source software have bugs and can suffer from bad programming behaviour.
But, if you take small hobbyist projects out of the equation, than open-source software has slightly better code quality and slightly less bugs than
closed source software.
In most companies, money is more important than code quality and there's no way to check their code quality.
Shareholders must be kept happy, release quick, fix problems later. And all those rules and certifications are becoming more and more bureaucratic,
just to keep regulators happy and to put up barriers for new competitors.

Anyway, I was talking about the GNU tool chain which is not written by hobbyists.
 

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 8831
  • Country: fi
Re: ARM Memory Layout C
« Reply #19 on: January 10, 2024, 01:11:09 pm »
All full of ego - I do this, I do that, blablabla -  then in reality, when everyone can modify everything without any restriction and supervision, it's like public toilets: everyone wants to defecate in them, some keeps it clean, others make it dirty, and when "shits happen", no one ever wants to clean it, and well, here that's precisely the point: as a tester, and then developer, after 15 years I am really tired of having to clean up other people's shit, in fact I would like to become a full project manager, so I can kick out anyone who dares to touch any point of the code, the toolchain, or anything else, without my written authorization/discussion/documentation and especially when it is not even strictly necessary.

What a weird rant. Do you post this shit in completely random threads? What does your rant have to do with anything here? Memory layout was discussed. Linker script is literally THE way to define the memory layout. Of course someone has to write and/or modify linker scripts. No one suggested taking a working project and doing random changes for no reason.
« Last Edit: January 10, 2024, 01:13:07 pm by Siwastaja »
 
The following users thanked this post: Karel

Offline std

  • Contributor
  • Posts: 15
  • Country: 00
Re: ARM Memory Layout C
« Reply #20 on: February 09, 2024, 09:07:57 pm »
I'm particularly interested in understanding how the memory layout works for the ARM Cortex M series, especially for Cortex M0. Do variables get stored in a similar manner as I explained in the general terms at the beginning of this message?
1. What you mean "memory layout works for"? 
2. Other ways to explore memory map is already mentioned here (.map file, object dump, etc). In case you are using STM32CubeIDE try "Build analyzer" view.
2009402-0
It can give you deep understanding/feeling where is all variable/region is.  You can doubleckick view lines jumping to code. Also, I remember IAR embedded workbench (IAR EWARM) also has runtime view with mem objects addresses (I'll be surprised if Keil doesn't have it). I’m not even talking about the Segger debugger "Ozone" or their other tools.

To profs:
this CubeIDE view is very convenient to find object allocation mistakes, such as forgotten 'const'.
In this case object goes to SRAM (.data section), not to FLASH (.rodata section).

« Last Edit: February 09, 2024, 09:09:30 pm by std »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf