How to get the first address of initialized data segment - c

my program is working on linux using gcc. Through the manual page, I find edata, which represent the first address past the end of the initialized data segment.
But I want know the first address of initialized data segment
How can I get it?
I have tried treating etext as the first address of initialized data segment. Then I got a segment fault when I increase the address and access the variable stored in it. I think some address space between etext and edata was not mapped into virtual memory. Is that right?

That depends on your linker scripts. For example on some platforms you have the symbol __bss_start at the beginning of BSS. It's a symbol without any data associated with it, you can get a pointer to it by extern declaring a variable with that name (only for the sake of taking the address of that variable). For example:
#include <stdio.h>
extern char __bss_start;
int main()
{
printf("%p\n", &__bss_start);
return 0;
}
You find this by looking in the linker script, for example in /usr/lib/ldscripts/elf_x64_64.x:
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
_edata = .; PROVIDE (edata = .);
__bss_start = .; /* <<<<< this is what you're looking for /*
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections.
FIXME: Why do we need it? When there is no .bss section, we don't
pad the .data section. */
. = ALIGN(. != 0 ? 64 / 8 : 1);
}
You can also see the edata you mentioned, but as edata is not reserved for the implementation (the PROVIDE means only to create this symbol if it otherwise isn't used) you should probably use _edata instead.
If you want the address to the start of the data section you could modify the linker script:
__data_start = . ;
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
_edata = .; PROVIDE (edata = .);
__bss_start = .; /* <<<<< this is what you're looking for /*
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections.
FIXME: Why do we need it? When there is no .bss section, we don't
pad the .data section. */
. = ALIGN(. != 0 ? 64 / 8 : 1);
}
You probably want to make a copy of the linker script (look for the right one in /usr/lib/ldscripts, they are different depending on what kind of output you're targeting) and supply it when you compile:
gcc -o execfile source.c -Wl,-T ldscript
Another option if you don't want to modify the linker script could be to use the __executable_start and parse the ELF headers (hoping that the executable is sufficiently linearly mapped)
As for _etext, it is the end of the text section (you can read that in the linker script as well, but I didn't include it in the excerpt), but the text section is followed by rodata, trying to write there is probably going to segfault.

You can use the linux tool size (binutils package in Debian/Ubuntu).
Example
size -A /usr/bin/gcc
results in
/usr/bin/gcc :
section size addr
.interp 28 4194928
.note.ABI-tag 32 4194956
.note.gnu.build-id 36 4194988
.gnu.hash 240 4195024
.dynsym 4008 4195264
.dynstr 2093 4199272
.gnu.version 334 4201366
.gnu.version_r 160 4201704
.rela.dyn 720 4201864
.rela.plt 3240 4202584
.init 14 4205824
.plt 2176 4205840
.text 384124 4208016
.fini 9 4592140
.rodata 303556 4592160
.eh_frame_hdr 8540 4895716
.eh_frame 50388 4904256
.gcc_except_table 264 4954644
.tbss 16 7052632
.init_array 16 7052632
.fini_array 8 7052648
.jcr 8 7052656
.data.rel.ro 3992 7052672
.dynamic 480 7056664
.got 216 7057144
.got.plt 1104 7057384
.data 2520 7058496
.bss 80976 7061024
.gnu_debuglink 12 0
Total 849310

Related

GCC LD NOLOAD linker section generates loadable segment

I'm working on an Arm bare-metal application and I've marked some sections with NOLOAD. According to the explanation in Understanding linker script NOLOAD sections in embedded software
, I expected the resulting ELF file to not have a loadable segment (program header) for these sections, but it does.
Is this correct? Why are those sections marked as loadable in the ELF file?
As the linker is still placing the data in .bss, how a loader is supposed to know that the sections shouldn't be loaded? Or am I missing the meaning of 'load' in the sense that NOLOAD only makes sense for initialized symbols (which would normally be placed into .data)?
This is a part of my linker script:
.bss (NOLOAD) :
{
. = ALIGN(4);
__bss_start__ = .;
*(.bss_begin .bss_begin.*)
*(.bss .bss.*)
*(COMMON)
*(.bss_end .bss_end.*)
. = ALIGN(4);
__bss_end__ = .;
} >DRAM
.noinit (NOLOAD) :
{
. = ALIGN(4);
__noinit_start__ = .;
*(.noinit .noinit.*)
. = ALIGN(4) ;
__noinit_end__ = .;
} > DRAM
/* Check if there is enough space to allocate the main stack */
._stack (NOLOAD) :
{
. = ALIGN(4);
. = . + __Main_Stack_Size ;
. = ALIGN(4);
} >DRAM
This is the output ELF file:
arm-none-eabi-readelf.exe -l test.elf
Elf file type is EXEC (Executable file)
Entry point 0x601b9
There are 2 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x010000 0x00060000 0x00060000 0x06840 0x06840 RWE 0x10000
LOAD 0x020000 0x20010000 0x20010000 0x00000 0x06084 RW 0x10000
Section to Segment mapping:
Segment Sections...
00 .text .ARM.exidx.reset .data
01 .systemclock .bss ._stack
Why are the .bss and ._stack sections there?
Thanks!!
The FileSiz of the second segment is 0 which means that there is no data that will be loaded from the elf file into this segment.
The reason why this segment exists in the table is that on a non-embedded system, the program loader would still need to request memory for sections in that segment and mark the relevant pages with the relevant attributes (readable and writable in this case).
Edit: After reading the question again, I did a bit more experimenting and it seems that all (NOLOAD) seems to do is to set the type of the section in the output file to SHT_NOBITS. The elf specification calls that a section that occupies no space in the file but is still part of the program's memory image.
If the goal is to link against code that is already present in the rom before the program is loaded, those symbols should be defined in the linker script outside of any section. E.g. already_present_code = 0x06000000; SECTIONS { .text : {*(.text*)} /* ... */}. That seems to have the desired effect.

Cannot assign address of variable defined in linker script

I found a solution, although I don't understand what went wrong. Here is the original question. The solution is at the end.
I am following this Raspberry PI OS tutorial with a few tweaks. As the title says, one assignment appears to fail.
Here is my C code:
extern int32_t __end;
static int32_t *arena;
void init() {
arena = &__end;
assert(0 != arena); // fails
...
The assert triggers! Surely the address shouldn't be 0. __end is declared in my linker script:
ENTRY(_start)
SECTIONS
{
/* Starts at LOADER_ADDR. 0x8000 is a convention. */
. = 0x8000;
__start = .;
.text : {
*(.text)
}
.rodata : { *(.rodata) }
.data : { *(.data) }
/* Define __bss_start and __bss_end for boot.s to set to 0 */
__bss_start=.;
.bss : { *(.bss) }
__bss_end=.;
/* First usable address for the allocator */
. = ALIGN(4);
__end = .;
}
Investigating in GDB (running it in QEMU):
Thread 1 hit Breakpoint 1, init () at os.c:75
75 arena = &__end;
(gdb) p &__end
$1 = (int32_t *) 0x9440
(gdb) p arena
$2 = (int32_t *) 0x0
(gdb) n
76 assert(0 != arena);
(gdb) p arena
$3 = (int32_t *) 0x0
GDB can find __end but my program cannot?
Here are a few other things I tried:
the tutorial's code works without an issue (implying that QEMU and the ARM compiler are working)
the assertion still fails when running without GDB (implying GDB is not the issue)
I am able to assign 0xccc to arena (implying arena is not the issue)
I am not able to assign &__end to a local variable (implying &__end is the issue).
As requested in the comments, this is how I tried to assign to a local variable:
void* arena2 = (void*)&__end;
assert(0 != arena2);
The assertion fails. In GDB:
Thread 1 hit Breakpoint 1, mem_init () at mem.c:77
77 void* arena2 = (void*)&__end;
(gdb) p arena2
$1 = (void *) 0x13
(gdb) p &__end
$2 = (int32_t *) 0x94a4
(gdb) n
78 assert(0 != arena2);
(gdb) p arena2
$3 = (void *) 0x0
(gdb) p &__end
$4 = (int32_t *) 0x94a4
assert(0 != &__end); succeeds (implying &__end is not the issue?)
N.B. This version of assert is not the same as the one in assert.h, but I don't think it causes the problem. It just checks a condition, prints the condition, and goes to a breakpoint. I can reproduce the issue in GDB with the assert commented out.
N.B.2. I previously included the ARM assembly of the C code in case there was a compiler bug
My solution is to edit the linker script to:
ENTRY(_start)
SECTIONS
{
/* Starts at LOADER_ADDR. 0x8000 is a convention. */
. = 0x8000;
__start = .;
.text : {
*(.text)
}
. = ALIGN(4096);
.rodata : { *(.rodata) }
. = ALIGN(4096);
.data : { *(.data) }
. = ALIGN(4096);
/* Define __bss_start and __bss_end for boot.s to set to 0 */
__bss_start = .;
.bss : { *(.bss) }
. = ALIGN(4096);
__bss_end = .;
/* First usable address for the allocator */
. = ALIGN(4096);
__end = .;
}
I don't understand why the additional ALIGNs are important.
The problem you're having here is because the "clear the BSS" loop in boot.S is also clearing some of the compiler-generated data in the ELF file that the C code is using at runtime. Notably, it is accidentally zeroing out the GOT (global offset table) which is in the .got ELF section and which is where the actual address of the __end label has been placed by the linker. So the linker correctly fills in the address in the ELF file, but then the boot.S code zeroes it, and when you try to read it from C then you get zero rather than what you were expecting.
Adding all that alignment in the linker script is probably working around this by coincidentally causing the GOT to not be in the area that gets zeroed.
You can see where the linker has put things by using 'objdump -x myos.elf'. In my test case based on the tutorial you link I see a SYMBOL TABLE which includes among other entries:
000080d4 l .bss 00000004 arena
00000000 l df *ABS* 00000000
000080c8 l O .got.plt 00000000 _GLOBAL_OFFSET_TABLE_
000080d8 g .bss 00000000 __bss_end
0000800c g F .text 00000060 kernel_main
00008000 g .text 00000000 __start
0000806c g .text.boot 00000000 _start
000080d8 g .bss 00000000 __end
00008000 g F .text 0000000c panic
000080c4 g .text.boot 00000000 __bss_start
So you can see that the linker script has set __bss_start to 0x80c4 and __bss_end to 0x80d8, which is a pity because the GOT is at 0x80c4/0x80c8. I think what has happened here is that because you didn't specify explicitly in your linker script where to put the .got and .got.plt sections, the linker has decided to put them after the __bss_start assignment and before the .bss section, so they get covered by the zeroing code.
You can see what the ELF file contents of the .got are with 'objdump --disassemble-all myos.elf', which among other things includes:
Disassembly of section .got:
000080c4 <.got>:
80c4: 000080d8 ldrdeq r8, [r0], -r8 ; <UNPREDICTABLE>
so you can see we have one GOT table entry, whose contents are the address 0x80d8 which is the __end value we want. When the boot.S code zeroes this out your C code reads a 0 rather than the constant it was expecting.
You should probably ensure that the bss start/end are at least 16-aligned, because the boot.S code works via a loop that clears 16 bytes at a time, but I think that if you fix your linker script to explicitly put the .got and .got.plt sections somewhere then you'll find you don't need the 4K alignments everywhere.
FWIW, I diagnosed this using: (1) the QEMU "-d in_asm,cpu,exec,int,unimp,guest_errors -singlestep" options to get a dump of register state and instruction execution and (2) objdump of the ELF file to figure out what the compiler's generated code was actually doing. I had a suspicion this was going to turn out to be either "accidentally zeroed data we shouldn't have" or "failed to include in the image or otherwise initialize data we should have" kind of bug, and so it turned out.
Oh, and the reason GDB was printing the right value for __end when your code wasn't was that GDB could just look directly in the debug/symbol info in the ELF file for the answer; it wasn't doing it by going via the in-memory GOT.

How does objcopy compute what sections for the elf file to insert into the output file?

let's say I run arm-none-eabi-objcopy firmwared.elf -O ihex firmware.hex
Assume the binary was generated with the following linker script:
ENTRY(Reset_Handler)
MEMORY
{
FLASH (RX) : ORIGIN = 0x08020000, LENGTH = 896K
SRAM (RWX) : ORIGIN = 0x20000000, LENGTH = 512K
BKPSRAM (RW) : ORIGIN = 0x40024000, LENGTH = 4K
}
_estack = 0x20080000;
SECTIONS
{
.isr_vector :
{
. = ALIGN(4);
_isr_vector = .;
KEEP(*(.isr_vector))
. = ALIGN(4);
} > FLASH
.firmware_header_vector :
{
. = ALIGN(4);
KEEP(*(.firmware_header_vector))
. = ALIGN(4);
} > FLASH
.text :
{
. = ALIGN(4);
_stext = .;
*(.Reset_Handler)
*(.text)
*(.text*)
*(.rodata)
*(.rodata*)
*(.glue_7)
*(.glue_7t)
KEEP(*(.init))
KEEP(*(.fini))
. = ALIGN(4);
_etext = .;
} > FLASH
.ARM.extab :
{
. = ALIGN(4);
*(.ARM.extab)
*(.gnu.linkonce.armextab.*)
. = ALIGN(4);
} > FLASH
.exidx :
{
. = ALIGN(4);
PROVIDE(__exidx_start = .);
*(.ARM.exidx*)
. = ALIGN(4);
PROVIDE(__exidx_end = .);
} > FLASH
.preinit_array :
{
PROVIDE(__preinit_array_start = .);
KEEP(*(.preinit_array*))
PROVIDE(__preinit_array_end = .);
} > FLASH
.init_array :
{
PROVIDE(__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array*))
PROVIDE(__init_array_end = .);
} > FLASH
.fini_array :
{
PROVIDE(__fini_array_start = .);
KEEP(*(.fini_array*))
KEEP(*(SORT(.fini_array.*)))
PROVIDE(__fini_array_end = .);
} > FLASH
_sidata = .;
.data_x : AT(_sidata) /* LMA address is _sidata (in FLASH) */
{
. = ALIGN(4);
_sdata = .; /* data section VMA address */
*(.data*)
. = ALIGN(4);
_edata = .;
} > SRAM
.firmware_header (_sidata + SIZEOF(.data_x)):
{
. = ALIGN(4);
KEEP(*(.firmware_header))
. = ALIGN(4);
} > FLASH
.eth (NOLOAD) :
{
. = ALIGN(4);
KEEP(*(.RxDecripSection))
KEEP(*(.TxDescripSection))
KEEP(*(.RxarraySection))
KEEP(*(.TxarraySection))
. = ALIGN(4);
} > SRAM
.bss :
{
. = ALIGN(4);
_sbss = .;
PROVIDE(__bss_start__ = _sbss);
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
PROVIDE(__bss_end__ = _ebss);
} > SRAM
PROVIDE(end = .);
.heap (NOLOAD) :
{
. = ALIGN(4);
PROVIDE(__heap_start__ = .);
KEEP(*(.heap))
. = ALIGN(4);
PROVIDE(__heap_end__ = .);
} > SRAM
.reserved_for_stack (NOLOAD) :
{
. = ALIGN(4);
PROVIDE(__reserved_for_stack_start__ = .);
KEEP(*(.reserved_for_stack))
. = ALIGN(4);
PROVIDE(__reserved_for_stack_end__ = .);
} > SRAM
.battery_backed_sram (NOLOAD) :
{
. = ALIGN(4);
KEEP(*(.battery_backed_sram))
. = ALIGN(4);
} > BKPSRAM
/DISCARD/ :
{
*(.ARM.attributes)
}
}
This results in a readelf output of:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .isr_vector PROGBITS 08020000 010000 0001f8 00 WA 0 0 4
[ 2] .firmware_header_ PROGBITS 080201f8 0101f8 000004 00 WA 0 0 4
[ 3] .text PROGBITS 08020200 010200 021b44 00 AX 0 0 64
[ 4] .ARM.extab PROGBITS 08041d44 042728 000000 00 W 0 0 1
[ 5] .exidx ARM_EXIDX 08041d44 031d44 000008 00 AL 3 0 4
[ 6] .init_array INIT_ARRAY 08041d4c 031d4c 000008 04 WA 0 0 4
[ 7] .fini_array FINI_ARRAY 08041d54 031d54 000004 04 WA 0 0 4
[ 8] .data_x PROGBITS 20000000 040000 0009c8 00 WA 0 0 8
[ 9] .firmware_header PROGBITS 08042720 042720 000008 00 WA 0 0 4
[10] .eth NOBITS 200009c8 0509c8 0030a0 00 WA 0 0 4
[11] .bss NOBITS 20003a68 0509c8 045da4 00 WA 0 0 4
[12] .heap PROGBITS 2004980c 042728 000000 00 W 0 0 1
[13] .reserved_for_sta PROGBITS 2004980c 042728 000000 00 W 0 0 1
[14] .battery_backed_s NOBITS 40024000 044000 00000c 00 WA 0 0 4
[15] .comment PROGBITS 00000000 042728 000075 01 MS 0 0 1
[16] .debug_frame PROGBITS 00000000 0427a0 00144c 00 0 0 4
[17] .stab PROGBITS 00000000 043bec 000084 0c 18 0 4
[18] .stabstr STRTAB 00000000 043c70 000117 00 0 0 1
[19] .symtab SYMTAB 00000000 043d88 009b00 10 20 1787 4
[20] .strtab STRTAB 00000000 04d888 0042bb 00 0 0 1
[21] .shstrtab STRTAB 00000000 051b43 0000e6 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
y (purecode), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x010000 0x08020000 0x08020000 0x21d58 0x21d58 RWE 0x10000
LOAD 0x040000 0x20000000 0x08041d58 0x009c8 0x009c8 RW 0x10000
LOAD 0x042720 0x08042720 0x08042720 0x00008 0x00008 RW 0x10000
LOAD 0x0509c8 0x200009c8 0x08042720 0x00000 0x48e44 RW 0x10000
LOAD 0x044000 0x40024000 0x40024000 0x00000 0x0000c RW 0x10000
Section to Segment mapping:
Segment Sections...
00 .isr_vector .firmware_header_vector .text .exidx .init_array .fini_array
01 .data_x
02 .firmware_header
03 .eth .bss
04 .battery_backed_sram
How does objcopy know not to insert sections such as .bss in to the output image? I know that it computes this on the fly, and I'm assuming this mechanism is driven through the section to segment mapping, but I cannot find any explanation as to how it actually performs the segment to section mapping. The elf file stores no information about which segments are flash, and yet somehow objcopy knows that it should not copy .bss into the output file. How?
The 'A' flag in the Flg column of the readelf output, which ELF calls SHF_ALLOC, indicates a section that 'occupies memory during process execution'.
SHF_ALLOC : The section occupies memory during process execution. Some
control sections do not reside in the memory image of an object file;
this attribute is off for those sections.
http://refspecs.linuxbase.org/elf/elf.pdf
Typically this applies to both program and data memory, and in a 'normal' OS environment the OS loads the SHF_ALLOC sections to the indicated addresses (ignoring or using for other purposes the non-SHF_ALLOC sections as appropriate) and everything's ready to go.
In an embedded environment with ROM program memory, only SHF_ALLOC sections mapping to addresses in ROM need to be output.
(This question illustrates a case in which going by address alone without taking SHF_ALLOC into account is not enough.)
However:
Depending on how the linker produced the ELF file, initialised data sections may or may not require additional handling.
Ideally the linker produces two sections related to initialised data: one in RAM (the runtime data area), and one in ROM (the initialisation contents for the RAM data). Startup code references the addresses of these sections and copies the init data to the RAM area. In this case simply outputting SHF_ALLOC sections in the ROM address range will automatically produce correct results.
If the linker instead produces a single section as if for a normal OS, then when generating the ROM image the data section needs to be identified and emitted at an address in ROM (and the startup code needs to be able to find that address).
Most decent toolchains take the former approach if configured correctly, but I've certainly worked with the latter as well.
If you want an algorithm that mimics what objcopy -O binary does you need to look at the section headers. Unlike as is often suggested, just looking at segment headers is not enough.
Quote from the objcopy man page:
objcopy can be used to generate a raw binary file by using an output target of binary (e.g., use -O binary). When objcopy generates a raw binary file, it will essentially produce a memory dump of the contents of the input object file. All symbols and relocation information will be discarded. The memory dump will start at the load address of the lowest section copied into the output file.
I think the last sentence says very deliberately 'section', as this really is what happens: objcopy goes through all the sections and discards those that do not go into a raw binary file. The remaining sections are written to the output file, sorted by ascending address. Space between sections in the output file is filled with zeros.
For an own project where I need to convert ELF files to raw binaries and where I do not want to depend on objcopy I came up with the following:
If a section is of type SHT_NULL or SHT_NOBITS, then the section is not included. This follows from the ELF specification (https://refspecs.linuxbase.org/elf/elf.pdf):
SHT_NULL: "This value marks the section header as inactive; it does not have an associated section. Other members of the section header have undefined values."
SHT_NOBITS: "A section of this type occupies no space in the file but otherwise resembles SHT_PROGBITS. Although this section contains no bytes, the sh_offset member contains the conceptual file offset."
If the sh_addr field of a section header is 0, then the section is not included. Quote from the ELF specification: "If the section will appear in the memory image of a process, this member
gives the address at which the section's first byte should reside. Otherwise,
the member contains 0."
If the sh_size field of a section header is 0, then the section is not included. Quote from the ELF specification: "This member gives the section's size in bytes. Unless the section type is SHT_NOBITS, the section occupies sh_size bytes in the file. A section of type SHT_NOBITS may have a non-zero size, but it occupies no space
in the file
If the sh_flags field of a section header doesn't have the SHF_ALLOCflag set, then the section is not included. Quote from the ELF specification: "The section occupies memory during process execution. Some control sections do not reside in the memory image of an object file; this attribute is off for those sections."
Step 3 is somewhat redundant, but for me it nicely filters out sections whose size is zero, so that I do not have to deal with these during later stages.
This works for me, although I haven't thrown many ELF files at it yet. Also note that this is guesswork to some extent. I thought about reading the objcopy source code, but that leads very quickly into the bowels of libbfd.
I have no idea if this is the real way to do this, but this is how I ended up solving this:
# For each program header, get the sections contained by it
# For each section, calculate the LMA the section will reside at
# Do NOT load a section if...
# Section type is SHT_NULL or NOBITS
# Section size = 0
# The LMA is outside of the isr_vector -> header region
Note that in my case, I had a section capping off the end of the image. This made it very obvious as to exactly where the image ended.

Why does objcopy exclude one section but not another?

Background
I'm attempting to utilize a special section of SRAM on my STM32 device which is located at address 0x40006000. One way of doing this which I saw in ST's example code was just to simply create pointers whose value happened to live inside that section of RAM. What I'm trying to do is get the linker to manage static allocations in that section for me.
Basically, I'm going from something like this:
static uint16_t *buffer0 = ((uint16_t *)0x40006000);
static uint16_t *buffer1 = ((uint16_t *)0x40006080);
To something like this (which I think is far less breakable and not as hacky, though not as portable):
#define _PMA __attribute__((section(".pma"), aligned(2)))
static uint16_t _PMA buffer0[64];
static uint16_t _PMA buffer1[64];
In order to accomplish this, I've modified my linker script to have a new memory called "PMA" located at 0x40006000 and I have located the ".pma" section inside it as follows:
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
PMA (xrw) : ORIGIN = 0x40006000, LENGTH = 1024 /* This is the memory I added */
}
SECTIONS
{
.text
{
..blah blah blah..
} > FLASH
...more sections, like rodata and init_array..
/* Initialized data goes into RAM, load LMA copy after code */
.data
{
..blah blah blah with some linker symbols to denote the start and end..
} >RAM AT> FLASH
.bss
{
..blah blah blah..
} >RAM
.pma /* My new section! */
{
_pma_start = .;
. = ALIGN(2);
*(.pma)
*(.pma*)
} >PMA
}
What Happens
So this seems all fine and dandy, my stuff compiles and the map shows me that buffer0 and buffer1 are indeed placed at 0x40006000 and 0x40006080. Here is the output of the last bit of my makefile:
arm-none-eabi-gcc obj/usb_desc.o obj/usb_application.o obj/osc.o obj/usb.o obj/main.o obj/system_stm32f1xx.o obj/queue.o obj/list.o obj/heap_1.o obj/port.o obj/tasks.o obj/timers.o obj/startup_stm32f103x6.o -TSTM32F103X8_FLASH.ld -mthumb -mcpu=cortex-m3 --specs=nosys.specs -Wl,-Map,bin/blink.map -o bin/blink.elf
arm-none-eabi-objdump -D bin/blink.elf > bin/blink.lst
arm-none-eabi-size --format=SysV bin/blink.elf
bin/blink.elf :
section size addr
.isr_vector 268 134217728
.text 13504 134218000
.rodata 44 134231504
.ARM 8 134231548
.init_array 8 134231556
.fini_array 4 134231564
.data 1264 536870912
.jcr 4 536872176
.bss 1348 536872180
._user_heap_stack 1536 536873528
.pma 256 1073766400
.ARM.attributes 41 0
.debug_info 26748 0
.debug_abbrev 5331 0
.debug_aranges 368 0
.debug_line 5274 0
.debug_str 8123 0
.comment 29 0
.debug_frame 4988 0
Total 69146
arm-none-eabi-objcopy -R .stack -O binary bin/blink.elf bin/blink.bin
I see that .pma has 256 bytes used, just as I expected. The address looks correct as well. Now, when I ls the bin directory I'm greeted by this:
-rwxr-xr-x 1 kevin users 939548800 Nov 2 00:04 blink.bin*
-rwxr-xr-x 1 kevin users 221528 Nov 2 00:04 blink.elf*
That bin file is what I'm loading onto my chip's flash via openocd. It is an image of the flash starting at 0x08000000.
Sidenote: I actually attempted to load that bin file onto my chip before I had realized how huge it was...that didn't work obviously.
Here's what I get when I remove the PMA section:
arm-none-eabi-size --format=SysV bin/blink.elf
bin/blink.elf :
section size addr
.isr_vector 268 134217728
.text 13504 134218000
.rodata 44 134231504
.ARM 8 134231548
.init_array 8 134231556
.fini_array 4 134231564
.data 1392 536870912
.jcr 4 536872304
.bss 1348 536872308
._user_heap_stack 1536 536873656
.ARM.attributes 41 0
.debug_info 26748 0
.debug_abbrev 5331 0
.debug_aranges 368 0
.debug_line 5274 0
.debug_str 8123 0
.comment 29 0
.debug_frame 4988 0
Total 69018
And the file size is exactly as I would expect:
-rwxr-xr-x 1 kevin users 15236 Nov 2 00:09 blink.bin
-rwxr-xr-x 1 kevin users 198132 Nov 2 00:09 blink.elf
Question
As I understand it, what is going on here is that objcopy has just copied everything from 0x08000000 to 0x400060FF into that bin file. That is obviously not what I wanted to happen. I expected that it would just copy the flash.
Now, obviously I can just say -R .pma on my objcopy command and everything will be happy. However, what I'm curious about is how objcopy knows not to copy .data into the binary image. I noticed that running objcopy -R .data has the exact same result as running objcopy without that. Same thing with .bss. This tells me that it isn't the AT command in the linker script (which is the only real difference I can see between .data and .bss)
What can I do to make my .pma section behave the same way as .data or .bss from objcopy's perspective? Is there something interesting going on with .data/.bss in the intermediate elf file I'm using perhaps (see above for the linker command generating the elf file)?
Having defined your section as ".pma" most probably gave it the type "PROGBITS" (check with readelf), which indicates a section to be loaded on the target.
What you want/need is to define a section that doesn't have to be loaded on the target, like the ".bss" section, which has the type "NOBITS".
I frequently use the following section definition to avoid having certain buffers into the ".bss" section (as this slows down the startup phase due to the zero-initalization of the ".bss" section):
static uint8_t uart1_buffer_rx[4096] __attribute__((section(".noinit,\"aw\",%nobits#")));
I don't remember why I used the name ".noinit", but this sections appears after the ".bss" section.
In your case, it will probably help to add the "aw" and "nobits" flags after the ".pma" section declaration.

integer variable size in bss and data segment

I am using a test program for understanding C memory model on linux 6.3 with kernal version 2.6.32-279.el6.x86_64 .
First i have compile below code,
#include <stdio.h>
int main(void)
{
static int i = 100; /* Initialized static variable stored in DS*/
return 0;
}
on running size command , i got below ,
[root#rachitjain jan14]# size a.out
text data bss dec hex filename
1040 488 16 1544 608 a.out
then, after removing the intialization for static variable 'i' , my code becomes ,
include <stdio.h>
int main(void)
{
static int i ;
return 0;
}
On running size on after compiling above ,
[root#rachitjain jan14]# size a.out
text data bss dec hex filename
1040 484 24 1548 60c a.out
There is 8 byte increment in bss section but only 4 bytes are reduced in the data section. Why the size is integer in getting doubled while moving to bss segment ?
I have tested this character and float as well , observed the same behavioral.
Look, the answer is that the .bss section has a requirement to be aligned on 64 bits and the .data does not have this requirement.
How can you see this? When you build your program ld uses a default linker script in order to build your program. You can see it if you add -Wl,-verbose:
g++ main.cpp -Wl,-verbose
When you build 64 bit applicaton this is a default aligment for .bss section:
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections.
FIXME: Why do we need it? When there is no .bss section, we don't
pad the .data section. */
. = ALIGN(. != 0 ? 64 / 8 : 1);
}
As you can see ALIGN(. != 0 ? 64 / 8 : 1); tells to align to 8 bytes
When you build 32 bit applicaton (g++ -m32 main.cpp -Wl,-verbose) this is a default aligment for .bss section:
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections.
FIXME: Why do we need it? When there is no .bss section, we don't
pad the .data section. */
. = ALIGN(. != 0 ? 32 / 8 : 1);
}
Your .data section obviously does not have any ALIGN commands in the default linker script:
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
Useful links:
http://sourceware.org/ml/binutils/2009-05/msg00174.html
http://access.redhat.com/site/documentation/en-US/Red_Hat_Enterprise_Linux/4/html/Using_ld_the_GNU_Linker/expressions.html)
http://lwn.net/Articles/531148/
ALIGN in Linker Scripts

Resources