ARM Memory Layout C - Page 1
Forum section: electronics / general

author: Mtech1
author_rank: Contributor
timestamp: January 08, 2024, 10:03:38 am
content: 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

author: peter-h
author_rank: Super Contributor
timestamp: January 08, 2024, 11:50:09 am
content: 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 = 32KFLASH_APP (rx) : ORIGIN = 0x08008000, LENGTH = 1024K-32KRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128KCCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64KThe 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.

author: Siwastaja
author_rank: Super Contributor
timestamp: January 08, 2024, 01:47:29 pm
content: "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.

author: Mtech1
author_rank: Contributor
timestamp: January 08, 2024, 05:34:54 pm
content: Quote from: peter-h on January 08, 2024, 11:50:09 amHow 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?

author: Tation
author_rank: Regular Contributor
timestamp: January 08, 2024, 06:31:41 pm
content: 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.

author: Karel
author_rank: Super Contributor
timestamp: January 08, 2024, 06:44:49 pm
content: 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.elftext data bss dec hex filename16520 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 valueallocates 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

author: ejeffrey
author_rank: Super Contributor
timestamp: January 08, 2024, 06:55:22 pm
content: Quote from: Mtech1 on January 08, 2024, 05:34:54 pmNormally 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")))

author: westfw
author_rank: Super Contributor
timestamp: January 09, 2024, 01:10:11 am
content: QuoteThere are tools that will let you look at things. "objdump" and "nm" are two common ones.Here's some output fromarm-none-eabi-objdump -xC <someArduinoExampleSketch.elf>Code:20000478 l O .bss 000001c0 udd_ep_in_cache_buffer20000638 l O .bss 000001c0 udd_ep_out_cache_buffer00003bdc l F .text 00000010 _GLOBAL__sub_I_usbd20000450 l O .bss 0000001c epHandlers2000046c l O .bss 00000001 isEndpointHalt2000046d l O .bss 00000007 LastTransmitTimedOut20000474 l O .bss 00000001 isRemoteWakeUpEnabled000047fc l O .text 00000009 STRING_PRODUCT00004806 l O .text 00000004 STRING_LANGUAGE0000480a l O .text 00000009 STRING_MANUFACTURER00004813 l O .text 00000012 USB_DeviceDescriptorB20000a08 l O .bss 00000004 guard variable for PluggableUSB()::obj20000a0c l O .bss 00000008 PluggableUSB()::obj000041d8 l .text 00000000 .udivsi3_skip_div0_test20000a1c l O .bss 00000004 heap_end.41440000485c l O .text 00000000 __FRAME_END__2000008c l O .data 00000060 impure_data200000fc l .data 00000000 __init_array_end

author: newbrain
author_rank: Super Contributor
timestamp: January 09, 2024, 08:02:39 am
content: Quote from: westfw on January 09, 2024, 01:10:11 amAnother helpful tool is the .map file generated by the linker:Code:VMA LMA Size Align Out In Symbol0 0 0 1 _estack = ORIGIN ( DTCMRAM ) + LENGTH ( DTCMRAM )0 0 0 1 _Min_Heap_Size = 0x2000 0 0 1 _Min_Stack_Size = 0x4008000000 8000000 298 1 .isr_vector8000000 8000000 0 1 . = ALIGN ( 4 )8000000 8000000 298 1 CMakeFiles/DaisySeed.dir/startup_stm32h750xx.s.obj:(.isr_vector)8000000 8000000 0 1 g_pfnVectors8000298 8000298 0 1 . = ALIGN ( 4 )[---8<---]800243e 800243e 24 2 CMakeFiles/DaisySeed.dir/freertos/tasks.c.obj:(.text.xTaskGetSchedulerState)800243e 800243e 0 1 $t.39800243f 800243f 24 1 xTaskGetSchedulerState8002462 8002462 c4 2 CMakeFiles/DaisySeed.dir/freertos/tasks.c.obj:(.text.ulTaskGenericNotifyTake)8002462 8002462 0 1 $t.458002463 8002463 c4 1 ulTaskGenericNotifyTake8002526 8002526 1a 2 CMakeFiles/DaisySeed.dir/freertos/tasks.c.obj:(.text.vApplicationGetTimerTaskMemory)8002526 8002526 0 1 $t.578002527 8002527 1a 1 vApplicationGetTimerTaskMemory[---8<---]200000e0 200000e0 14 4 CMakeFiles/DaisySeed.dir/freertos/tasks.c.obj:(.bss.xSuspendedTaskList)200000e0 200000e0 14 1 xSuspendedTaskList200000f4 200000f4 4 4 CMakeFiles/DaisySeed.dir/freertos/tasks.c.obj:(.bss.xYieldPendings)200000f4 200000f4 4 1 xYieldPendings200000f8 200000f8 14 4 CMakeFiles/DaisySeed.dir/freertos/tasks.c.obj:(.bss.xPendingReadyList)200000f8 200000f8 14 1 xPendingReadyList

