Re: Remote query for structure layout
David Blaikie via Gdb <[email protected]>
| Newsgroups | gmane.comp.gdb.devel |
|---|---|
| Message-ID | <CAENS6Eteo7ZwOatL8yQ=Zp9fpMmqwNi4d8T9ixoz-zKW=kjMJA@mail.gmail.com> |
On Wed, Mar 31, 2021 at 6:00 AM Simon Marchi <[email protected]> wrote: > > 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 <[email protected]> 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. Ah, fair enough - good to understand and compare/contrast. > >> 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. Fair enough. > > 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. Oh, that helps a lot, then. > 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. OK > 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). Great! One possibly "cheap" way would be for FreeRTOS to use the structure in C code to do the register stashing - rather than or in addition to/in some kind of hybrid manner along with the assembly. If that's not possible/would make the code harder to understand, then finding some other way to pin the data structure into the DWARF would be needed. I'd be inclined to avoid adding a new attribute or other feature (though I wouldn't be entirely opposed to it - I can certainly think of other places where it may be useful to have a "whenever the compiler sees this type, ensure it makes it into the resulting DWARF no matter what" attribute) if reasonably possible - instead finding some lowest-common-denominator/highly reliable signal that compilers use to emit type information. The simplest such signal is a global variable of the type (not a pointer to it, but of the type itself) - though probably has to be annotated in some way that ensures the compiler won't optimize the variable away even under LTO, etc. I'm not sure sure off-hand if I have a great way to do that portably (non-portably I guess there's various "exported" attributes that ensure the entity is visible even beyond the shared library/executable scope, and thus can't be optimized away - maybe such an attribute would be sufficiently portable for FreeRTOS's needs (ie: no less portable than the rest of the code already)). If such a variable would be problematic (taking up space in the image, etc) - maybe there's a way to ensure it through an extra nonce parameter (if it's C++ code, a parameter with a default argument - so the caller never has to think about it) to some core FreeRTOS function that won't be optimized away/removed as unused/etc. Again, has a runtime impact, though - maybe if it was zero-size (like a class template specialization with no members) & with the type as a template parameter /maybe/ that would suffice... Happy to play around with mechanisms like that if it's of interest. > >> 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.