Open muff1n1634 opened 12 months ago
There is a change in the works for this type of allocation scheme as well as other types of allocations schemes. I can't give you a time frame when it will be finished as another developer is working on it.
As you mention, it can be done manually, but is tedious and would be much better done automatically.
That's good to hear 🙂
Is your feature request related to a problem? Please describe.
In my time using Ghidra to disasemble PowerPC (specifically,
ppc32-eabi
), I have come across functions whose prototypes containlong long
arguments. Normally, Ghidra allocates these into the stack, because they are larger than the natural word size of the processor. However, according to the ABI (the SysV ABI, PowerPC Processor Supplement[^abi], p.3-20
),LONG_LONG
arguments must go in odd-even register pairs, skipping registers as necessary to align (unless doing so would go pastr10
, in which case it would go into the stack.)[^abi]: The actual PowerPC EABI 32-bit Implementation (p.
5
-6
) mostly follows the SysV PowerPC ABI. Importantly, it does not change parameter passing rules forlong long
values.Neither of the current allocation strategies do this; they both use the first register that is available and large enough.
Describe the solution you'd like
I would like a new allocation strategy that intentionally skips registers to be able to accurately represent the parameter passing rules of this ABI.
For example, given the following
pentries
and the C function prototype
this new strategy would behave as follows.
pentry
1.pentry
1 allows for 4 byte wide parameters, andr3
has not been used, sopentry
1 is valid. Assignarg1
tor3
.pentry
1 (not from the beginning). Find nextpentry
2.pentry
2 does not allow 8 byte wide parameters, sopentry
2 is not valid.pentry
3.pentry
3 allows for 8 byte wide parameters, butr3
has already been used, sopentry
3 is not valid.pentry
4.pentry
4 does not allow 8 byte wide parameters, sopentry
4 is not valid.pentry
5.pentry
5 does not allow 8 byte wide parameters, sopentry
5 is not valid.pentry
6.pentry
6 allows for 8 byte wide parameters, and neitherr5
norr6
have been used, sopentry
6 is valid. Assignarg2
tor5
andr6
.pentry
6 (not from the beginning). Find nextpentry
7.pentry
7 allows for 4 byte wide parameters, andr7
has not been used, sopentry
7 is valid. Assignarg3
tor7
.pentry
7 (not from the beginning). Find nextpentry
8.pentry
8 allows for 2 byte wide parameters, andr8
has not been used, sopentry
8 is valid. Assignarg4
tor8
.Basically, what the standard strategy does, but instead of starting at the beginning of the list, it starts at the
pentry
it just allocated.This would follow the ABI's rules about skipping registers that aren't aligned to the type's natural alignment.
Describe alternatives you've considered
The obvious alternative is to use Custom Storage to manually correct any incorrect allocation, but for more than a few functions, this gets tedious.
Another alternative I considered was editing and importing a
.cspec
prototype that added thepentries
seen above and then use Custom Storage from there, but it would be an imported Specification Extension, so I'm not sure how compatible that is with future versions of Ghidra.Additional context
During my research into this topic, I found out that there are other ABIs with this behavior of skipping registers: notably, the RISC-V ABI (p.
90
) and the ARM ABI (p.21
). Therefore, this new allocation strategy would not be limited in usefulness to just PowerPC code; people disassembling RISC-V or ARM code could make more accurate prototypes with this new allocation strategy as well.I put a small test case in a little bundle. Originally this was to show an updated prototype that could handle
long long
parameters more accurately than the normal PowerPC__stdcall
prototype by storing them in aligned registers (for a different issue in a different repo), but I believe the premise of this feature request is similar enough that I've also put it here.stdcall_oe_test.zip
__stdcall_oe.xml
- The aforementioned second alternative I considered, where I addedpentries
for thelong long
parameters to be able to be stored in registers.stdcall_oe_test.c
- source file withvoid f(int arg1, void *arg2, long long arg3, short arg4)
andvoid g(int arg1, long long arg2, void *arg3, short arg4)
to show the differences in register allocation for different placements of thelong long
parameter.stdcall_oe_test.o
- compiled with Metrowerks CW 4.3 with the PowerPC EABI[^object].To test the
__stdcall_oe.xml
prototype (oe
= odd-even pairs), loadstdcall_oe_test.o
into Ghidra and go toEdit
->Options for <program name>
->Specification Extensions
->Import...
[^object]: However, I've also tested this by compiling with Clang and
--target=arm-none-eabi
, in which caseg()
becomes the normal case, andf()
becomes the problem case, as the ARM ABI uses even-odd alignment, starting atr0
.