Skip to content

__get_current_exception() located in flash RAM in copy_to_ram builds #1015

@alastairpatrick

Description

@alastairpatrick

__get_current_exception is very simple and locating it in a section that is copied to RAM by copy_to_ram builds or making it inline could be justified on that basis alone. However, it goes deeper...

This is the implementation of __get_current_exception:

.section .reset, "ax"
//...
.global __get_current_exception
.thumb_func
__get_current_exception:
    mrs  r0, ipsr
    uxtb r0, r0
    bx   lr

Because it is in the .reset section, it is not copied to RAM when using the copy_to_ram binary type. This means that programs built with copy_to_ram might unexpectedly access flash RAM, which could cause poor behavior if, for example, the other core is writing to flash RAM or if the program has hard real-time constraints with regard to interrupt latency or jitter.

Here is one way to reproduce. It configures the MPU to hard fault if flash RAM is accessed, as a program that writes to flash RAM or one that has hard real-time constraints might,

CMakeLists.txt:

cmake_minimum_required(VERSION 3.13)

add_executable(example
  main.c
)

pico_set_binary_type(example copy_to_ram)

target_link_libraries(example
  hardware_timer
  pico_stdlib
)

pico_add_extra_outputs(example)

main.c:

#include "hardware/structs/mpu.h"
#include "hardware/timer.h"

// Configure MPU to cause a hard fault if flash RAM is accessed.
void protect_flash() {
  mpu_hw->rbar = XIP_BASE | M0PLUS_MPU_RBAR_VALID_BITS;
  mpu_hw->rasr = 0x10000000 /* XN */ | (23 << M0PLUS_MPU_RASR_SIZE_LSB) | M0PLUS_MPU_RASR_ENABLE_BITS;  // 2^(23+1) = 16MB
  mpu_hw->ctrl = M0PLUS_MPU_CTRL_ENABLE_BITS | M0PLUS_MPU_CTRL_PRIVDEFENA_BITS;
}

int main() {
  protect_flash();

  // This crashes in debug builds.
  sleep_ms(1000);

  // This would crash for any build type.
  __get_current_exception();
}

This is the crash:

> bt
#0  isr_hardfault () at D:\src\qos\pico-sdk\src\rp2_common\pico_standard_link\crt0.S:98
#1  <signal handler called>
#2  __get_current_exception () at D:\src\qos\pico-sdk\src\rp2_common\pico_standard_link\crt0.S:331
#3  0x200010f4 in sleep_until (t=...) at D:\src\qos\pico-sdk\src\common\pico_time\time.c:347
#4  0x20001214 in sleep_us (us=<optimized out>) at D:\src\qos\pico-sdk\src\common\pico_time\include/pico/time.h:102
#5  0x2000124e in sleep_ms (ms=ms@entry=0x3e8) at D:\src\qos\pico-sdk\src\common\pico_time\time.c:393
#6  0x20000136 in main () at D:\src\qos\examples\main.c:13
void sleep_until(absolute_time_t t) {
#if PICO_ON_DEVICE && !defined(NDEBUG)
    if (__get_current_exception()) {                       // <-- CRASH
        panic("Attempted to sleep inside of an exception handler; use busy_wait if you must");
    }
   ...
#endif

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions