I am writing a program to run bare metal. I am trying to write to a variable from a custom linker script. The code runs perfectly when compiled with -O0 options but not as expected when compiled with -Os option.
The code I used is as follows.
main.c:
#define TTB_BASE (&Image$$TTB)
extern unsigned int Image$$TTB;
int main ()
{
*TTB_BASE = 56326;
unsigned int *ttb=TTB_BASE 16;
for (int i = 0; i < 8; i )
{
*ttb = 1;
}
*(volatile unsigned int*)(0x00100000) = *TTB_BASE;
}
When compiled with -Os option, the code *TTB_BASE = 56326; is seems to be optimized out and the value is not stored to the address TTB_BASE.
The linker script used:
ARMCA7.ld
ENTRY(main)
SECTIONS
{
. = 0x00160000;
.text : { *(.text*) }
.data : { *(.data .data.* .gnu.linkonce.d*) }
.bss (NOLOAD) : {
. = ALIGN(16);
*(.bss .bss.*)
*(COMMON)
}
. = 0x00170000;
.ttb :
{
Image$$TTB = .;
}
}
Compiling statement:
arm-none-eabi-gcc -mcpu=cortex-a7 -Os -std=gnu99 -c -o main.o main.c
arm-none-eabi-gcc -mcpu=cortex-a7 -Os -T ARMCA7.ld -nostartfiles -Xlinker --gc-sections -Wl,-Map,"main.map" -o main.elf main.o
Disassembly got:
Address : Opcode Statement
------- ------ ---------
8 unsigned int *ttb=TTB_BASE 16;
main:
00160000: 24 30 9f e5 ldr r3, [pc, #36] ; 0x16002c <main 44>
12 *ttb = 1;
00160004: 01 10 a0 e3 mov r1, #1
00160008: 20 20 83 e2 add r2, r3, #32
0016000c: 04 10 83 e4 str r1, [r3], #4
10 for (int i = 0; i < 8; i )
00160010: 02 00 53 e1 cmp r3, r2
00160014: fc ff ff 1a bne 0x16000c <main 12>
14 *(volatile unsigned int*)(0x00100000) = *TTB_BASE;
00160018: 60 20 13 e5 ldr r2, [r3, #-96] ; 0xffffffa0
0016001c: 01 36 a0 e3 mov r3, #1048576 ; 0x100000
15 }
00160020: 00 00 a0 e3 mov r0, #0
14 *(volatile unsigned int*)(0x00100000) = *TTB_BASE;
00160024: 00 20 83 e5 str r2, [r3]
15 }
00160028: 1e ff 2f e1 bx lr
0016002c: 40 00 17 00 andseq r0, r7, r0, asr #32
It can be seen that nothing is written to the address TTB_BASE which is 0x00170000 defined in the linker script, and the value written to address 0x00100000 is wrong.
Is there some bug in the code or linker script?
Note:
If I change the first two lines of main.c to the following code,
#define TTB_BASE (Image$$TTB)
extern unsigned int Image$$TTB[];
The code can be correctly compiled and the address TTB_BASE is written correctly. However, as I seen from other questions, both approach should be correct.
gcc version used:
arm-none-eabi-gcc --version
arm-none-eabi-gcc.exe (Arm GNU Toolchain 11.3.Rel1) 11.3.1 20220712
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
I also test the code with arm-none-eabi-gcc.exe 10.3.1 20210621 (release) and got the same problem.
CodePudding user response:
Your external variable has no side effects visible to the compiler, and as such it is allowed to optimize the assignment out.
The standard compliant way to avoid this is to add the qualifier volatile
. It tells the compiler that this variable has side effects. Therefore it cannot optimize the assignment out.
extern volatile unsigned int Image$$TTB;
You need this qualifier also, if you have multiple threads like interrupt service routines, which share variables with each other or the main thread.
Addition:
As others pointed out in comments, there is an error in the linker script. You don't reserve space for the variable.
CodePudding user response:
I found that the question posted is related to external variables other than linker script. Please refer to this new post for further discussion.