# Protected Object ceiling priority

**URL:** https://forum.ada-lang.io/t/protected-object-ceiling-priority/4645
**Category:** Help
**Tags:** embedded, embedded-stm32f429di
**Created:** [August 18, 2026, 11:54pm UTC](https://forum.ada-lang.io/t/protected-object-ceiling-priority/4645 "2026-08-18T23:54:19Z")
**Posts on this page:** 15
**Page:** 1

<div class="post-metadata">

### Author: ![sidisyom](https://forum.ada-lang.io/letter_avatar_proxy/v4/letter/s/5fc32e/32.png) [@sidisyom](https://forum.ada-lang.io/u/sidisyom)
#### Post date: [August 18, 2026, 11:54pm UTC](https://forum.ada-lang.io/t/protected-object-ceiling-priority/4645/1 "2026-08-18T23:54:19Z")

</div>

Hi,

I’ve written a small program which is targeting the `embedded-stm32f429disco` runtime and have come across an issue related to tasks accessing a protected object.

The details of the program aren’t too important for this I reckon (characters that get generated randomly from a task get printed into a minicom console via a series of daisy-chained USARTs) but consider the following:

```ada
task type Character_Stream with Priority => System.Max_Priority;
   
task type Character_Stream_Consumer with Priority => System.Max_Priority - 1;
   
task type USART_Consumer with Priority => System.Max_Priority - 2;

```

and then:

```ada
protected Buffer with Priority => System.Max_Priority is
            
     procedure Put (C : Character ; OK : out Boolean);
      
     procedure Get (C : out Character ; OK : out Boolean);
.........................................................................
.........................................................................

```

with `Character_Stream` and `Character_Stream_Consumer` both calling the `Put` and `Get` procedures respectively (the buffer is declared as a straight object than a type).

Now, the moment `Character_Stream` calls `Put` the program traps immediately and through some weird machinery of `__gnat_last_chance_handler` and local exception handling (allowed in Ravenscar) which can be the topic of a separate post, I managed to narrow this down to `procedure Lock (Object : Protection_Access)` inside `System.Tasking.Protected_Objects` in `./arm-eabi/lib/gnat/embedded-stm32f429disco/gnarl/s-taprob.adb`:

 ![Screenshot from 2026-08-19 00-19-31](https://forum.ada-lang.io/uploads/default/original/2X/c/c626ab6a26d36f5bd81b0fbbe18cb0928b85dcf1.png)

where the explicit `raise` takes place.

But that conditional is for `>` and not for `>=` which as the comment above explains is perfectly fine, so the call from `Character_Stream` should be accepted, shouldn’t it?

Does the protected object need to be configured with a specific ceiling protocol or something like that?

Any thoughts? - Thanks!

---

<div class="post-metadata">

### Author: ![Max](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/max/32/21_2.png) [@Max](https://forum.ada-lang.io/u/Max)
#### Post date: [August 19, 2026, 5:32am UTC](https://forum.ada-lang.io/t/protected-object-ceiling-priority/4645/2 "2026-08-19T05:32:12Z")

</div>

You are right, `Character_Stream` calls should be accepted. Try to see in the debugger value of the properties. Probably you have corrupted memory or exhausted stack.

---

<div class="post-metadata">

### Author: ![dmitry-kazakov](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/dmitry-kazakov/32/522_2.png) [@dmitry-kazakov](https://forum.ada-lang.io/u/dmitry-kazakov)
#### Post date: [August 19, 2026, 7:54am UTC](https://forum.ada-lang.io/t/protected-object-ceiling-priority/4645/3 "2026-08-19T07:54:38Z")

</div>

The object ceiling can be dynamically changed through the attribute 'Priority ([ARM D.5.2](https://www.adaic.org/resources/add_content/standards/22aarm/html/AA-D-5-2.html)). Could it be the case?

P.S. You do not need protected objects for a non-blocking LIFO. In your case it is non-blocking because you made Put and Get procedures rather than entries.

---

<div class="post-metadata">

### Author: ![sidisyom](https://forum.ada-lang.io/letter_avatar_proxy/v4/letter/s/5fc32e/32.png) [@sidisyom](https://forum.ada-lang.io/u/sidisyom)
#### Post date: [August 19, 2026, 9:19pm UTC](https://forum.ada-lang.io/t/protected-object-ceiling-priority/4645/4 "2026-08-19T21:19:43Z")

</div>

Yeah, this is what I’m trying to do but struggling a bit to include the runtime symbols in the elf file so that they are available for debugging (obviously compiling with `-g`).

Any tips? 🙂

---

<div class="post-metadata">

### Author: ![sidisyom](https://forum.ada-lang.io/letter_avatar_proxy/v4/letter/s/5fc32e/32.png) [@sidisyom](https://forum.ada-lang.io/u/sidisyom)
#### Post date: [August 19, 2026, 9:39pm UTC](https://forum.ada-lang.io/t/protected-object-ceiling-priority/4645/5 "2026-08-19T21:39:21Z")

</div>

> [@dmitry-kazakov](#):
>
> The object ceiling can be dynamically changed through the attribute 'Priority ([ARM D.5.2](https://www.adaic.org/resources/add_content/standards/22aarm/html/AA-D-5-2.html)). Could it be the case?

Oh, that’s a good point, didn’t think of that at all! But this should actually have the desired effect already i.e. the priority of the entering task is raised to that of the protected object (if lower) so that it can’t be preempted by a mid-priority task and therefore cause priority inversion to a higher priority task (assuming the priorities of all participants have been worked out correctly, that is). But it’s not raised higher than that of the protected object which seems to be the issue here (and the check is done before setting the priority).

The runtime code actually shows that:

 ![Screenshot from 2026-08-19 22-14-20](https://forum.ada-lang.io/uploads/default/original/2X/c/ca2b5edf195159de5daace83a4a92ca92da5d94a.png)

> [@dmitry-kazakov](#):
>
> You do not need protected objects for a non-blocking LIFO. In your case it is non-blocking because you made Put and Get procedures rather than entries.

Yeah, the intention for this one is to be a queue so that characters are added at the tail and removed at the head (i.e. characters are printed on minicom in the same order as they are generated in the mcu).  
Adding more than one entry in Ravenscar isn’t allowed due to the `Max_Protected_Entries => 1` restriction so implementing a shared queue requires the use of two `Suspension_Object`’s whose state is constantly flipped by the consumer and producer tasks.

---

<div class="post-metadata">

### Author: ![Max](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/max/32/21_2.png) [@Max](https://forum.ada-lang.io/u/Max)
#### Post date: [August 20, 2026, 6:21am UTC](https://forum.ada-lang.io/t/protected-object-ceiling-priority/4645/6 "2026-08-20T06:21:41Z")

</div>

Rebuilding runtime should be simple:

> **[GitHub - AdaCore/bb-runtimes: Source repository for the GNAT Bare Metal BSPs](https://github.com/adacore/bb-runtimes#rebuild-of-a-runtime)**
>
> Source repository for the GNAT Bare Metal BSPs

---

<div class="post-metadata">

### Author: ![dmitry-kazakov](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/dmitry-kazakov/32/522_2.png) [@dmitry-kazakov](https://forum.ada-lang.io/u/dmitry-kazakov)
#### Post date: [August 20, 2026, 7:01am UTC](https://forum.ada-lang.io/t/protected-object-ceiling-priority/4645/7 "2026-08-20T07:01:47Z")

</div>

> [@sidisyom](#):
>
> Yeah, the intention for this one is to be a queue so that characters are added at the tail and removed at the head (i.e. characters are printed on minicom in the same order as they are generated in the mcu).

Same for FIFO. Use atomic in and out indices with a ring buffer. That suffice because producer and consumer modify only one of the indices and buffer full/empty tests are read-only. No protected object is needed so long you deploy busy waiting as you do.

> [@sidisyom](#):
>
> Adding more than one entry in Ravenscar isn’t allowed due to the `Max_Protected_Entries => 1` restriction

You need only Get to be an entry (wait for a character). Put can remain procedure raising Program\_Error when buffer is full.

---

<div class="post-metadata">

### Author: ![sidisyom](https://forum.ada-lang.io/letter_avatar_proxy/v4/letter/s/5fc32e/32.png) [@sidisyom](https://forum.ada-lang.io/u/sidisyom)
#### Post date: [August 24, 2026, 3:47pm UTC](https://forum.ada-lang.io/t/protected-object-ceiling-priority/4645/8 "2026-08-24T15:47:14Z")

</div>

So, I copied the entire directory of this runtime into a new directory with a `-debug` suffix and then ran the following inside the new directory (i.e. `embedded-stm32f429disco-debug`):

`gprbuild -j0 -P ravenscar_build.gpr -XBUILD=Debug`

which completed successfully.

I then pointed my alire gpr file to it:

`for Runtime ("Ada") use "embedded-stm32f429disco-debug";`

and ran an `alr build` which also completed successfully.

But trying to `load` the elf file in gdb results in the following error:

```ada
Launching: st-util -p 4242 --connect-under-reset --semihosting
Launching debugger.
[2026-08-24 16:31:33] Language unknown, defaulting to C: asm

```

With the full `load` command being something like:

```ada
(gdb) load
Loading section .text, size 0x20018 lma 0x8000000
+download,{section=.text,section-size=131096,total-size=1142213}
+download,{section=.text,section-sent=16128,section-size=131096,total-sent=16128,total-size=1142213}
+download,{section=.text,section-sent=131096,section-size=131096,total-sent=131096,total-size=1142213}
~Loading section .ARM.extab, size 0x1fbc lma 0x8020018
+download,{section=.ARM.extab,section-size=8124,total-size=1142213}
~Loading section .ARM.exidx, size 0x1a40 lma 0x8021fd4
+download,{section=.ARM.exidx,section-size=6720,total-size=1142213}
~Loading section .rodata, size 0x414c lma 0x8023a18
+download,{section=.rodata,section-size=16716,total-size=1142213}
Loading section .data, size 0xe6c lma 0x8027b64
+download,{section=.data,section-size=3692,total-size=1142213}
Start address 0x0801ccc4, load size 166348
Transfer rate: 18 KB/sec, 9241 bytes/write.

```

and when I try to `continue` it aborts:

```ada
(gdb) c
Continuing.
[program running]
Command aborted.

```

Any ideas how I can troubleshoot this further? 🤔

Is it something to do with the file size? The debug one is naturally larger (~1.3M) whereas the non-debug one (which continues to load and run fine) is in the 950K region.

Thanks!

---

<div class="post-metadata">

### Author: ![Max](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/max/32/21_2.png) [@Max](https://forum.ada-lang.io/u/Max)
#### Post date: [August 25, 2026, 4:55pm UTC](https://forum.ada-lang.io/t/protected-object-ceiling-priority/4645/9 "2026-08-25T16:55:07Z")

</div>

After that I usually do (in `arm-eabi-gdb` console): (At least it works with OpenOCD, but probably should work with `st-util` also)

```ada
br __gnat_last_chance_handler
mon reset init
cont

```

First command sets a breakpoint on exception handler. The second one resets the board. The last one launches the execution. If an exception occurs, GDB should stops in `__gnat_last_chance_handler` and you will be able to see backtrace (with `bt`), switch the frame (with `frame 4` or something) and examine the state with `print Var` or `info locals`.

---

<div class="post-metadata">

### Author: ![sidisyom](https://forum.ada-lang.io/letter_avatar_proxy/v4/letter/s/5fc32e/32.png) [@sidisyom](https://forum.ada-lang.io/u/sidisyom)
#### Post date: [August 25, 2026, 9:20pm UTC](https://forum.ada-lang.io/t/protected-object-ceiling-priority/4645/10 "2026-08-25T21:20:53Z")

</div>

Thanks for the suggestions 🙏

So, in my case the ` __gnat_last_chance_handler` is never hit..rather it goes straight to the default fault/trap handler declared in `handler.S`:

 ![Screenshot from 2026-08-25 21-55-57](https://forum.ada-lang.io/uploads/default/original/2X/e/e7b9f8621f0a66eb34ac330308e7d55199f62d88.png)

Following some suggestions from gpt and digging through the Cortex-M4 programming manual I can see that the `PRECISERR` & `BFARVALID` flags in the `CFSR` register (section `4.4.10` in the PM) are set which I believe suggest some kind of bus fault error.

I grabbed the addresses of the `psp` & `msp` from within the default fault handler and using `arm-eabi-addr2line` I got the following for `psp` :

```ada
arm-eabi-addr2line -e usart_flow -f -C 0x200012b0
__bss_start
??:?

```

and the following for the `msp`:

```ada
arm-eabi-addr2line -e usart_flow -f -C 0x200077d8
__stack_start
??:?

```

(I _believe_ the `lr` had a `psp` return address but not entirely sure)

So, maybe some unaligned access in `start-rom.S`? 🤔

Though, I can see in the linker script both those symbols are properly aligned:  
`. = ALIGN(0x8);`

One final thing, loading the elf file “manually” in an `arm-eabi-gdb` console doesn’t result in the previous error (where I was loading it via the GNAT Studio console).

I shall continue to investigate, thanks for your time.

---

<div class="post-metadata">

### Author: ![godunko](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/godunko/32/22_2.png) [@godunko](https://forum.ada-lang.io/u/godunko)
#### Post date: [August 26, 2026, 4:47am UTC](https://forum.ada-lang.io/t/protected-object-ceiling-priority/4645/11 "2026-08-26T04:47:33Z")

</div>

In case of bus fault, check address in BFAR (0xE000ED38) register (not in \*SP registers).

---

<div class="post-metadata">

### Author: ![Max](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/max/32/21_2.png) [@Max](https://forum.ada-lang.io/u/Max)
#### Post date: [August 26, 2026, 7:27am UTC](https://forum.ada-lang.io/t/protected-object-ceiling-priority/4645/12 "2026-08-26T07:27:08Z")

</div>

After `mon reset init` if you execute several `si` (step by one asm instruction), does it start executing instructions from `start-rom.S`?

---

<div class="post-metadata">

### Author: ![damaki](https://forum.ada-lang.io/letter_avatar_proxy/v4/letter/d/3bc359/32.png) [@damaki](https://forum.ada-lang.io/u/damaki)
#### Post date: [August 26, 2026, 9:56am UTC](https://forum.ada-lang.io/t/protected-object-ceiling-priority/4645/13 "2026-08-26T09:56:37Z")

</div>

The address of `psp` is in the range of the symbol `sec_default_sized_stacks`, which is unusual since this is the area for secondary stacks, not primary stacks. So it looks to me like you may be encountering a stack overflow in one of your tasks, where the task is overflowing and corrupting adjacent memory locations. This would also explain why you were seeing a ceiling locking violation in your original message, since a stack overflow may be corrupting the `Caller_Priority`. I’ve seen similar things before caused by stack overflows in the past.

Since you’re using the “embedded” runtime profile which supports exception propagation, you could try turning on stack checking with `-fstack-check` and a `Storage_Error` exception should then be raised in the task that is overflowing, which may help with identifying the offending task.

You can set the stack size for tasks using the `Storage_Size` aspect or pragma, for example:

```ada
task type Character_Stream with 
  Priority => System.Max_Priority,
  Storage_Size => 8 * 1024; -- 8 kB stack

```

---

<div class="post-metadata">

### Author: ![sidisyom](https://forum.ada-lang.io/letter_avatar_proxy/v4/letter/s/5fc32e/32.png) [@sidisyom](https://forum.ada-lang.io/u/sidisyom)
#### Post date: [August 26, 2026, 9:41pm UTC](https://forum.ada-lang.io/t/protected-object-ceiling-priority/4645/14 "2026-08-26T21:41:11Z")

</div>

It was a stack overflow indeed! 🙂

Doubling the primary stack as suggested fixed the initial issue but also that in the debug runtime too so that I was able to step through `Lock` inside `System.Tasking.Protected_Objects` and check the `Caller_Priority` value. 👍

Interesting how that was corrupting the priority value for the task. It looks as if this was overshooting even the secondary stack and landing into the area right next to that where (presumably) the task attributes are stored..? 🤔

One thing to note is that initially I also gave the `-fstack-check` a try to prove this suggestion and effectively did the comb inside `Character_Stream` in something like:

```ada
declare
    ....................
 begin
    ....................
 exception
    when Storage_Error => 
       declare
          I : Integer;
       begin
          I := I + 1;
       end;
 end;

```

with `I` merely declared so that I could have something to place a breakpoint to but that never got hit and the flow was again going straight to the fault handler in `handler.S`

Assume this is where the exception handling logic needs to go as only local handling is allowed in this profile?

Set it this way:

```ada
package Compiler is
  for Default_Switches ("Ada") use Stm32F429_Discovery_Full.Compiler'Default_Switches ("Ada") & ("-fstack-check");
end Compiler;

```

Many thanks all for your time and all the suggestions, that was really useful. 🙏

---

<div class="post-metadata">

### Author: ![system](https://forum.ada-lang.io/uploads/default/original/1X/e625e7957e269cbf6d3128dab48a9e54fe3a9bdc.png) [@system](https://forum.ada-lang.io/u/system)
#### Post date: [August 27, 2026, 9:42pm UTC](https://forum.ada-lang.io/t/protected-object-ceiling-priority/4645/15 "2026-08-27T21:42:08Z")

</div>

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.
