Skip to content

Commit

Permalink
Use special function to access pointers in fs and gs segments
Browse files Browse the repository at this point in the history
  • Loading branch information
fay59 committed Jul 11, 2016
1 parent 252a44b commit 951f05a
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 31 deletions.
61 changes: 50 additions & 11 deletions fcd/codegen/code_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,41 @@ namespace
return x86Intrins.count(name) != 0;
}

Value* buildMemoryAddress(Value& segment, Value& pointer, Value& size, Instruction& location)
{
uint64_t loadSize = cast<ConstantInt>(size).getLimitedValue();
Type* loadType = getMemoryType(size.getContext(), loadSize)->getPointerTo();
x86_reg segmentReg = static_cast<x86_reg>(cast<ConstantInt>(segment).getLimitedValue());

switch (segmentReg)
{
case X86_REG_INVALID:
case X86_REG_CS:
case X86_REG_DS:
case X86_REG_ES:
case X86_REG_SS:
// "obvious"/invalid segment, discard
return CastInst::Create(CastInst::IntToPtr, &pointer, loadType, "", &location);

case X86_REG_FS:
case X86_REG_GS:
break;

default:
llvm_unreachable("invalid segment register!");
}

char segmentFuncName[] = "__Ss_ptr_512";
snprintf(segmentFuncName, sizeof segmentFuncName, "__%cs_ptr_i%" PRIu64,
segmentReg == X86_REG_FS ? 'f' : 'g',
loadSize * 8);

Module* module = location.getParent()->getParent()->getParent();
FunctionType* segmentFuncType = FunctionType::get(loadType, { pointer.getType() }, false);
Function* segmentFunc = cast<Function>(module->getOrInsertFunction(segmentFuncName, segmentFuncType, AttributeSet()));
return CallInst::Create(segmentFunc, { &pointer }, "", &location);
}

