Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 36 additions & 4 deletions arch/powerpc/arch_ppc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,12 @@ class PowerpcArchitecture: public Architecture
}

case PPC_ID_BCLRx:
// Keep linked branches as calls in LLIL so dataflow can distinguish
// a GOT-address helper returning through incoming LR from an indirect call.
if ((bo & 0x14) == 0x14)
result.AddBranch(FunctionReturn);
break;

case PPC_ID_VLE_SE_BLRx:
if (!instruction.flags.lk && (bo & 0x14) == 0x14)
result.AddBranch(FunctionReturn);
Expand Down Expand Up @@ -661,6 +667,11 @@ class PowerpcArchitecture: public Architecture
return "";
}

virtual BNIntrinsicClass GetIntrinsicClass(uint32_t intrinsic) override
{
return intrinsic == PPC_INTRIN_COPY_STRING_WORDS ? MemoryIntrinsicClass : GeneralIntrinsicClass;
}

virtual string GetIntrinsicName(uint32_t intrinsic) override
{
switch (intrinsic)
Expand All @@ -669,6 +680,8 @@ class PowerpcArchitecture: public Architecture
return "__builtin_clz";
case PPC_INTRIN_FRSP:
return "float_round";
case PPC_INTRIN_COPY_STRING_WORDS:
return "copy_string_words";
default:
if ((decodeFlags & DECODE_FLAGS_PS))
{
Expand All @@ -682,9 +695,7 @@ class PowerpcArchitecture: public Architecture

virtual std::vector<uint32_t> GetAllIntrinsics() override
{
// Highest intrinsic number currently is PPC_PS_INTRIN_END.
// If new extensions are added please update this code.
std::vector<uint32_t> result{PPC_PS_INTRIN_END};
std::vector<uint32_t> result{PPC_INTRIN_COPY_STRING_WORDS};

// Double check someone didn't insert a new intrinsic at the beginning of our enum since we rely
// on it to fill the next array.
Expand Down Expand Up @@ -728,6 +739,10 @@ class PowerpcArchitecture: public Architecture
return {NameAndType(Type::IntegerType(4, false))};
case PPC_INTRIN_FRSP:
return {NameAndType(Type::FloatType(4))};
case PPC_INTRIN_COPY_STRING_WORDS:
return {NameAndType("dest", Type::PointerType(GetAddressSize(), Type::IntegerType(1, false))),
NameAndType("source", Type::PointerType(GetAddressSize(), Type::IntegerType(1, false))),
NameAndType("count", Type::IntegerType(4, false))};
// for now, quantize is operating on the float in, and the gqr that holds the scale
default:
if ((decodeFlags & DECODE_FLAGS_PS))
Expand Down Expand Up @@ -762,6 +777,9 @@ class PowerpcArchitecture: public Architecture
return {Type::IntegerType(4, false)};
case PPC_INTRIN_FRSP:
return {Type::FloatType(4)};
case PPC_INTRIN_COPY_STRING_WORDS:
// Up to eight words loaded by lswi, preserved as native-width GPR values.
return vector<Confidence<Ref<Type>>>(8, Type::IntegerType(GetAddressSize(), false));
default:
if ((decodeFlags & DECODE_FLAGS_PS))
{
Expand All @@ -775,6 +793,7 @@ class PowerpcArchitecture: public Architecture

virtual bool GetInstructionLowLevelIL(const uint8_t* data, uint64_t addr, size_t& len, LowLevelILFunction& il) override
{
const size_t available = len;
size_t instructionLength = GetInstructionLength(data, len, decodeFlags);
if (instructionLength == 0)
{
Expand All @@ -783,7 +802,6 @@ class PowerpcArchitecture: public Architecture
}

len = instructionLength;

Instruction instruction;
if (!FillInstruction(&instruction, data, instructionLength, addr, DECODE_FLAGS_VLE_TRANSLATE))
{
Expand All @@ -792,6 +810,20 @@ class PowerpcArchitecture: public Architecture
return false;
}

// The default lifter supplies the remaining bytes of the current basic block.
// Keep instruction decoding at four bytes; only lifting consumes the pair.
if (!(decodeFlags & DECODE_FLAGS_VLE) && instruction.id == PPC_ID_LSWI && available >= 8
&& !il.GetLabelForAddress(this, addr + 4))
{
Instruction store;
if (FillInstruction(&store, data + 4, 4, addr + 4)
&& GetLowLevelILForPPCStringCopy(this, il, &instruction, &store))
{
len = 8;
return true;
}
}

return GetLowLevelILForPPCInstruction(this, il, &instruction, addr);
}

Expand Down
107 changes: 107 additions & 0 deletions arch/powerpc/il.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,15 @@ static bool LiftBranches(Architecture* arch, LowLevelILFunction &il, const Instr

if (instruction->flags.lk)
{
if (blr && !wasConditionalBranch)
{
// BLRL branches through the old LR while setting LR to the next
// instruction. Preserve that write if analysis turns the call into a return.
il.AddInstruction(il.SetRegister(addressSize_l, LLIL_TEMP(0), expr));
il.AddInstruction(il.SetRegister(addressSize_l, PPC_REG_LR,
il.ConstPointer(addressSize_l, addr + instruction->numBytes)));
expr = il.Register(addressSize_l, LLIL_TEMP(0));
}
il.AddInstruction(il.Call(expr));
if (wasConditionalBranch)
il.AddInstruction(il.Goto(*falseLabel));
Expand Down Expand Up @@ -500,6 +509,94 @@ static void load_float(LowLevelILFunction& il,
}
}

bool GetLowLevelILForPPCStringCopy(Architecture* arch, LowLevelILFunction& il, Instruction* load, Instruction* store)
{
if (load->id != PPC_ID_LSWI || store->id != PPC_ID_STSWI
|| load->numOperands != 3 || store->numOperands != 3
|| load->operands[0].reg != store->operands[0].reg
|| load->operands[2].uimm != store->operands[2].uimm)
return false;

const uint32_t firstReg = load->operands[0].reg;
const uint32_t sourceReg = load->operands[1].reg;
const uint32_t destReg = store->operands[1].reg;
const uint32_t count = load->operands[2].uimm ? load->operands[2].uimm : 32;
if (firstReg == PPC_REG_GPR0 && sourceReg == PPC_REG_GPR0)
return false; // Invalid lswi form.

vector<RegisterOrFlag> outputs;
for (uint32_t offset = 0; offset < count; offset += 4)
{
const uint32_t reg = PPC_REG_GPR0 + ((firstReg - PPC_REG_GPR0 + offset / 4) % 32);
// Reject invalid loads and stores whose destination address is changed by the load.
if ((sourceReg != PPC_REG_GPR0 && reg == sourceReg) || (destReg != PPC_REG_GPR0 && reg == destReg))
return false;
outputs.push_back(RegisterOrFlag::Register(reg));
}

// Snapshot all source bytes before writing the destination, including when the
// ranges overlap. Outputs retain the loaded words (zero-padded and zero-extended)
// for later register uses, exactly as with the separate lswi and stswi.
const size_t addressSize = arch->GetAddressSize();
il.AddInstruction(il.Intrinsic(outputs, PPC_INTRIN_COPY_STRING_WORDS, {
operToIL(il, &store->operands[1], OTI_GPR0_ZERO, 0, addressSize),
operToIL(il, &load->operands[1], OTI_GPR0_ZERO, 0, addressSize), il.Const(4, count)}));
return true;
}

static void LiftStringWord(Architecture* arch, LowLevelILFunction& il, Instruction* instruction)
{
const size_t addressSize = arch->GetAddressSize();
const bool littleEndian = arch->GetEndianness() == LittleEndian;
const bool load = instruction->id == PPC_ID_LSWI;
const uint32_t count = instruction->operands[2].uimm ? instruction->operands[2].uimm : 32;
const uint32_t firstReg = instruction->operands[0].reg;
const ExprId base = operToIL(il, &instruction->operands[1], OTI_GPR0_ZERO, 0, addressSize);
auto address = [&](uint32_t offset) {
return offset ? il.Add(addressSize, base, il.Const(addressSize, offset)) : base;
};
// String instructions place bytes left to right in the low word of each GPR.
auto loadBytes = [&](size_t size, uint32_t offset) {
ExprId value = il.Load(size, address(offset));
return littleEndian && size > 1 ? il.ByteSwap(size, value) : value;
};
auto storeBytes = [&](size_t size, uint32_t offset, ExprId value) {
if (littleEndian && size > 1)
value = il.ByteSwap(size, value);
il.AddInstruction(il.Store(size, address(offset), value));
};

for (uint32_t offset = 0; offset < count; offset += 4)
{
const uint32_t reg = PPC_REG_GPR0 + ((firstReg - PPC_REG_GPR0 + offset / 4) % 32);
const uint32_t remaining = count - offset;
const size_t size = remaining >= 4 ? 4 : remaining >= 2 ? 2 : 1;
if (load)
{
ExprId value = loadBytes(size, offset);
if (size < 4)
value = il.ShiftLeft(4, il.ZeroExtend(4, value), il.Const(1, (4 - size) * 8));
// A three-byte tail uses a halfword and a byte, without reading past the string.
if (remaining == 3)
value = il.Or(4, value, il.ShiftLeft(4, il.ZeroExtend(4, loadBytes(1, offset + 2)),
il.Const(1, 8)));
if (addressSize == 8)
value = il.ZeroExtend(8, value);
il.AddInstruction(il.SetRegister(addressSize, reg, value));
}
else
{
ExprId value = il.Register(4, reg);
if (size < 4)
value = il.LowPart(size, il.LogicalShiftRight(4, value, il.Const(1, (4 - size) * 8)));
storeBytes(size, offset, value);
if (remaining == 3)
storeBytes(1, offset + 2,
il.LowPart(1, il.LogicalShiftRight(4, il.Register(4, reg), il.Const(1, 8))));
}
}
}

/* returns TRUE - if this IL continues
FALSE - if this IL terminates a block */
bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il,
Expand Down Expand Up @@ -937,6 +1034,11 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il,
}
break;

case PPC_ID_LSWI:
REQUIRE3OPS
LiftStringWord(arch, il, instruction);
break;

case PPC_ID_LMW:
REQUIRE2OPS
for (i = oper0->reg; i <= PPC_REG_GPR31; ++i)
Expand Down Expand Up @@ -1318,6 +1420,11 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il,
il.AddInstruction(ei0);
break;

case PPC_ID_STSWI:
REQUIRE3OPS
LiftStringWord(arch, il, instruction);
break;

case PPC_ID_STMW:
REQUIRE2OPS
for (i = oper0->reg; i <= PPC_REG_GPR31; ++i)
Expand Down
2 changes: 2 additions & 0 deletions arch/powerpc/il.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,10 @@ enum PPCIntrinsic : uint32_t
PPC_PS_INTRIN_QUANTIZE,
PPC_PS_INTRIN_DEQUANTIZE,
PPC_PS_INTRIN_END,
PPC_INTRIN_COPY_STRING_WORDS,
PPC_INTRIN_INVALID = 0xFFFFFFFF,
};


bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction& il, Instruction* instruction, uint64_t addr);
bool GetLowLevelILForPPCStringCopy(Architecture* arch, LowLevelILFunction& il, Instruction* load, Instruction* store);
Loading
Loading