I'm working on a big embedded project and I'm facing an issue that I cannot explain with my limited knowledge of linkers. I've managed to recreate the problem in a really simple program.
I have a single C source file and I want to align an array to a 16 kB boundary, but I don't want to do that with the aligned attribute. Instead, I want to use ALIGN builtin function of the linker script.
The source file is pretty simple:
#include "boot.h"
typedef unsigned char uint8_t;
uint8_t boo[0x800] __attribute__ (( section(".data_aligned") )) = { 0U };
uint8_t foo[0x800] = { 1U };
void start()
{
return;
}
And the linker script is:
SECTIONS
{
. = 0x00100000 ;
_sCode = . ;
.text ALIGN(0x1000) : { *(.text) }
.data ALIGN(0x1000) :
{
_sData = . ;
*(.data) ;
_eData = . ;
. = ALIGN(0x4000) ;
_sDataAlign = . ;
*(.data_aligned) ;
_eDataAlign = . ;
}
.rodata ALIGN(0x1000) : { *(.rodata) }
.bss ALIGN(0x1000) : { *(.bss) }
_eCode = . ;
}
I compiled and linked the project with gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) with these commands:
gcc -c -masm=att -m32 -nostdinc -nostdlib -fno-builtin -fno-pic -std=c99 -o bin/boot.o boot.c
ld -e 0x100000 bin/boot.o -T linker_script_gcc.ld -m elf_i386 --verbose -O0 -o bin/application.elf
From the elf file I get:
...
8: 00101000 0 NOTYPE GLOBAL DEFAULT 2 _sData
2: 00101000 0 SECTION LOCAL DEFAULT 2
13: 00101000 2048 OBJECT GLOBAL DEFAULT 2 foo
10: 00101800 0 NOTYPE GLOBAL DEFAULT 2 _eData
14: 00104000 0 NOTYPE GLOBAL DEFAULT 2 _sDataAlign
7: 00104000 2048 OBJECT GLOBAL DEFAULT 2 boo
9: 00104800 0 NOTYPE GLOBAL DEFAULT 2 _eDataAlign
...
It seems that the boo array has been succesfully aligned to the 16 kB boundary.
However, when this program is compiled with the GHS compiler, the boo array is not aligned as expected:
...
26: 00101000 0 NOTYPE GLOBAL DEFAULT 2 _sData
6: 00101000 18432 SECTION LOCAL DEFAULT 2 .data
22: 00101000 2048 OBJECT GLOBAL DEFAULT 2 foo
27: 00101800 0 NOTYPE GLOBAL DEFAULT 2 _eData
28: 00105000 0 NOTYPE GLOBAL DEFAULT 2 _sDataAlign
21: 00105000 2048 OBJECT GLOBAL DEFAULT 2 boo
29: 00105800 0 NOTYPE GLOBAL DEFAULT 2 _eDataAlign
...
So I wondered what really should be the value of _sDataAlign and why.
According to the documentation linked here, the location counter in a linker script is relative to the start of the output section in which is evaluated:
Note: . actually refers to the byte offset from the start of the current containing object. Normally this is the SECTIONS statement, whose start address is 0, hence . can be used as an absolute address. If . is used inside a section description however, it refers to the byte offset from the start of that section, not an absolute address.
Moreover, here it is explained:
Unary operations on a relative address, and binary operations on two relative addresses in the same section or between one relative address and a number, apply the operator to the offset part of the address(es).
And:
The result of other operations on relative addresses (after above conversions) is a relative address in the same section as the operand(s).
So, in my understanding, the symbol _sDataAlign is set to 0x4000 relative to the start of the .data section which is 0x101000. Therefore, _sDataAlign should be 0x105000, exactly as computed by the GHS compiler.
Is this analysis correct? If so, why GNU linker is giving me a wrong value?