void replaceIntrinsic(AddressToFunction& funcMap, AddressToBlock& blockMap, StringRef name, CallInst* translated)
{
if (name == "x86_jump_intrin")
Expand Down Expand Up @@ -97,10 +132,11 @@ namespace
}
else if (name == "x86_read_mem")
{
Value* intptr = translated->getOperand(0);
ConstantInt* sizeOperand = cast<ConstantInt>(translated->getOperand(1));
Type* loadType = getMemoryType(translated->getContext(), sizeOperand->getLimitedValue());
CastInst* pointer = CastInst::Create(CastInst::IntToPtr, intptr, loadType->getPointerTo(), "", translated);
Value* segment = translated->getOperand(0);
Value* intptr = translated->getOperand(1);
Value* size = translated->getOperand(2);
Value* pointer = buildMemoryAddress(*segment, *intptr, *size, *translated);

Instruction* replacement = new LoadInst(pointer, "", translated);
md::setProgramMemory(*replacement);

Expand All @@ -114,16 +150,19 @@ namespace
}
else if (name == "x86_write_mem")
{
Value* intptr = translated->getOperand(0);
Value* value = translated->getOperand(2);
ConstantInt* sizeOperand = cast<ConstantInt>(translated->getOperand(1));
Type* storeType = getMemoryType(translated->getContext(), sizeOperand->getLimitedValue());
CastInst* pointer = CastInst::Create(CastInst::IntToPtr, intptr, storeType->getPointerTo(), "", translated);
Value* segment = translated->getOperand(0);
Value* intptr = translated->getOperand(1);
Value* size = translated->getOperand(2);
Value* value = translated->getOperand(3);
Value* pointer = buildMemoryAddress(*segment, *intptr, *size, *translated);

if (value->getType() != storeType)
// PointerType->getElementType() will eventually go away.
// However, when that happens, so can this check.
Type* elementType = cast<PointerType>(pointer->getType())->getElementType();
if (value->getType() != elementType)
{
// Assumption: storeType can only be smaller than the type of storeValue
value = CastInst::Create(Instruction::Trunc, value, storeType, "", translated);
value = CastInst::Create(Instruction::Trunc, value, elementType, "", translated);
}
StoreInst* storeInst = new StoreInst(value, pointer, translated);
md::setProgramMemory(*storeInst);
Expand Down
57 changes: 42 additions & 15 deletions fcd/cpu/x86.emulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
#include <limits.h>
#include <type_traits>

struct x86_effective_address
{
x86_reg segment;
uint64_t pointer;
};

[[gnu::always_inline]]
static bool x86_parity(uint64_t value)
{
Expand Down Expand Up @@ -153,40 +159,61 @@ static void x86_write_reg(PTR(x86_regs) regs, CPTR(cs_x86_op) reg, uint64_t valu
}

[[gnu::always_inline]]
static uint64_t x86_get_effective_address(CPTR(x86_regs) regs, CPTR(cs_x86_op) op)
static x86_effective_address x86_get_effective_address(CPTR(x86_regs) regs, CPTR(cs_x86_op) op)
{
x86_effective_address result;
const x86_op_mem* address = &op->mem;
uint64_t value = address->disp;
if (address->segment != X86_REG_INVALID)
result.pointer = address->disp;
if (address->segment == X86_REG_INVALID)
{
switch (address->base)
{
case X86_REG_BP:
case X86_REG_EBP:
case X86_REG_RBP:
result.segment = X86_REG_SS;
break;

case X86_REG_IP:
case X86_REG_EIP:
case X86_REG_RIP:
result.segment = X86_REG_CS;
break;

default:
result.segment = X86_REG_INVALID;
}
}
else
{
value += x86_read_reg(regs, static_cast<x86_reg>(address->segment));
result.segment = static_cast<x86_reg>(address->segment);
}

if (address->index != X86_REG_INVALID)
{
uint64_t index = x86_read_reg(regs, static_cast<x86_reg>(address->index));
value += index * address->scale;
result.pointer += index * address->scale;
}

if (address->base != X86_REG_INVALID)
{
value += x86_read_reg(regs, static_cast<x86_reg>(address->base));
result.pointer += x86_read_reg(regs, static_cast<x86_reg>(address->base));
}
return value;
return result;
}

[[gnu::always_inline]]
static uint64_t x86_read_mem(CPTR(x86_regs) regs, CPTR(cs_x86_op) op)
{
uint64_t address = x86_get_effective_address(regs, op);
return x86_read_mem(address, op->size);
auto address = x86_get_effective_address(regs, op);
return x86_read_mem(address.segment, address.pointer, op->size);
}

[[gnu::always_inline]]
static void x86_write_mem(CPTR(x86_regs) regs, CPTR(cs_x86_op) op, uint64_t value)
{
uint64_t address = x86_get_effective_address(regs, op);
x86_write_mem(address, op->size, value);
auto address = x86_get_effective_address(regs, op);
x86_write_mem(address.segment, address.pointer, op->size, value);
}

[[gnu::always_inline]]
Expand Down Expand Up @@ -339,15 +366,15 @@ static uint64_t x86_logical_operator(PTR(x86_regs) regs, PTR(x86_flags_reg) flag
static void x86_push_value(CPTR(x86_config) config, PTR(x86_regs) regs, size_t size, uint64_t value)
{
uint64_t push_address = x86_read_reg(regs, config->sp) - size;
x86_write_mem(push_address, size, value);
x86_write_mem(X86_REG_SS, push_address, size, value);
x86_write_reg(regs, config->sp, push_address);
}

[[gnu::always_inline]]
static uint64_t x86_pop_value(CPTR(x86_config) config, PTR(x86_regs) regs, size_t size)
{
uint64_t pop_address = x86_read_reg(regs, config->sp);
uint64_t popped = x86_read_mem(pop_address, size);
uint64_t popped = x86_read_mem(X86_REG_SS, pop_address, size);
x86_write_reg(regs, config->sp, pop_address + size);
return popped;
}
Expand Down Expand Up @@ -1073,8 +1100,8 @@ X86_INSTRUCTION_DEF(lea)
{
const cs_x86_op* destination = &inst->operands[0];
const cs_x86_op* source = &inst->operands[1];
uint64_t value = x86_get_effective_address(regs, source);
x86_write_destination_operand(destination, regs, value);
auto address = x86_get_effective_address(regs, source);
x86_write_destination_operand(destination, regs, address.pointer);
}

X86_INSTRUCTION_DEF(leave)
Expand Down
4 changes: 2 additions & 2 deletions fcd/cpu/x86.emulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ static_assert(X86_INS_ENDING == 1295, "Fcd requires Capstone 3.0.4.");
#define CPTR(t) [[gnu::nonnull]] const t* __restrict__

#pragma mark - Intrinsic functions (handled by emulator)
extern "C" void x86_write_mem(uint64_t address, size_t size, uint64_t value);
extern "C" uint64_t x86_read_mem(uint64_t address, size_t size);
extern "C" void x86_write_mem(x86_reg segment, uint64_t address, size_t size, uint64_t value);
extern "C" uint64_t x86_read_mem(x86_reg segment, uint64_t address, size_t size);
extern "C" void x86_call_intrin(CPTR(x86_config) config, PTR(x86_regs) regs, uint64_t target);
NORETURN extern "C" void x86_jump_intrin(CPTR(x86_config) config, PTR(x86_regs) regs, uint64_t target);
NORETURN extern "C" void x86_ret_intrin(CPTR(x86_config) config, PTR(x86_regs) regs);
Expand Down
7 changes: 4 additions & 3 deletions x86_emu_tests/x86_intrin_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,9 @@ namespace
}

extern const char x86_test_epilogue[];

extern "C" void x86_write_mem(uint64_t address, size_t size, uint64_t value)

// Ignore segments.
extern "C" void x86_write_mem(x86_reg, uint64_t address, size_t size, uint64_t value)
{
switch (size)
{
Expand All @@ -79,7 +80,7 @@ extern "C" void x86_write_mem(uint64_t address, size_t size, uint64_t value)
}
}

extern "C" uint64_t x86_read_mem(uint64_t address, size_t size)
extern "C" uint64_t x86_read_mem(x86_reg, uint64_t address, size_t size)
{
switch (size)
{
Expand Down

0 comments on commit 951f05a

Please sign in to comment.