I'm trying to assemble a small application for STM32F103 with bare assembly and linker script.
Here's what I've come up with:
linker.ld:
MEMORY {
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
SRAM : ORIGIN = 0x20000000, LENGTH = 20K
}
sp_initial_value = ORIGIN(SRAM) + LENGTH(SRAM);
vector_table_size = 0x130;
SECTIONS {
vector_table ORIGIN(FLASH) : {
LONG(sp_initial_value);
LONG(reset_exception_handler);
} > FLASH
.text ADDR(vector_table) + vector_table_size :
{
} > FLASH
}
blink.s:
.cpu cortex-m3
.syntax unified
.thumb
.text
.global reset_exception_handler
.thumb_func
.type reset_exception_handler, %function
reset_exception_handler:
add r0, 1
b reset_exception_handler
Makefile:
blink-asm.bin: blink-asm.elf
arm-none-eabi-objcopy -O binary blink-asm.elf blink-asm.bin
flash: blink-asm.bin
st-flash write blink-asm.bin 0x08000000
blink-asm.elf: blink.o
arm-none-eabi-ld -T linker.ld -o blink-asm.elf blink.o
blink.o: blink.s
arm-none-eabi-as -o blink.o blink.s
clean:
rm -f blink.o blink-asm.elf blink-asm.bin
And here's hex for blink-asm.bin:
hexdump -C blink-asm.bin
00000000 00 50 00 20 30 01 00 08 00 00 00 00 00 00 00 00 |.P. 0...........|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000130 00 f1 01 00 ff f7 fc bf |........|
So first value which will be loaded at 0x08000000 is 20005000 which is end of the 20k SRAM.
Second value is reset exception handler address which is 08000130. However for ARM it must be encoded with lower bit, so it actually must be 08000131. If I would load current application code to MCU, it would generate hard fault instead of running reset_exception_handler.
Of course I can just write LONG(reset_exception_handler + 1);
in the linker script and it'll work as expected.
However I actually expected directive .thumb_func
to modify symbol reset_exception_handler
.
What's the proper way to write linker script and assembly file so they'd generate proper addresses?
Think or with one not add one, if you add one then when you use the tools correctly the add one now breaks it, the or one you can survive (to fix later).
As written in your question (as of my reading)
MEMORY {
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
SRAM : ORIGIN = 0x20000000, LENGTH = 20K
}
sp_initial_value = ORIGIN(SRAM) + LENGTH(SRAM);
vector_table_size = 0x130;
SECTIONS {
vector_table ORIGIN(FLASH) : {
LONG(sp_initial_value);
LONG(reset_exception_handler);
} > FLASH
.text ADDR(vector_table) + vector_table_size :
{
} > FLASH
}
.cpu cortex-m3
.syntax unified
.thumb
.text
.global reset_exception_handler
.thumb_func
.type reset_exception_handler, %function
reset_exception_handler:
add r0, 1
b reset_exception_handler
Disassembly of section vector_table:
08000000 <sp_initial_value-0x18005000>:
8000000: 20005000 andcs r5, r0, r0
8000004: 08000130 stmdaeq r0, {r4, r5, r8}
Disassembly of section .text:
08000130 <reset_exception_handler>:
8000130: f100 0001 add.w r0, r0, #1
8000134: f7ff bffc b.w 8000130 <reset_exception_handler>
There is no reason for a separate section for the vector table. Avoid linker script as much as possible, it is not the place to solve problems.
MEMORY {
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
SRAM : ORIGIN = 0x20000000, LENGTH = 20K
}
sp_initial_value = ORIGIN(SRAM) + LENGTH(SRAM);
SECTIONS {
.text : { *(.text) } > FLASH
.rodata : { *(.rodata) } > FLASH
.bss : { *(.bss) } > SRAM
}
.cpu cortex-m3
.syntax unified
.thumb
.text
.word sp_initial_value
.word reset_exception_handler
.thumb_func
reset_exception_handler:
b .
08000000 <reset_exception_handler-0x8>:
8000000: 20005000 andcs r5, r0, r0
8000004: 08000009 stmdaeq r0, {r0, r3}
08000008 <reset_exception_handler>:
8000008: e7fe b.n 8000008 <reset_exception_handler>
easier, cleaner, nicer. And correct.
Now you can also as you may see gcc do:
.cpu cortex-m3
.syntax unified
.thumb
.text
.word sp_initial_value
.word reset_exception_handler
.thumb_func
.type reset_exception_handler,%function
reset_exception_handler:
b .
which is just redundant, pick one.
.cpu cortex-m3
.syntax unified
.thumb
.text
.word sp_initial_value
.word reset_exception_handler
.type reset_exception_handler,%function
reset_exception_handler:
b .
08000000 <reset_exception_handler-0x8>:
8000000: 20005000 andcs r5, r0, r0
8000004: 08000009 stmdaeq r0, {r0, r3}
08000008 <reset_exception_handler>:
8000008: e7fe b.n 8000008 <reset_exception_handler>
but you have to pick one
.cpu cortex-m3
.syntax unified
.thumb
.text
.word sp_initial_value
.word reset_exception_handler
reset_exception_handler:
b .
08000000 <reset_exception_handler-0x8>:
8000000: 20005000 andcs r5, r0, r0
8000004: 08000008 stmdaeq r0, {r3}
08000008 <reset_exception_handler>:
8000008: e7fe b.n 8000008 <reset_exception_handler>
the .type..function is position independent and the same syntax is used in arm and thumb mode, there is no .arm_func. So if you are only a thumb/cortex-m programmer and/or a little lazy (I am in this case) then .thumb_func is nice because you can cut and paste it or type it and not have to type the function name. But only works for thumb. Having a .type habit is better since you can do it for all functions, but a little more work.
.cpu cortex-m3
.syntax unified
.thumb
.text
.type two,%function
.word sp_initial_value
.word reset_exception_handler
.word one
.word two
.thumb_func
one:
b .
two:
b .
reset_exception_handler:
b .
08000000 <one-0x10>:
8000000: 20005000 andcs r5, r0, r0
8000004: 08000014 stmdaeq r0, {r2, r4}
8000008: 08000011 stmdaeq r0, {r0, r4}
800000c: 08000013 stmdaeq r0, {r0, r1, r4}
08000010 <one>:
8000010: e7fe b.n 8000010 <one>
08000012 <two>:
8000012: e7fe b.n 8000012 <two>
08000014 <reset_exception_handler>:
8000014: e7fe b.n 8000014 <reset_exception_handler>
As basically answered in the comments, no reason to assume the linker script knows thumb stuff. (the linker does know about thumb certainly). Also as answered in that comment, just use assembly language.
And also as pointed out if you do not put at least a minimum number of vectors then if you have some other issue like unaligned or undefined instruction, etc, then you are going to fault and crash (ideally) or land in some strange place (re-) running some fraction of your code (unlucky).
The interrupts themselves do not come into play unless you do the work and I think it is up to chip vendor as to how many interrupts (and thus vectors) are used for a specific chip/core up to a max for that core (the different cores have a different max).