Search code examples
assemblylinkerarmstm32

thumb_func directive is not accounted for


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?


Solution

  • 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).