author: Karel
author_rank: Super Contributor
timestamp: January 09, 2024, 09:45:34 am
content: Quote from: ejeffrey on January 08, 2024, 06:55:22 pmAnd 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 tothe 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.

author: DiTBho
author_rank: Super Contributor
timestamp: January 09, 2024, 10:24:28 am
content: Quote from: Siwastaja on January 08, 2024, 01:47:29 pmIn 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.

author: Karel
author_rank: Super Contributor
timestamp: January 09, 2024, 11:24:13 am
content: Quote from: DiTBho on January 09, 2024, 10:24:28 amDepends 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.htmlApart from the above example about storing and flashing a serialnumber in a device, I modify the linkerscript also in order to be able tocalculate 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.

author: peter-h
author_rank: Super Contributor
timestamp: January 09, 2024, 12:44:18 pm
content: QuoteYou look in the map fileCode:.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 0x00000000080022f80x0000000020000004 . = ALIGN (0x4)0x0000000020000004 _s_boot_bss = .*main.o(.bss .bss* .COMMON .common .common*).bss 0x0000000020000004 0x564 ./src/main.o0x000000002000000c g_dev_id0x0000000020000010 ctx0x0000000020000110 hspi2but 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, whereasint 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

author: DiTBho
author_rank: Super Contributor
timestamp: January 09, 2024, 03:39:08 pm
content: Quote from: Karel on January 09, 2024, 11:24:13 amCode:checksum:@am checksum -p ${TargetChip} -d obj/app.binserialnumber:@am serialnumber -p ${TargetChip} -d obj/app.bin -i $snGreenHills has dedicated tools for that.It means nobody is allowed to change the linker script for that.

author: Karel
author_rank: Super Contributor
timestamp: January 09, 2024, 03:58:12 pm
content: Quote from: DiTBho on January 09, 2024, 03:39:08 pmThat'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.

author: Tation
author_rank: Regular Contributor
timestamp: January 09, 2024, 04:55:18 pm
content: Quote from: Karel on January 09, 2024, 03:58:12 pmMaybe it is that you do not need certification in your products.

author: DiTBho
author_rank: Super Contributor
timestamp: January 10, 2024, 09:34:25 am
content: Quote from: Karel on January 09, 2024, 03:58:12 pmSure, 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 neccessaryif you know what you are doing && you modify something, youMUSTspend some time making sure your changes don't silently screw everything up other thingsDo 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.

author: newbrain
author_rank: Super Contributor
timestamp: January 10, 2024, 09:45:48 am
content: Quote from: DiTBho on January 10, 2024, 09:34:25 am"given enough bugs, all eyeballs are shallow", myself, paraphrasing Eric Raymond.

author: Karel
author_rank: Super Contributor
timestamp: January 10, 2024, 09:56:33 am
content: 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 thanclosed 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.

author: Siwastaja
author_rank: Super Contributor
timestamp: January 10, 2024, 01:11:09 pm
content: Quote from: DiTBho on January 10, 2024, 09:34:25 amWhat 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.

author: std
author_rank: Contributor
timestamp: February 09, 2024, 09:07:57 pm
content: Quote from: Mtech1 on January 08, 2024, 10:03:38 am1. 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.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).