Closed leyondlee closed 2 years ago
I tried implementing the initialization and saving of temp registers through pcode injection but I came across a "Low-level Error" with the decompiler on some functions. It seems to be originate from flow.cc#L131.
public void emitAssignRegisterToRegister(String dest, String src) {
Varnode out = findRegister(dest);
Varnode[] in = new Varnode[1];
in[0] = findRegister(src);
PcodeOp op = new PcodeOp(opAddress, seqnum++, PcodeOp.COPY, in, out);
opList.add(op);
}
initTempRegisters:
public class InjectInitTempRegisters extends InjectPayloadTileGx {
public InjectInitTempRegisters(String sourceName, SleighLanguage language, long uniqBase) {
super(sourceName, language, uniqBase);
}
@Override
public PcodeOp[] getPcode(Program program, InjectContext con) {
PcodeOpEmitter pCode = new PcodeOpEmitter(language, con.baseAddr, uniqueBase);
for (String regName : RegisterUtil.REGISTER_NAMES) {
String tempName = RegisterUtil.getTempName(regName);
pCode.emitAssignRegisterToRegister(tempName, regName); // temp = reg
}
return pCode.getPcodeOps();
}
}
saveTempRegisters:
public class InjectSaveTempRegisters extends InjectPayloadTileGx {
public InjectSaveTempRegisters(String sourceName, SleighLanguage language, long uniqBase) {
super(sourceName, language, uniqBase);
}
@Override
public PcodeOp[] getPcode(Program program, InjectContext con) {
PcodeOpEmitter pCode = new PcodeOpEmitter(language, con.baseAddr, uniqueBase);
for (String regName : RegisterUtil.REGISTER_NAMES) {
String tempName = RegisterUtil.getTempName(regName);
pCode.emitAssignRegisterToRegister(regName, tempName); // reg = temp
}
return pCode.getPcodeOps();
}
}
I tried implementing the initialization and saving of temp registers through pcode injection but I came across a "Low-level Error" with the decompiler on some functions. It seems to be originate from flow.cc#L131.
public void emitAssignRegisterToRegister(String dest, String src) { Varnode out = findRegister(dest); Varnode[] in = new Varnode[1]; in[0] = findRegister(src); PcodeOp op = new PcodeOp(opAddress, seqnum++, PcodeOp.COPY, in, out); opList.add(op); }
initTempRegisters:
public class InjectInitTempRegisters extends InjectPayloadTileGx { public InjectInitTempRegisters(String sourceName, SleighLanguage language, long uniqBase) { super(sourceName, language, uniqBase); } @Override public PcodeOp[] getPcode(Program program, InjectContext con) { PcodeOpEmitter pCode = new PcodeOpEmitter(language, con.baseAddr, uniqueBase); for (String regName : RegisterUtil.REGISTER_NAMES) { String tempName = RegisterUtil.getTempName(regName); pCode.emitAssignRegisterToRegister(tempName, regName); // temp = reg } return pCode.getPcodeOps(); } }
saveTempRegisters:
public class InjectSaveTempRegisters extends InjectPayloadTileGx { public InjectSaveTempRegisters(String sourceName, SleighLanguage language, long uniqBase) { super(sourceName, language, uniqBase); } @Override public PcodeOp[] getPcode(Program program, InjectContext con) { PcodeOpEmitter pCode = new PcodeOpEmitter(language, con.baseAddr, uniqueBase); for (String regName : RegisterUtil.REGISTER_NAMES) { String tempName = RegisterUtil.getTempName(regName); pCode.emitAssignRegisterToRegister(regName, tempName); // reg = temp } return pCode.getPcodeOps(); } }
I'd recommend avoiding pcode injection when you can. While it may seem easier to use for complicated instructions than implementing it in sleigh, I've been burned a few times by breaking changes to that api.
With that said, unfortunately I come offering no alternatives or other suggestions.
Thanks @astrelsky for the insight.
I tried simplifying initTempRegisters()
and saveTempRegisters()
using a loop and accessing the registers through addresses.
define space register type=register_space size=$(REGSIZE);
...
...
...
define register offset=0x1000 size=$(REGSIZE) [
temp_r0 temp_r1 temp_r2 temp_r3 temp_r4
temp_r5 temp_r6 temp_r7 temp_r8 temp_r9
temp_r10 temp_r11 temp_r12 temp_r13 temp_r14
temp_r15 temp_r16 temp_r17 temp_r18 temp_r19
temp_r20 temp_r21 temp_r22 temp_r23 temp_r24
temp_r25 temp_r26 temp_r27 temp_r28 temp_r29
temp_r30 temp_r31 temp_r32 temp_r33 temp_r34
temp_r35 temp_r36 temp_r37 temp_r38 temp_r39
temp_r40 temp_r41 temp_r42 temp_r43 temp_r44
temp_r45 temp_r46 temp_r47 temp_r48 temp_r49
temp_r50 temp_r51 temp_r52 temp_r53 temp_sp
temp_lr temp_r56 temp_idn0 temp_idn1 temp_udn0
temp_udn1 temp_udn2 temp_udn3
];
macro initTempRegisters() {
local i:8 = 0;
<loop>
local regAddr:8 = i * 8;
local tempAddr:8 = regAddr + 0x1000;
local val:8 = *[register] regAddr;
*[register] tempAddr = val;
i = i + 1;
if (i < 63) goto <loop>;
}
macro saveTempRegisters() {
local i:8 = 0;
<loop>
local regAddr:8 = i * 8;
local tempAddr:8 = regAddr + 0x1000;
local val:8 = *[register] tempAddr;
*[register] regAddr = val;
i = i + 1;
if (i < 63) goto <loop>;
}
But it seems to be decompiling into actual code (even though the access is to register_space
and not ram_space
)
You should be able to access registers via the register address space but it can be a bit finicky. I'll usually get warnings about the decompiler ignoring indirect accesses, which is exactly what is wanted anyway (access the variable as if in a register not like a pointer). I think the variable is causing the trouble here because in my case I started with a constant register address and the offset could be resolved to a constant.
It's going to be pretty ugly, but try unrolling the loop and see what happens.
The decompiler works fine when the registers are assigned individually.
macro initTempRegisters() {
temp_r0 = r0;
temp_r1 = r1;
temp_r2 = r2;
temp_r3 = r3;
temp_r4 = r4;
...
...
...
}
macro saveTempRegisters() {
r0 = temp_r0;
r1 = temp_r1;
r2 = temp_r2;
r3 = temp_r3;
r4 = temp_r4;
...
...
...
}
But the pcode display is truncated and does not show the all the pcodes.
You can control the number of lines in the pcode field via Edit->Tool Options->Listing Field->Pcode Field (from the code browser)
macro saveTempRegisters() { r0 = temp_r0;
I don't understand the need for the bulk saveTempRegisters
macro. Assuming the temp registers are used for all reads, why not let the actual instruction writes target the real register(s)? I assume the intent is to ensure that register reads do not reflect any modifications which may occur within the packed instruction.
I would definitely stay clear of any injection approach which should be uneccessary.
You can control the number of lines in the pcode field via Edit->Tool Options->Listing Field->Pcode Field (from the code browser)
Thanks 😃
macro saveTempRegisters() { r0 = temp_r0;
I don't understand the need for the bulk
saveTempRegisters
macro. Assuming the temp registers are used for all reads, why not let the actual instruction writes target the real register(s)? I assume the intent is to ensure that register reads do not reflect any modifications which may occur within the packed instruction.I would definitely stay clear of any injection approach which should be uneccessary.
Yes, the reads should not be affected by any register modifications within the instruction bundle.
In the current process, the actual registers are used for all reads while any writes to the registers (within the instruction bundle) are first written to the temp register. The (new) values in the temp register will then be written back to the actual registers after the last instruction in the bundle using the saveTempRegisters
macro.
In the current process, the actual registers are used for all reads while any writes to the registers (within the instruction bundle) are first written to the temp register.
I would think the reverse may produce better results within the decompiler. Read from temp shadow copies and write to actual registers only when modified by an instruction. If a temp is never read the copy should be ignored by decompiler.
I would think the reverse may produce better results within the decompiler. Read from temp shadow copies and write to actual registers only when modified by an instruction. If a temp is never read the copy should be ignored by decompiler.
Thanks, I will give it a try 👍
Another issue I am currently facing is regarding the function signatures. It seems to detect that all input registers (r0-r9) are part of the function.
<compiler_spec>
...
...
...
<default_proto>
<prototype name="default" extrapop="0" stackshift="0">
<input>
<pentry minsize="1" maxsize="8">
<register name="r0"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="r1"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="r2"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="r3"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="r4"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="r5"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="r6"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="r7"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="r8"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="r9"/>
</pentry>
</input>
<output>
<pentry minsize="1" maxsize="8">
<register name="r0"/>
</pentry>
</output>
</prototype>
</default_proto>
</compiler_spec>
That's also an issue with using the temp registers. It sees the reads from all the registers and assigns them as operands as it sees them all being read from. It should be fixed by swapping the role of read-from vs write-to temporaries.
Thanks, that works 😃
@leyondlee can this issue be closed now?
@leyondlee can this issue be closed now?
Yes, thanks for everyone's help
@leyondlee Are you willing to share your final revision of your sinc file?
@leyondlee Are you willing to share your final revision of your sinc file?
Hi, I am unfortunately unable to share the file due to a NDA.
However, the work can be replicated with reference to the first post and the TileGx ISA documentation.
Just note to use shadow copies of the registers in the p-code to maintain read before write. For example:
:move Dest_Y0, SrcA_Y0 EndPacket is phase=1 & Opcode_Y0=0xA & RRROpcodeExtension_Y0=0x2 & SrcB_Y0=0x3F & SrcA_Y0 & Dest_Y0 & EndPacket {
initTempRegisters(); # Copy values to temp register. temp_r0=r0, temp_r1=r1, temp_r2=r2...
Dest_Y0 = <SrcA_Y0's Temp Register>;
}
Hi,
I am in the progress of adding TileGx architecture to Ghidra. With reference to #4390
The processor specification states that: (UG401-ISA.pdf Section 1.2)
For example:
Should roughly translate to:
My current code:
This works but it breaks the pcode display and function signatures.
Image 1
Image 2
Note: r0, r1, ..., r9 are declared as inputs in compiler_spec
I am wondering if there is a more elegant way to implement this?