Now running up to the first required kernel call.
If memory address validation is turned off it runs a lot further.
This commit is contained in:
parent
46d5a0b51d
commit
5c2060af72
5
TODO.md
5
TODO.md
|
@ -1,3 +1,7 @@
|
||||||
|
## Kernel
|
||||||
|
|
||||||
|
NtAllocateVirtualMemory
|
||||||
|
|
||||||
## Instructions
|
## Instructions
|
||||||
|
|
||||||
### XER CA bit (carry)
|
### XER CA bit (carry)
|
||||||
|
@ -6,7 +10,6 @@ Not sure the way I'm doing this is right. addic/subficx/etc set it to the value
|
||||||
of the overflow bit from the LLVM *_with_overflow intrinsic.
|
of the overflow bit from the LLVM *_with_overflow intrinsic.
|
||||||
|
|
||||||
```
|
```
|
||||||
rlwinmx
|
|
||||||
rlwimix
|
rlwimix
|
||||||
rldiclx
|
rldiclx
|
||||||
```
|
```
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
# LLVM paths.
|
# LLVM paths.
|
||||||
'llvm_path': 'build/llvm/release/',
|
# TODO(benvanik): switch based on configuration.
|
||||||
|
'llvm_path': 'build/llvm/debug/',
|
||||||
'llvm_config': '<(llvm_path)bin/llvm-config',
|
'llvm_config': '<(llvm_path)bin/llvm-config',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ public:
|
||||||
void update_gpr_value(uint32_t n, llvm::Value* value);
|
void update_gpr_value(uint32_t n, llvm::Value* value);
|
||||||
|
|
||||||
llvm::Value* GetMembase();
|
llvm::Value* GetMembase();
|
||||||
llvm::Value* memory_addr(uint32_t addr);
|
llvm::Value* GetMemoryAddress(llvm::Value* addr);
|
||||||
llvm::Value* ReadMemory(llvm::Value* addr, uint32_t size, bool extend);
|
llvm::Value* ReadMemory(llvm::Value* addr, uint32_t size, bool extend);
|
||||||
void WriteMemory(llvm::Value* addr, uint32_t size, llvm::Value* value);
|
void WriteMemory(llvm::Value* addr, uint32_t size, llvm::Value* value);
|
||||||
|
|
||||||
|
|
|
@ -128,6 +128,8 @@ public:
|
||||||
|
|
||||||
uint32_t address;
|
uint32_t address;
|
||||||
char* name;
|
char* name;
|
||||||
|
|
||||||
|
kernel::KernelExport* kernel_export;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ExceptionEntrySymbol : public Symbol {
|
class ExceptionEntrySymbol : public Symbol {
|
||||||
|
@ -154,6 +156,7 @@ public:
|
||||||
VariableSymbol* GetVariable(uint32_t address);
|
VariableSymbol* GetVariable(uint32_t address);
|
||||||
Symbol* GetSymbol(uint32_t address);
|
Symbol* GetSymbol(uint32_t address);
|
||||||
|
|
||||||
|
int GetAllVariables(std::vector<VariableSymbol*>& variables);
|
||||||
int GetAllFunctions(std::vector<FunctionSymbol*>& functions);
|
int GetAllFunctions(std::vector<FunctionSymbol*>& functions);
|
||||||
|
|
||||||
void Write(const char* file_name);
|
void Write(const char* file_name);
|
||||||
|
|
|
@ -15,11 +15,15 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct xe_ppc_state xe_ppc_state_t;
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
||||||
|
|
||||||
typedef void (*xe_kernel_export_fn)();
|
typedef void (*xe_kernel_export_impl_fn)();
|
||||||
|
typedef void (*xe_kernel_export_shim_fn)(xe_ppc_state_t* state);
|
||||||
|
|
||||||
class KernelExport {
|
class KernelExport {
|
||||||
public:
|
public:
|
||||||
|
@ -34,21 +38,24 @@ public:
|
||||||
char signature[16];
|
char signature[16];
|
||||||
char name[96];
|
char name[96];
|
||||||
|
|
||||||
|
bool is_implemented;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
// Variable data. Only valid when kXEKernelExportFlagVariable is set.
|
// Variable data. Only valid when kXEKernelExportFlagVariable is set.
|
||||||
void *variable_data;
|
// This is an address in the client memory space that the variable can
|
||||||
|
// be found at.
|
||||||
|
uint32_t variable_ptr;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
// Real function implementation (if present).
|
// Shimmed implementation.
|
||||||
xe_kernel_export_fn impl;
|
// This is called directly from generated code.
|
||||||
|
// It should parse args, do fixups, and call the impl.
|
||||||
|
xe_kernel_export_shim_fn shim;
|
||||||
|
|
||||||
// Shimmed implementation (call if param structs are big endian).
|
// Real function implementation.
|
||||||
// This may be NULL if no shim is needed or present.
|
xe_kernel_export_impl_fn impl;
|
||||||
xe_kernel_export_fn shim;
|
|
||||||
} function_data;
|
} function_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool IsImplemented();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define XE_DECLARE_EXPORT(module, ordinal, name, signature, type, flags) \
|
#define XE_DECLARE_EXPORT(module, ordinal, name, signature, type, flags) \
|
||||||
|
@ -73,6 +80,12 @@ public:
|
||||||
const uint32_t ordinal);
|
const uint32_t ordinal);
|
||||||
KernelExport* GetExportByName(const char* library_name, const char* name);
|
KernelExport* GetExportByName(const char* library_name, const char* name);
|
||||||
|
|
||||||
|
void SetVariableMapping(const char* library_name, const uint32_t ordinal,
|
||||||
|
uint32_t value);
|
||||||
|
void SetFunctionMapping(const char* library_name, const uint32_t ordinal,
|
||||||
|
xe_kernel_export_shim_fn shim,
|
||||||
|
xe_kernel_export_impl_fn impl);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class ExportTable {
|
class ExportTable {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -10,9 +10,11 @@ fi
|
||||||
private/$1 \
|
private/$1 \
|
||||||
--optimize_ir_modules=true \
|
--optimize_ir_modules=true \
|
||||||
--optimize_ir_functions=true \
|
--optimize_ir_functions=true \
|
||||||
|
--memory_address_verification=true \
|
||||||
--trace_kernel_calls=true \
|
--trace_kernel_calls=true \
|
||||||
--trace_user_calls=false \
|
--trace_user_calls=true \
|
||||||
--trace_instructions=false \
|
--trace_instructions=true \
|
||||||
|
--abort_before_entry=true \
|
||||||
1>build/run.txt
|
1>build/run.txt
|
||||||
#2>build/run.llvm.txt \
|
#2>build/run.llvm.txt \
|
||||||
|
|
||||||
|
|
|
@ -779,12 +779,13 @@ XEEMITTER(rlwinmx, 0x54000000, M )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
// m <- MASK(MB+32, ME+32)
|
// m <- MASK(MB+32, ME+32)
|
||||||
// RA <- r & m
|
// RA <- r & m
|
||||||
|
|
||||||
// The compiler will generate a bunch of these for the special case of
|
// The compiler will generate a bunch of these for the special case of SH=0.
|
||||||
// SH=0, MB=ME
|
// Which seems to just select some bits and set cr0 for use with a branch.
|
||||||
// Which seems to just select a single bit and set cr0 for use with a branch.
|
|
||||||
// We can detect this and do less work.
|
// We can detect this and do less work.
|
||||||
if (!i.M.SH && i.M.MB == i.M.ME) {
|
if (!i.M.SH) {
|
||||||
Value* v = b.CreateAnd(g.gpr_value(i.M.RS), 1 << i.M.MB);
|
Value* v = b.CreateAnd(b.CreateTrunc(g.gpr_value(i.M.RS), b.getInt32Ty()),
|
||||||
|
b.getInt32(XEMASK(i.M.MB + 32, i.M.ME + 32)));
|
||||||
|
v = b.CreateZExt(v, b.getInt64Ty());
|
||||||
g.update_gpr_value(i.M.RA, v);
|
g.update_gpr_value(i.M.RA, v);
|
||||||
if (i.M.Rc) {
|
if (i.M.Rc) {
|
||||||
// With cr0 update.
|
// With cr0 update.
|
||||||
|
@ -793,20 +794,20 @@ XEEMITTER(rlwinmx, 0x54000000, M )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// // ROTL32(x, y) = rotl(i64.(x||x), y)
|
// ROTL32(x, y) = rotl(i64.(x||x), y)
|
||||||
// Value* v = b.CreateZExt(b.CreateTrunc(g.gpr_value(i.M.RS)), b.getInt64Ty());
|
Value* v = b.CreateAnd(g.gpr_value(i.M.RS), UINT32_MAX);
|
||||||
// v = b.CreateOr(b.CreateLShr(v, 32), v);
|
v = b.CreateOr(b.CreateShl(v, 32), v);
|
||||||
// // (v << shift) | (v >> (64 - shift));
|
// (v << shift) | (v >> (32 - shift));
|
||||||
// v = b.CreateOr(b.CreateShl(v, i.M.SH), b.CreateLShr(v, 32 - i.M.SH));
|
v = b.CreateOr(b.CreateShl(v, i.M.SH), b.CreateLShr(v, 32 - i.M.SH));
|
||||||
// v = b.CreateAnd(v, XEMASK(i.M.MB + 32, i.M.ME + 32));
|
v = b.CreateAnd(v, XEMASK(i.M.MB + 32, i.M.ME + 32));
|
||||||
|
g.update_gpr_value(i.M.RA, v);
|
||||||
|
|
||||||
// if (i.M.Rc) {
|
if (i.M.Rc) {
|
||||||
// // With cr0 update.
|
// With cr0 update.
|
||||||
// g.update_cr_with_cond(0, v, b.getInt64(0), true);
|
g.update_cr_with_cond(0, v, b.getInt64(0), true);
|
||||||
// }
|
}
|
||||||
|
|
||||||
XEINSTRNOTIMPLEMENTED();
|
return 0;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(rlwnmx, 0x5C000000, M )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(rlwnmx, 0x5C000000, M )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||||
|
|
|
@ -90,13 +90,18 @@ int XeEmitBranchTo(
|
||||||
Function* target_fn = g.GetFunction(fn_block->outgoing_function);
|
Function* target_fn = g.GetFunction(fn_block->outgoing_function);
|
||||||
Function::arg_iterator args = g.gen_fn()->arg_begin();
|
Function::arg_iterator args = g.gen_fn()->arg_begin();
|
||||||
Value* state_ptr = args;
|
Value* state_ptr = args;
|
||||||
b.CreateCall2(target_fn, state_ptr, b.getInt64(cia + 4));
|
|
||||||
BasicBlock* next_bb = g.GetNextBasicBlock();
|
BasicBlock* next_bb = g.GetNextBasicBlock();
|
||||||
if (!lk || !next_bb) {
|
if (!lk || !next_bb) {
|
||||||
// Tail. No need to refill the local register values, just return.
|
// Tail. No need to refill the local register values, just return.
|
||||||
|
// We optimize this by passing in the LR from our parent instead of the
|
||||||
|
// next instruction. This allows the return from our callee to pop
|
||||||
|
// all the way up.
|
||||||
|
b.CreateCall2(target_fn, state_ptr, ++args);
|
||||||
b.CreateRetVoid();
|
b.CreateRetVoid();
|
||||||
} else {
|
} else {
|
||||||
|
// Will return here eventually.
|
||||||
// Refill registers from state.
|
// Refill registers from state.
|
||||||
|
b.CreateCall2(target_fn, state_ptr, b.getInt64(cia + 4));
|
||||||
g.FillRegisters();
|
g.FillRegisters();
|
||||||
b.CreateBr(next_bb);
|
b.CreateBr(next_bb);
|
||||||
}
|
}
|
||||||
|
@ -188,7 +193,7 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
// Ignore cond.
|
// Ignore cond.
|
||||||
} else {
|
} else {
|
||||||
Value* cr = g.cr_value(i.B.BI >> 2);
|
Value* cr = g.cr_value(i.B.BI >> 2);
|
||||||
cr = b.CreateAnd(cr, 1 << (i.B.BI >> 2));
|
cr = b.CreateAnd(cr, 1 << (i.B.BI & 3));
|
||||||
if (XESELECTBITS(i.B.BO, 3, 3)) {
|
if (XESELECTBITS(i.B.BO, 3, 3)) {
|
||||||
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
|
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
|
||||||
} else {
|
} else {
|
||||||
|
@ -257,7 +262,7 @@ XEEMITTER(bcctrx, 0x4C000420, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
// Ignore cond.
|
// Ignore cond.
|
||||||
} else {
|
} else {
|
||||||
Value* cr = g.cr_value(i.XL.BI >> 2);
|
Value* cr = g.cr_value(i.XL.BI >> 2);
|
||||||
cr = b.CreateAnd(cr, 1 << (i.XL.BI >> 2));
|
cr = b.CreateAnd(cr, 1 << (i.XL.BI & 3));
|
||||||
if (XESELECTBITS(i.XL.BO, 3, 3)) {
|
if (XESELECTBITS(i.XL.BO, 3, 3)) {
|
||||||
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
|
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
|
||||||
} else {
|
} else {
|
||||||
|
@ -336,7 +341,7 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
// Ignore cond.
|
// Ignore cond.
|
||||||
} else {
|
} else {
|
||||||
Value* cr = g.cr_value(i.XL.BI >> 2);
|
Value* cr = g.cr_value(i.XL.BI >> 2);
|
||||||
cr = b.CreateAnd(cr, 1 << (i.XL.BI >> 2));
|
cr = b.CreateAnd(cr, 1 << (i.XL.BI & 3));
|
||||||
if (XESELECTBITS(i.XL.BO, 3, 3)) {
|
if (XESELECTBITS(i.XL.BO, 3, 3)) {
|
||||||
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
|
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
#include <xenia/cpu/codegen/function_generator.h>
|
#include <xenia/cpu/codegen/function_generator.h>
|
||||||
|
|
||||||
|
#include <llvm/IR/Intrinsics.h>
|
||||||
|
|
||||||
#include <xenia/cpu/ppc/state.h>
|
#include <xenia/cpu/ppc/state.h>
|
||||||
|
|
||||||
#include "cpu/cpu-private.h"
|
#include "cpu/cpu-private.h"
|
||||||
|
@ -20,6 +22,10 @@ using namespace xe::cpu::ppc;
|
||||||
using namespace xe::cpu::sdb;
|
using namespace xe::cpu::sdb;
|
||||||
|
|
||||||
|
|
||||||
|
DEFINE_bool(memory_address_verification, false,
|
||||||
|
"Whether to add additional checks to generated memory load/stores.");
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This generates function code.
|
* This generates function code.
|
||||||
* One context is created for each function to generate. Each basic block in
|
* One context is created for each function to generate. Each basic block in
|
||||||
|
@ -752,9 +758,6 @@ void FunctionGenerator::update_cr_with_cond(
|
||||||
// bit1 = RA > RB
|
// bit1 = RA > RB
|
||||||
// bit2 = RA = RB
|
// bit2 = RA = RB
|
||||||
// bit3 = XER[SO]
|
// bit3 = XER[SO]
|
||||||
// Bits are reversed:
|
|
||||||
// 0123
|
|
||||||
// 3210
|
|
||||||
|
|
||||||
// TODO(benvanik): inline this using the x86 cmp instruction - this prevents
|
// TODO(benvanik): inline this using the x86 cmp instruction - this prevents
|
||||||
// the need for a lot of the compares and ensures we lower to the best
|
// the need for a lot of the compares and ensures we lower to the best
|
||||||
|
@ -770,8 +773,8 @@ void FunctionGenerator::update_cr_with_cond(
|
||||||
b.CreateICmpSLT(lhs, rhs) : b.CreateICmpULT(lhs, rhs);
|
b.CreateICmpSLT(lhs, rhs) : b.CreateICmpULT(lhs, rhs);
|
||||||
Value* is_gt = is_signed ?
|
Value* is_gt = is_signed ?
|
||||||
b.CreateICmpSGT(lhs, rhs) : b.CreateICmpUGT(lhs, rhs);
|
b.CreateICmpSGT(lhs, rhs) : b.CreateICmpUGT(lhs, rhs);
|
||||||
Value* cp = b.CreateSelect(is_gt, b.getInt8(1 << 2), b.getInt8(1 << 1));
|
Value* cp = b.CreateSelect(is_gt, b.getInt8(1 << 1), b.getInt8(1 << 2));
|
||||||
Value* c = b.CreateSelect(is_lt, b.getInt8(1 << 3), cp);
|
Value* c = b.CreateSelect(is_lt, b.getInt8(1 << 0), cp);
|
||||||
|
|
||||||
// TODO(benvanik): set bit 4 to XER[SO]
|
// TODO(benvanik): set bit 4 to XER[SO]
|
||||||
|
|
||||||
|
@ -834,26 +837,53 @@ Value* FunctionGenerator::GetMembase() {
|
||||||
return builder_->CreateLoad(v);
|
return builder_->CreateLoad(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* FunctionGenerator::memory_addr(uint32_t addr) {
|
Value* FunctionGenerator::GetMemoryAddress(Value* addr) {
|
||||||
return NULL;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
||||||
|
// Input address is always in 32-bit space.
|
||||||
|
addr = b.CreateAnd(addr, UINT_MAX);
|
||||||
|
|
||||||
|
// Add runtime memory address checks, if needed.
|
||||||
|
if (FLAGS_memory_address_verification) {
|
||||||
|
BasicBlock* invalid_bb = BasicBlock::Create(*context_, "", gen_fn_);
|
||||||
|
BasicBlock* valid_bb = BasicBlock::Create(*context_, "", gen_fn_);
|
||||||
|
|
||||||
|
Value* gt = b.CreateICmpUGE(addr, b.getInt64(0x10000000));
|
||||||
|
b.CreateCondBr(gt, valid_bb, invalid_bb);
|
||||||
|
|
||||||
|
b.SetInsertPoint(invalid_bb);
|
||||||
|
Function* debugtrap = Intrinsic::getDeclaration(
|
||||||
|
gen_module_, Intrinsic::debugtrap);
|
||||||
|
b.CreateCall(debugtrap);
|
||||||
|
b.CreateBr(valid_bb);
|
||||||
|
|
||||||
|
b.SetInsertPoint(valid_bb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rebase off of memory base pointer.
|
||||||
|
return b.CreateInBoundsGEP(GetMembase(), addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* FunctionGenerator::ReadMemory(Value* addr, uint32_t size, bool extend) {
|
Value* FunctionGenerator::ReadMemory(Value* addr, uint32_t size, bool extend) {
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
||||||
Type* dataTy = NULL;
|
Type* dataTy = NULL;
|
||||||
|
bool needs_swap = false;
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 1:
|
case 1:
|
||||||
dataTy = b.getInt8Ty();
|
dataTy = b.getInt8Ty();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
dataTy = b.getInt16Ty();
|
dataTy = b.getInt16Ty();
|
||||||
|
needs_swap = true;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
dataTy = b.getInt32Ty();
|
dataTy = b.getInt32Ty();
|
||||||
|
needs_swap = true;
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
dataTy = b.getInt64Ty();
|
dataTy = b.getInt64Ty();
|
||||||
|
needs_swap = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
XEASSERTALWAYS();
|
XEASSERTALWAYS();
|
||||||
|
@ -861,31 +891,41 @@ Value* FunctionGenerator::ReadMemory(Value* addr, uint32_t size, bool extend) {
|
||||||
}
|
}
|
||||||
PointerType* pointerTy = PointerType::getUnqual(dataTy);
|
PointerType* pointerTy = PointerType::getUnqual(dataTy);
|
||||||
|
|
||||||
// Input address is always in 32-bit space.
|
Value* address = GetMemoryAddress(addr);
|
||||||
addr = b.CreateAnd(addr, UINT_MAX);
|
|
||||||
|
|
||||||
Value* offset_addr = b.CreateAdd(addr, b.getInt64(size));
|
|
||||||
Value* address = b.CreateInBoundsGEP(GetMembase(), offset_addr);
|
|
||||||
Value* ptr = b.CreatePointerCast(address, pointerTy);
|
Value* ptr = b.CreatePointerCast(address, pointerTy);
|
||||||
return b.CreateLoad(ptr);
|
Value* value = b.CreateLoad(ptr);
|
||||||
|
|
||||||
|
// Swap after loading.
|
||||||
|
// TODO(benvanik): find a way to avoid this!
|
||||||
|
if (needs_swap) {
|
||||||
|
Function* bswap = Intrinsic::getDeclaration(
|
||||||
|
gen_module_, Intrinsic::bswap, dataTy);
|
||||||
|
value = b.CreateCall(bswap, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionGenerator::WriteMemory(Value* addr, uint32_t size, Value* value) {
|
void FunctionGenerator::WriteMemory(Value* addr, uint32_t size, Value* value) {
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
||||||
Type* dataTy = NULL;
|
Type* dataTy = NULL;
|
||||||
|
bool needs_swap = false;
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 1:
|
case 1:
|
||||||
dataTy = b.getInt8Ty();
|
dataTy = b.getInt8Ty();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
dataTy = b.getInt16Ty();
|
dataTy = b.getInt16Ty();
|
||||||
|
needs_swap = true;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
dataTy = b.getInt32Ty();
|
dataTy = b.getInt32Ty();
|
||||||
|
needs_swap = true;
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
dataTy = b.getInt64Ty();
|
dataTy = b.getInt64Ty();
|
||||||
|
needs_swap = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
XEASSERTALWAYS();
|
XEASSERTALWAYS();
|
||||||
|
@ -893,16 +933,21 @@ void FunctionGenerator::WriteMemory(Value* addr, uint32_t size, Value* value) {
|
||||||
}
|
}
|
||||||
PointerType* pointerTy = PointerType::getUnqual(dataTy);
|
PointerType* pointerTy = PointerType::getUnqual(dataTy);
|
||||||
|
|
||||||
// Input address is always in 32-bit space.
|
Value* address = GetMemoryAddress(addr);
|
||||||
addr = b.CreateAnd(addr, UINT_MAX);
|
|
||||||
|
|
||||||
Value* offset_addr = b.CreateAdd(addr, b.getInt64(size));
|
|
||||||
Value* address = b.CreateInBoundsGEP(GetMembase(), offset_addr);
|
|
||||||
Value* ptr = b.CreatePointerCast(address, pointerTy);
|
Value* ptr = b.CreatePointerCast(address, pointerTy);
|
||||||
|
|
||||||
// Truncate, if required.
|
// Truncate, if required.
|
||||||
if (value->getType() != dataTy) {
|
if (value->getType() != dataTy) {
|
||||||
value = b.CreateTrunc(value, dataTy);
|
value = b.CreateTrunc(value, dataTy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Swap before storing.
|
||||||
|
// TODO(benvanik): find a way to avoid this!
|
||||||
|
if (needs_swap) {
|
||||||
|
Function* bswap = Intrinsic::getDeclaration(
|
||||||
|
gen_module_, Intrinsic::bswap, dataTy);
|
||||||
|
value = b.CreateCall(bswap, value);
|
||||||
|
}
|
||||||
|
|
||||||
b.CreateStore(value, ptr);
|
b.CreateStore(value, ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,7 +102,7 @@ int ModuleGenerator::Generate() {
|
||||||
PrepareFunction(fn);
|
PrepareFunction(fn);
|
||||||
break;
|
break;
|
||||||
case FunctionSymbol::Kernel:
|
case FunctionSymbol::Kernel:
|
||||||
if (fn->kernel_export && fn->kernel_export->IsImplemented()) {
|
if (fn->kernel_export && fn->kernel_export->is_implemented) {
|
||||||
AddPresentImport(fn);
|
AddPresentImport(fn);
|
||||||
} else {
|
} else {
|
||||||
AddMissingImport(fn);
|
AddMissingImport(fn);
|
||||||
|
@ -191,20 +191,19 @@ void ModuleGenerator::AddMissingImport(FunctionSymbol* fn) {
|
||||||
// Create the function (and setup args/attributes/etc).
|
// Create the function (and setup args/attributes/etc).
|
||||||
Function* f = CreateFunctionDefinition(fn->name);
|
Function* f = CreateFunctionDefinition(fn->name);
|
||||||
|
|
||||||
// TODO(benvanik): log errors.
|
|
||||||
BasicBlock* block = BasicBlock::Create(context, "entry", f);
|
BasicBlock* block = BasicBlock::Create(context, "entry", f);
|
||||||
IRBuilder<> builder(block);
|
IRBuilder<> b(block);
|
||||||
|
|
||||||
if (FLAGS_trace_kernel_calls) {
|
if (FLAGS_trace_kernel_calls) {
|
||||||
Value* traceKernelCall = m->getFunction("XeTraceKernelCall");
|
Value* traceKernelCall = m->getFunction("XeTraceKernelCall");
|
||||||
builder.CreateCall3(
|
b.CreateCall3(
|
||||||
traceKernelCall,
|
traceKernelCall,
|
||||||
f->arg_begin(),
|
f->arg_begin(),
|
||||||
builder.getInt64(fn->start_address),
|
b.getInt64(fn->start_address),
|
||||||
++f->arg_begin());
|
++f->arg_begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.CreateRetVoid();
|
b.CreateRetVoid();
|
||||||
|
|
||||||
OptimizeFunction(m, f);
|
OptimizeFunction(m, f);
|
||||||
|
|
||||||
|
@ -219,10 +218,44 @@ void ModuleGenerator::AddMissingImport(FunctionSymbol* fn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModuleGenerator::AddPresentImport(FunctionSymbol* fn) {
|
void ModuleGenerator::AddPresentImport(FunctionSymbol* fn) {
|
||||||
// Module *m = gen_module_;
|
Module *m = gen_module_;
|
||||||
// LLVMContext& context = m->getContext();
|
LLVMContext& context = m->getContext();
|
||||||
|
|
||||||
// TODO(benvanik): add import thunk code.
|
// Add the extern.
|
||||||
|
char shim_name[256];
|
||||||
|
xesnprintfa(shim_name, XECOUNT(shim_name),
|
||||||
|
"__shim__%s", fn->kernel_export->name);
|
||||||
|
std::vector<Type*> shimArgs;
|
||||||
|
shimArgs.push_back(PointerType::getUnqual(Type::getInt8Ty(context)));
|
||||||
|
FunctionType* shimTy = FunctionType::get(
|
||||||
|
Type::getVoidTy(context), shimArgs, false);
|
||||||
|
Function* shim = Function::Create(
|
||||||
|
shimTy, Function::ExternalLinkage, shim_name, m);
|
||||||
|
// engine_->addGlobalMapping(shim,
|
||||||
|
// (void*)fn->kernel_export->function_data.shim);
|
||||||
|
|
||||||
|
// Create the function (and setup args/attributes/etc).
|
||||||
|
Function* f = CreateFunctionDefinition(fn->name);
|
||||||
|
|
||||||
|
BasicBlock* block = BasicBlock::Create(context, "entry", f);
|
||||||
|
IRBuilder<> b(block);
|
||||||
|
|
||||||
|
if (FLAGS_trace_kernel_calls) {
|
||||||
|
Value* traceKernelCall = m->getFunction("XeTraceKernelCall");
|
||||||
|
b.CreateCall3(
|
||||||
|
traceKernelCall,
|
||||||
|
f->arg_begin(),
|
||||||
|
b.getInt64(fn->start_address),
|
||||||
|
++f->arg_begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
b.CreateCall(
|
||||||
|
shim,
|
||||||
|
f->arg_begin());
|
||||||
|
|
||||||
|
b.CreateRetVoid();
|
||||||
|
|
||||||
|
OptimizeFunction(m, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModuleGenerator::PrepareFunction(FunctionSymbol* fn) {
|
void ModuleGenerator::PrepareFunction(FunctionSymbol* fn) {
|
||||||
|
|
|
@ -41,6 +41,7 @@ using namespace llvm;
|
||||||
using namespace xe;
|
using namespace xe;
|
||||||
using namespace xe::cpu;
|
using namespace xe::cpu;
|
||||||
using namespace xe::cpu::codegen;
|
using namespace xe::cpu::codegen;
|
||||||
|
using namespace xe::cpu::sdb;
|
||||||
using namespace xe::kernel;
|
using namespace xe::kernel;
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,7 +73,16 @@ int ExecModule::PrepareUserModule(kernel::UserModule* user_module) {
|
||||||
sdb_ = shared_ptr<sdb::SymbolDatabase>(
|
sdb_ = shared_ptr<sdb::SymbolDatabase>(
|
||||||
new sdb::XexSymbolDatabase(memory_, export_resolver_.get(), user_module));
|
new sdb::XexSymbolDatabase(memory_, export_resolver_.get(), user_module));
|
||||||
|
|
||||||
return Prepare();
|
int result_code = Prepare();
|
||||||
|
if (result_code) {
|
||||||
|
return result_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import variables.
|
||||||
|
xe_xex2_ref xex = user_module->xex();
|
||||||
|
xe_xex2_release(xex);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ExecModule::PrepareRawBinary(uint32_t start_address, uint32_t end_address) {
|
int ExecModule::PrepareRawBinary(uint32_t start_address, uint32_t end_address) {
|
||||||
|
@ -339,6 +349,38 @@ int ExecModule::InjectGlobals() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int ExecModule::Init() {
|
int ExecModule::Init() {
|
||||||
|
// Setup all kernel variables.
|
||||||
|
std::vector<VariableSymbol*> variables;
|
||||||
|
if (sdb_->GetAllVariables(variables)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
uint8_t* mem = xe_memory_addr(memory_, 0);
|
||||||
|
for (std::vector<VariableSymbol*>::iterator it = variables.begin();
|
||||||
|
it != variables.end(); ++it) {
|
||||||
|
VariableSymbol* var = *it;
|
||||||
|
if (!var->kernel_export) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
KernelExport* kernel_export = var->kernel_export;
|
||||||
|
|
||||||
|
// Grab, if available.
|
||||||
|
uint32_t* slot = (uint32_t*)(mem + var->address);
|
||||||
|
if (kernel_export->type == KernelExport::Function) {
|
||||||
|
// Not exactly sure what this should be...
|
||||||
|
// TODO(benvanik): find out what import variables are.
|
||||||
|
} else {
|
||||||
|
if (kernel_export->is_implemented) {
|
||||||
|
// Implemented - replace with pointer.
|
||||||
|
*slot = kernel_export->variable_ptr;
|
||||||
|
} else {
|
||||||
|
// Not implemented - write with a dummy value.
|
||||||
|
*slot = 0xDEADBEEF;
|
||||||
|
XELOGCPU("WARNING: imported a variable with no value: %s",
|
||||||
|
kernel_export->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Run static initializers. I'm not sure we'll have any, but who knows.
|
// Run static initializers. I'm not sure we'll have any, but who knows.
|
||||||
engine_->runStaticConstructorsDestructors(gen_module_.get(), false);
|
engine_->runStaticConstructorsDestructors(gen_module_.get(), false);
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,8 @@ namespace {
|
||||||
LLVMLinkInJIT();
|
LLVMLinkInJIT();
|
||||||
InitializeNativeTarget();
|
InitializeNativeTarget();
|
||||||
|
|
||||||
|
llvm_start_multithreaded();
|
||||||
|
|
||||||
// TODO(benvanik): only do this once
|
// TODO(benvanik): only do this once
|
||||||
codegen::RegisterEmitCategoryALU();
|
codegen::RegisterEmitCategoryALU();
|
||||||
codegen::RegisterEmitCategoryControl();
|
codegen::RegisterEmitCategoryControl();
|
||||||
|
@ -89,17 +91,21 @@ xe_memory_ref Processor::memory() {
|
||||||
int Processor::Setup() {
|
int Processor::Setup() {
|
||||||
XEASSERTNULL(engine_);
|
XEASSERTNULL(engine_);
|
||||||
|
|
||||||
if (!llvm_start_multithreaded()) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
dummy_context_ = auto_ptr<LLVMContext>(new LLVMContext());
|
dummy_context_ = auto_ptr<LLVMContext>(new LLVMContext());
|
||||||
Module* dummy_module = new Module("dummy", *dummy_context_.get());
|
Module* dummy_module = new Module("dummy", *dummy_context_.get());
|
||||||
|
|
||||||
std::string error_message;
|
std::string error_message;
|
||||||
engine_ = shared_ptr<ExecutionEngine>(
|
|
||||||
ExecutionEngine::create(dummy_module, false, &error_message,
|
EngineBuilder builder(dummy_module);
|
||||||
CodeGenOpt::Aggressive, false));
|
builder.setEngineKind(EngineKind::JIT);
|
||||||
|
builder.setErrorStr(&error_message);
|
||||||
|
builder.setOptLevel(CodeGenOpt::None);
|
||||||
|
//builder.setOptLevel(CodeGenOpt::Aggressive);
|
||||||
|
//builder.setTargetOptions();
|
||||||
|
builder.setAllocateGVsWithCode(false);
|
||||||
|
//builder.setUseMCJIT(true);
|
||||||
|
|
||||||
|
engine_ = shared_ptr<ExecutionEngine>(builder.create());
|
||||||
if (!engine_) {
|
if (!engine_) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -178,7 +184,7 @@ int Processor::Execute(ThreadState* thread_state, uint32_t address) {
|
||||||
uint32_t lr = 0xBEBEBEBE;
|
uint32_t lr = 0xBEBEBEBE;
|
||||||
|
|
||||||
// Setup registers.
|
// Setup registers.
|
||||||
ppc_state->lr = 0xBEBEBEBE;
|
ppc_state->lr = lr;
|
||||||
|
|
||||||
// Args:
|
// Args:
|
||||||
// - i8* state
|
// - i8* state
|
||||||
|
@ -191,6 +197,10 @@ int Processor::Execute(ThreadState* thread_state, uint32_t address) {
|
||||||
|
|
||||||
GenericValue ret = engine_->runFunction(f, args);
|
GenericValue ret = engine_->runFunction(f, args);
|
||||||
|
|
||||||
|
// typedef void (*fnptr)(xe_ppc_state_t*, uint64_t);
|
||||||
|
// fnptr ptr = (fnptr)engine_->getPointerToFunction(f);
|
||||||
|
// ptr(ppc_state, lr);
|
||||||
|
|
||||||
//return (uint32_t)ret.IntVal.getSExtValue();
|
//return (uint32_t)ret.IntVal.getSExtValue();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,8 @@ FunctionBlock* FunctionSymbol::SplitBlock(uint32_t address) {
|
||||||
|
|
||||||
VariableSymbol::VariableSymbol() :
|
VariableSymbol::VariableSymbol() :
|
||||||
Symbol(Variable),
|
Symbol(Variable),
|
||||||
address(0), name(0) {
|
address(0), name(0),
|
||||||
|
kernel_export(0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
VariableSymbol::~VariableSymbol() {
|
VariableSymbol::~VariableSymbol() {
|
||||||
|
@ -213,6 +214,15 @@ VariableSymbol* SymbolDatabase::GetVariable(uint32_t address) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SymbolDatabase::GetAllVariables(std::vector<VariableSymbol*>& variables) {
|
||||||
|
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
|
||||||
|
if (it->second->symbol_type == Symbol::Variable) {
|
||||||
|
variables.push_back(static_cast<VariableSymbol*>(it->second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int SymbolDatabase::GetAllFunctions(vector<FunctionSymbol*>& functions) {
|
int SymbolDatabase::GetAllFunctions(vector<FunctionSymbol*>& functions) {
|
||||||
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
|
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
|
||||||
if (it->second->symbol_type == Symbol::Function) {
|
if (it->second->symbol_type == Symbol::Function) {
|
||||||
|
@ -847,6 +857,7 @@ int XexSymbolDatabase::AddImports(const xe_xex2_import_library_t* library) {
|
||||||
info->ordinal);
|
info->ordinal);
|
||||||
}
|
}
|
||||||
var->name = xestrdupa(name);
|
var->name = xestrdupa(name);
|
||||||
|
var->kernel_export = kernel_export;
|
||||||
if (info->thunk_address) {
|
if (info->thunk_address) {
|
||||||
FunctionSymbol* fn = GetOrInsertFunction(info->thunk_address);
|
FunctionSymbol* fn = GetOrInsertFunction(info->thunk_address);
|
||||||
fn->end_address = fn->start_address + 16 - 4;
|
fn->end_address = fn->start_address + 16 - 4;
|
||||||
|
|
|
@ -14,23 +14,6 @@ using namespace xe;
|
||||||
using namespace xe::kernel;
|
using namespace xe::kernel;
|
||||||
|
|
||||||
|
|
||||||
bool KernelExport::IsImplemented() {
|
|
||||||
switch (type) {
|
|
||||||
case Function:
|
|
||||||
if (function_data.impl) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Variable:
|
|
||||||
if (variable_data) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ExportResolver::ExportResolver() {
|
ExportResolver::ExportResolver() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +27,13 @@ void ExportResolver::RegisterTable(
|
||||||
table.exports = exports;
|
table.exports = exports;
|
||||||
table.count = count;
|
table.count = count;
|
||||||
tables_.push_back(table);
|
tables_.push_back(table);
|
||||||
|
|
||||||
|
for (size_t n = 0; n < count; n++) {
|
||||||
|
exports[n].is_implemented = false;
|
||||||
|
exports[n].variable_ptr = 0;
|
||||||
|
exports[n].function_data.shim = NULL;
|
||||||
|
exports[n].function_data.impl = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KernelExport* ExportResolver::GetExportByOrdinal(const char* library_name,
|
KernelExport* ExportResolver::GetExportByOrdinal(const char* library_name,
|
||||||
|
@ -69,3 +59,23 @@ KernelExport* ExportResolver::GetExportByName(const char* library_name,
|
||||||
XEASSERTALWAYS();
|
XEASSERTALWAYS();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExportResolver::SetVariableMapping(const char* library_name,
|
||||||
|
const uint32_t ordinal,
|
||||||
|
uint32_t value) {
|
||||||
|
KernelExport* kernel_export = GetExportByOrdinal(library_name, ordinal);
|
||||||
|
XEASSERTNOTNULL(kernel_export);
|
||||||
|
kernel_export->is_implemented = true;
|
||||||
|
kernel_export->variable_ptr = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExportResolver::SetFunctionMapping(const char* library_name,
|
||||||
|
const uint32_t ordinal,
|
||||||
|
xe_kernel_export_shim_fn shim,
|
||||||
|
xe_kernel_export_impl_fn impl) {
|
||||||
|
KernelExport* kernel_export = GetExportByOrdinal(library_name, ordinal);
|
||||||
|
XEASSERTNOTNULL(kernel_export);
|
||||||
|
kernel_export->is_implemented = true;
|
||||||
|
kernel_export->function_data.shim = shim;
|
||||||
|
kernel_export->function_data.impl = impl;
|
||||||
|
}
|
||||||
|
|
|
@ -17,11 +17,55 @@ using namespace xe::kernel;
|
||||||
using namespace xe::kernel::xboxkrnl;
|
using namespace xe::kernel::xboxkrnl;
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// bit 27 = something?
|
||||||
|
typedef struct {
|
||||||
|
uint32_t flags;
|
||||||
|
uint8_t processor_count;
|
||||||
|
uint8_t unknown0;
|
||||||
|
uint8_t unknown1;
|
||||||
|
uint8_t unknown2;
|
||||||
|
uint32_t unknown4;
|
||||||
|
uint16_t unknown5;
|
||||||
|
uint16_t unknown6;
|
||||||
|
} XboxHardwareInfo_t;
|
||||||
|
// XboxHardwareInfo_t XboxHardwareInfo = {
|
||||||
|
// 0x00000000, 3, 0, 0, 0, 0, 0, 0,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// This should remain zero for now.
|
||||||
|
// The pointer returned should have a 4b value at 0x58 that is the first
|
||||||
|
// argument to RtlImageXexHeaderField (header base?)
|
||||||
|
//uint32_t XexExecutableModuleHandle = 0x00000000;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
XboxkrnlModule::XboxkrnlModule(xe_pal_ref pal, xe_memory_ref memory,
|
XboxkrnlModule::XboxkrnlModule(xe_pal_ref pal, xe_memory_ref memory,
|
||||||
shared_ptr<ExportResolver> resolver) :
|
shared_ptr<ExportResolver> resolver) :
|
||||||
KernelModule(pal, memory, resolver) {
|
KernelModule(pal, memory, resolver) {
|
||||||
resolver->RegisterTable(
|
resolver->RegisterTable(
|
||||||
"xboxkrnl.exe", xboxkrnl_export_table, XECOUNT(xboxkrnl_export_table));
|
"xboxkrnl.exe", xboxkrnl_export_table, XECOUNT(xboxkrnl_export_table));
|
||||||
|
|
||||||
|
// TODO(benvanik): alloc heap memory somewhere in user space
|
||||||
|
// TODO(benvanik): tools for reading/writing to heap memory
|
||||||
|
|
||||||
|
// HACK: register some dummy globals for now.
|
||||||
|
// KeDebugMonitorData
|
||||||
|
resolver->SetVariableMapping(
|
||||||
|
"xboxkrnl.exe", 0x00000059,
|
||||||
|
0);
|
||||||
|
// XboxHardwareInfo
|
||||||
|
resolver->SetVariableMapping(
|
||||||
|
"xboxkrnl.exe", 0x00000156,
|
||||||
|
0);
|
||||||
|
// XexExecutableModuleHandle
|
||||||
|
resolver->SetVariableMapping(
|
||||||
|
"xboxkrnl.exe", 0x00000193,
|
||||||
|
0);
|
||||||
|
|
||||||
|
// 0x0000012B, RtlImageXexHeaderField
|
||||||
}
|
}
|
||||||
|
|
||||||
XboxkrnlModule::~XboxkrnlModule() {
|
XboxkrnlModule::~XboxkrnlModule() {
|
||||||
|
|
|
@ -369,7 +369,7 @@ void UserModule::Dump(ExportResolver* export_resolver) {
|
||||||
export_resolver->GetExportByOrdinal(library->name, info->ordinal);
|
export_resolver->GetExportByOrdinal(library->name, info->ordinal);
|
||||||
if (kernel_export) {
|
if (kernel_export) {
|
||||||
known_count++;
|
known_count++;
|
||||||
if (kernel_export->IsImplemented()) {
|
if (kernel_export->is_implemented) {
|
||||||
impl_count++;
|
impl_count++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -395,7 +395,7 @@ void UserModule::Dump(ExportResolver* export_resolver) {
|
||||||
bool implemented = false;
|
bool implemented = false;
|
||||||
if (kernel_export) {
|
if (kernel_export) {
|
||||||
name = kernel_export->name;
|
name = kernel_export->name;
|
||||||
implemented = kernel_export->IsImplemented();
|
implemented = kernel_export->is_implemented;
|
||||||
}
|
}
|
||||||
if (info->thunk_address) {
|
if (info->thunk_address) {
|
||||||
printf(" F %.8X %.8X %.3X (%3d) %s %s\n",
|
printf(" F %.8X %.8X %.3X (%3d) %s %s\n",
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
extrwi.o: file format elf64-powerpc
|
||||||
|
|
||||||
|
|
||||||
|
Disassembly of section .text:
|
||||||
|
|
||||||
|
0000000082010000 <.text>:
|
||||||
|
82010000: 54 a7 ef 3e rlwinm r7,r5,29,28,31
|
||||||
|
82010004: 4e 80 00 20 blr
|
|
@ -0,0 +1,11 @@
|
||||||
|
# This is a variant of rlwinmx:
|
||||||
|
# extrwi ra,rs,n,b (n > 0) == rlwinm ra,rs,b+n,32-n,31
|
||||||
|
|
||||||
|
# REGISTER_IN r5 0x30
|
||||||
|
|
||||||
|
# rlwinm r7, r5, 29, 28, 31
|
||||||
|
extrwi r7, r5, 4, 25
|
||||||
|
|
||||||
|
blr
|
||||||
|
# REGISTER_OUT r5 0x30
|
||||||
|
# REGISTER_OUT r7 0x06
|
|
@ -17,6 +17,10 @@ using namespace xe::cpu;
|
||||||
using namespace xe::kernel;
|
using namespace xe::kernel;
|
||||||
|
|
||||||
|
|
||||||
|
DEFINE_bool(abort_before_entry, false,
|
||||||
|
"Abort execution right before launching the module.");
|
||||||
|
|
||||||
|
|
||||||
class Run {
|
class Run {
|
||||||
public:
|
public:
|
||||||
Run();
|
Run();
|
||||||
|
@ -66,6 +70,10 @@ XECLEANUP:
|
||||||
}
|
}
|
||||||
|
|
||||||
int Run::Launch() {
|
int Run::Launch() {
|
||||||
|
if (FLAGS_abort_before_entry) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(benvanik): wait until the module thread exits
|
// TODO(benvanik): wait until the module thread exits
|
||||||
runtime_->LaunchModule(module_);
|
runtime_->LaunchModule(module_);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue