Re: Remote query for structure layout

Simon Marchi via Gdb
2021-03-31T13:00:16Z

On 2021-03-30 11:27 p.m., David Blaikie wrote:
> (let me know if I'm just being an unhelpful bystander here - I clearly
> don't have a lot of context)

I think it is interesting. If people are not interested they can just
skip reading this thread :).

> On Tue, Mar 30, 2021 at 5:16 PM Simon Marchi <simon.marchi@polymtl.ca> wrote:
>>
>> On 2021-03-30 6:44 p.m., David Blaikie wrote:
>>> If it's "just" some user-code, is there a variable of the desired type being declared around the function call?
>>
>> AFAIK, this type wouldn't be used by the FreeRTOS code at all, so no.
>> Again, here's my understanding, hopefully it's close enough to the
>> reality.
>>
>> When a trap occurs, FreeRTOS saves the current task's register values on
>> the task's stack, using some arch-specific assembly code. For example,
>> for RISC-V:
>>
>> https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/534eba66ce4a5bda45d5edeeb81ac5a3cf6d0df8/portable/GCC/RISC-V/portASM.S#L121
>
> Ah, OK. So this is part of signal handling - so it's not part of the
> code that's compiled into the user's program, for instance... not even
> in some system library linked in, necessarily, I guess?

It is part of "user code". Wih "big" OSes (e.g. Linux), the OS is
compiled on its own and the user programs are compiled separately. With
embedded RTOSes (at least those I worked with), it's just one big
program. Your user code includes <FreeRTOS.h> and calls FreeRTOS
functions to spawn to tasks.

>> The way these registers are pushed is an implementation detail of
>> FreeRTOS. And we can imagine that it can vary depending on the
>> compile-time FreeRTOS configuration,
>
> I guess in the worst case it could be totally dynamic - it could pick
> a different layout each time.

Yes, if it wanted to be really annoying :).

> (guess a side question: How's this different from other systems? I
> don't know how other/more common systems handle registers during
> signals)

I think Linux does the same, pushing the registers (and some information
about the signal) on the thread's stack before calling a signal handler.
The layout is defined by the ucontext structure, exposed in the Linux
ABI:

https://github.com/torvalds/linux/blob/master/arch/riscv/include/uapi/asm/ucontext.h

GDB hardcodes that layout here:

https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/riscv-linux-tdep.c;h=ca97a60128ffe681766e7e1600d3f24048bf1f68;hb=HEAD#l103

Being exposed by the Linux kernel and part of this ABI, this structure
is not likely to change. That makes it reasonable to hardcode it.

>> which OpenOCD doesn't know about.
>>
>> To get register values of scheduled out tasks, OpenOCD needs to
>> interpret these register values from the tasks' stacks.
>>
>> So Tim's suggestion is: have FreeRTOS declare a structure that has the
>> exact layout as the saved registers on the stack:
>>
>> struct freertos_saved_regs {
>> int x1;
>> int x5;
>> int x6;
>> ...
>> };
>>
>> Consumers could read that structure's layout from the DWARF info, and
>> read the register values based on that. That would be a lot more robust
>> than hard-coding in the consumers how FreeRTOS stores things.
>
> If practical experience has shown the hard-coding is not
> stable/reliable (than FreeRTOS does change its strategy from time to
> time - but it's always constant for any given build of the FreeRTOS) I
> guess.

My guess is that FreeRTOS does not change it that often, there's not
reason to. But it can vary from build to build due to compile-time
configuration of FreeRTOS (i.e. include or exclude support for using
some particular register). An external observer such as OpenOCD does
not have access to the compile-time configuration that was used to build
FreeRTOS, so it's difficult to know what the layout used by this
particular build is.

> But I'm not sure where FreeRTOS would expose this structure to user
> code - it's not like there's a system library header that user code
> must include...

There is, any code using FreeRTOS will include FreeRTOS.h. You can see
FreeRTOS more like a library than an OS. FreeRTOS is not involved at
all until the user program calls functions like xTaskCreate to create
some tasks and calls vTaskStartScheduler to hand over control to
FreeRTOS.

So if the structure was declared somewhere in the port's include file,
it would necessarily be seen by some CUs (those that invoke FreeRTOS
functions).

>> However, since that struct would never actually be used by FreeRTOS'
>> code, the compiler won't emit it. Hence the need to find a way to force
>> the compiler to include it in the DWARF.
>>
>> Does that clarify the situation?
>
> Somewhat - any lack of understanding is just my ignorance in this
> field/area in general, to be clear. (I'm also not a core gdb
> developer, so I'm not the sort of person you have to convince of
> anything - just a curious bystander trying to understand/maybe offer
> some insightful suggestions (I predominantly work on LLVM's debug info
> emission, so that's my background/connection))

Well, since it's related to debug info emission, it could land on your
plate at some point, it's good that you have some context.

Simon