Struct initialisation list is zero-ed instead of initialised to values

0

I'd say I'm decent in C++, however I'm very new to linker scripts and I'm not quite sure what I'm doing wrong. First off all, this is my linker script:

ENTRY(ISR_Reset)
MEMORY {
   FLASH   (rx)    : ORIGIN = 0x80000,     LENGTH = 128K
   RAM     (rwx)   : ORIGIN = 0x20000000   LENGTH = 36K
}
SECTIONS {
   .text : {
      *(.vector_table)
      *(.text.startup)
      *(.text)
      *(.rodata)
   } > FLASH

   __data_flash_source__ = ALIGN(4);
   .data : AT(__data_flash_source__) {
       __data_section_start__ = .;
       *(.data)
       *(.init_array)
       __data_section_end__ = .;
       __ram_end__ = ORIGIN(RAM) + LENGTH(RAM);
   } > RAM

   .bss : {
       *(.bss)
   } > RAM
}

I copy the data from FLASH to RAM between __data_section_start__ and __data_section_end__, then I zero out the rest of the RAM. I'm using the following command to compile my code: arm-none-eabi-g++.exe .\src\start.s .\src\main.cpp -o .\bin\main -nostdlib -ffreestanding -fno-exceptions -nostartfiles -T .\src\sam3u2e.ld -mthumb -pedantic -Wall -std=c++17 -ggdb -Ofast

If I remove the -Ofast flag, and don't use any optimisation I get the following linker error: section .text._ZN4_PMCC2Ev LMA [0008019c,000801bf] overlaps section .data LMA [0008019c,0008019f] However if I do include it it compiles just fine, but when I try to initialise structs like so:

struct Example {
   unsigned int const ID;
   unsigned int volatile & some_register;
   Example(unsigned int const id, unsigned int const register_address) : ID(id),
                                                                         some_register(*((unsigned int volatile *)register_address)) {}
} instance(0x12345678, 0xDEADBEEF);

I notice in the disassembly that the address of instance.some_register is stored in FLASH, right after the main() function, but it doesn't seem to get referenced by any code even when writing to it like so:

instance.some_register = 1234;

The instance.ID, however, is being loaded from RAM, but from a higher address than the size of .data, which means it never gets copied from FLASH to RAM. But if I change the location of .bss from > RAM to > FLASH it tries to load the instance.ID from FLASH, which I don't understand, because the ID should be in .rodata anyway.

Furthermore I notice that even if I don't reference the struct at all in the code other than defining it that the address is still located in FLASH, I wonder if it'd be possible to tell the linker to not include code for initialised structs that aren't referenced anywhere?

EDIT: Linker sections:

linker sections

as you can see the .data section (that gets copied into RAM) is only 4 bytes in size. However the assembly tries to load instance.ID and the address of instance.some_register from RAM ---that's higher than 0x20000004, which confuses me, because those values should be read-only anyways and therefore stored in flash?

EDIT 2: I probably wasn't clear enough in what I want the code to do. I'll try explaining again. I want to define a set of different structs, each of them would have different variables. Each variable references a device register (by setting the pointer of the variable to the address of the corresponding register). So when the user would write to those registers, for example: peripheral.some_register = 123; the value would be written to the actual physical register.

In the compiled (optimised) code I don't really want these structs to exist, so the CPU wouldn't need to deal with initialising data, and would instead just see it as a collection of pointers to different peripheral registers. Since it would be read-only data it could also be located in FLASH and so wouldn't need to use up any RAM.

I'm guessing that means I mustn't use constructors, as that would then mean the CPU will need to initialise them, even though the data is, again, read-only. The user also won't need to create his own instances of the structs, in case that helps with anything.

I initally wanted to use constructors because there are some peripherals with over 50 registers each, that have the same offsets per peripheral, but different base addresses, so I was hoping to save myself some work by not needing to copy paste the same address for each register and add a number to it.

c++
optimization
linker
arm
embedded
asked on Stack Overflow Jul 12, 2020 by enzeys • edited Jul 12, 2020 by enzeys

1 Answer

0

So there are a few things going on here. Let me try to tackle them with my also limited knowledge of linker scripts.

  1. The error you see related to the symbol .text._ZN4_PMCC2Ev is typically due to an issue of lacking entries in the linker script. In this case you don't have a place for .text.*, so I would suggest you try to add *(.text.*)
  2. I'm not sure what you mean by "it doesn't seem to get referenced", but instance.some_register would not be a symbol. Usually it would be loaded as an offset from the program counter. However, this will depend on how instance is allocated.
  3. Maybe I'm missing something, but while instance.ID is constant, instance isn't. I would thus not expect that to be located in .rodata.
answered on Stack Overflow Jul 12, 2020 by Alf

User contributions licensed under CC BY-SA 3.0