ARM Cortex-M4 Thumb2 Instruction Fault During Dynamic Linking with GCC

The issue at hand involves a fault occurring during the execution of a dynamically linked function on an ARM Cortex-M4 processor. The fault manifests as an INVSTATE exception, which is triggered when the processor attempts to execute a Thumb2 instruction with an invalid Execution Program Status Register (EPSR) value. Specifically, the fault occurs because the least significant bit (LSB) of the program counter (PC) is not set to 1, which is required for Thumb2 instructions. This issue arises when using the GCC toolchain to build Position Independent Code (PIC) for a shared library and linking it with a main application.

Thumb2 Instruction Encoding and EPSR State Mismatch

The ARM Cortex-M4 processor operates exclusively in Thumb2 mode, meaning all instructions must be Thumb2 encoded. Thumb2 instructions are 16-bit or 32-bit in length, and the processor relies on the EPSR.T bit to determine whether the current instruction stream is in Thumb state. When branching to an address, the LSB of the target address must be set to 1 to indicate Thumb mode. If this bit is not set, the processor will attempt to execute the instruction as an ARM instruction, leading to an INVSTATE fault.

In the provided scenario, the fault occurs at address 0x800010c, which is the entry point of the Procedure Linkage Table (PLT) stub. The PLT is used in dynamic linking to redirect function calls to the correct address at runtime. The fault is triggered because the address 0x800010c does not have its LSB set, causing the processor to misinterpret the instruction stream.

The root cause of this issue lies in the interaction between the GCC toolchain, the linker, and the ARM Cortex-M4’s Thumb2 execution model. The GCC toolchain generates code that assumes the LSB will be handled correctly during dynamic linking, but this assumption fails when the linker does not properly set the LSB for Thumb2 instructions in the PLT.

GCC Toolchain and Linker Behavior in Thumb2 Dynamic Linking

The GCC toolchain, when configured for Thumb2-only execution, should ensure that all branch targets, including those in the PLT, have their LSB set to 1. However, the current behavior suggests that the linker (arm-none-eabi-ld) is not correctly handling the LSB for Thumb2 instructions in the PLT. This results in the generation of an address (0x800010c) that does not conform to the Thumb2 execution model, leading to the INVSTATE fault.

The issue is further complicated by the fact that the Cortex-M4 does not support ARM mode instructions, meaning any attempt to execute an ARM instruction will result in a fault. The dynamic linking process, as implemented by the GCC toolchain, must therefore ensure that all addresses in the PLT and Global Offset Table (GOT) are correctly marked as Thumb2 addresses by setting their LSB to 1.

Correcting Thumb2 Instruction Encoding and Dynamic Linking Behavior

To resolve this issue, several steps must be taken to ensure that the GCC toolchain and linker correctly handle Thumb2 instruction encoding during dynamic linking:

  1. Modify the Linker Script: The linker script (stm32_linker.ld) should explicitly ensure that all PLT and GOT entries have their LSB set to 1. This can be achieved by adding a directive in the linker script to mark these sections as Thumb2 code.

  2. Use .thumb_func Directive: In assembly code, any function that will be called via the PLT should be marked with the .thumb_func directive. This ensures that the assembler generates the correct Thumb2 encoding and sets the LSB of the function address.

  3. Update GCC Toolchain: Ensure that the GCC toolchain being used supports Thumb2-only dynamic linking. The toolchain should be configured to generate Thumb2-compatible PLT and GOT entries, with the LSB set to 1 for all branch targets.

  4. Verify Dynamic Linker Behavior: If a dynamic linker is being used, it must be verified that it correctly handles Thumb2 instructions. The dynamic linker should ensure that all addresses it resolves and patches have their LSB set to 1.

  5. Debugging and Testing: Use a debugger to step through the dynamic linking process and verify that all addresses in the PLT and GOT have their LSB set to 1. This can be done by inspecting the disassembly of the generated binary and checking the values of the addresses.

Example Linker Script Modification

SECTIONS
{
    .plt : { *(.plt) } > FLASH
    .got : { *(.got) } > RAM
    .text : { 
        *(.text)
        *(.text.*)
        *(.rodata)
        *(.rodata.*)
        *(.glue_7)
        *(.glue_7t)
        *(.vfp11_veneer)
        *(.v4_bx)
        *(.thumb_func)
    } > FLASH
}

Example Assembly Code with .thumb_func Directive

.global library_function
.thumb_func
library_function:
    // Function implementation
    bx lr

Debugging Steps

  1. Disassemble the Binary: Use arm-none-eabi-objdump to disassemble the generated binary and inspect the PLT and GOT entries. Ensure that all addresses have their LSB set to 1.

  2. Step Through Execution: Use a debugger (e.g., GDB) to step through the execution of the program and verify that the LSB is set correctly when branching to the PLT.

  3. Check Fault Status Register: If a fault occurs, inspect the Configurable Fault Status Register (CFSR) to determine the cause of the fault. In this case, the INVSTATE bit should be set, indicating an invalid EPSR state.

Conclusion

The issue of Thumb2 instruction faults during dynamic linking on the ARM Cortex-M4 is a result of incorrect handling of the LSB in the PLT and GOT entries. By ensuring that the GCC toolchain, linker, and dynamic linker correctly handle Thumb2 instruction encoding, this issue can be resolved. Proper debugging and testing are essential to verify that all addresses in the PLT and GOT have their LSB set to 1, ensuring correct execution of Thumb2 instructions on the Cortex-M4.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *