/*******************************************************************************
-* Copyright 2016-2018 Intel Corporation
+* Copyright 2016-2019 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// This covers -std=(gnu|c)++(0x|11|1y), -stdlib=libc++, and modern Microsoft.
#if ((defined(_MSC_VER) && (_MSC_VER >= 1600)) || defined(_LIBCPP_VERSION) ||\
((__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__)))
+ #include <unordered_set>
+ #define XBYAK_STD_UNORDERED_SET std::unordered_set
#include <unordered_map>
#define XBYAK_STD_UNORDERED_MAP std::unordered_map
#define XBYAK_STD_UNORDERED_MULTIMAP std::unordered_multimap
libstdcxx 20070719 (from GCC 4.2.1, the last GPL 2 version).
*/
#elif XBYAK_GNUC_PREREQ(4, 5) || (XBYAK_GNUC_PREREQ(4, 2) && __GLIBCXX__ >= 20070719) || defined(__INTEL_COMPILER) || defined(__llvm__)
+ #include <tr1/unordered_set>
+ #define XBYAK_STD_UNORDERED_SET std::tr1::unordered_set
#include <tr1/unordered_map>
#define XBYAK_STD_UNORDERED_MAP std::tr1::unordered_map
#define XBYAK_STD_UNORDERED_MULTIMAP std::tr1::unordered_multimap
#elif defined(_MSC_VER) && (_MSC_VER >= 1500) && (_MSC_VER < 1600)
+ #include <unordered_set>
+ #define XBYAK_STD_UNORDERED_SET std::tr1::unordered_set
#include <unordered_map>
#define XBYAK_STD_UNORDERED_MAP std::tr1::unordered_map
#define XBYAK_STD_UNORDERED_MULTIMAP std::tr1::unordered_multimap
#else
+ #include <set>
+ #define XBYAK_STD_UNORDERED_SET std::set
#include <map>
#define XBYAK_STD_UNORDERED_MAP std::map
#define XBYAK_STD_UNORDERED_MULTIMAP std::multimap
enum {
DEFAULT_MAX_CODE_SIZE = 4096,
- VERSION = 0x5631 /* 0xABCD = A.BC(D) */
+ VERSION = 0x5760 /* 0xABCD = A.BC(D) */
};
#ifndef MIE_INTEGER_TYPE_DEFINED
ERR_INVALID_ZERO,
ERR_INVALID_RIP_IN_AUTO_GROW,
ERR_INVALID_MIB_ADDRESS,
- ERR_INTERNAL
+ ERR_INTERNAL,
+ ERR_X2APIC_IS_NOT_SUPPORTED
};
class Error : public std::exception {
"invalid rip in AutoGrow",
"invalid mib address",
"internal error",
+ "x2APIC is not supported"
};
assert((size_t)err_ < sizeof(errTbl) / sizeof(*errTbl));
return errTbl[err_];
const Label* label_;
bool isAddr_;
explicit RegRip(sint64 disp = 0, const Label* label = 0, bool isAddr = false) : disp_(disp), label_(label), isAddr_(isAddr) {}
+ friend const RegRip operator+(const RegRip& r, int disp) {
+ return RegRip(r.disp_ + disp, r.label_, r.isAddr_);
+ }
+ friend const RegRip operator-(const RegRip& r, int disp) {
+ return RegRip(r.disp_ - disp, r.label_, r.isAddr_);
+ }
friend const RegRip operator+(const RegRip& r, sint64 disp) {
return RegRip(r.disp_ + disp, r.label_, r.isAddr_);
}
// 2nd parameter for constructor of CodeArray(maxSize, userPtr, alloc)
void *const AutoGrow = (void*)1; //-V566
+void *const DontSetProtectRWE = (void*)2; //-V566
class CodeArray {
enum Type {
size_t size_;
bool isCalledCalcJmpAddress_;
+ bool useProtect() const { return alloc_->useProtect(); }
/*
allocate new memory and copy old data to the new area
*/
uint64 disp = i->getVal(top_);
rewrite(i->codeOffset, disp, i->jmpSize);
}
- if (alloc_->useProtect() && !protect(top_, size_, true)) throw Error(ERR_CANT_PROTECT);
isCalledCalcJmpAddress_ = true;
}
public:
+ enum ProtectMode {
+ PROTECT_RW = 0, // read/write
+ PROTECT_RWE = 1, // read/write/exec
+ PROTECT_RE = 2 // read/exec
+ };
explicit CodeArray(size_t maxSize, void *userPtr = 0, Allocator *allocator = 0)
- : type_(userPtr == AutoGrow ? AUTO_GROW : userPtr ? USER_BUF : ALLOC_BUF)
+ : type_(userPtr == AutoGrow ? AUTO_GROW : (userPtr == 0 || userPtr == DontSetProtectRWE) ? ALLOC_BUF : USER_BUF)
, alloc_(allocator ? allocator : (Allocator*)&defaultAllocator_)
, maxSize_(maxSize)
, top_(type_ == USER_BUF ? reinterpret_cast<uint8*>(userPtr) : alloc_->alloc((std::max<size_t>)(maxSize, 1)))
, isCalledCalcJmpAddress_(false)
{
if (maxSize_ > 0 && top_ == 0) throw Error(ERR_CANT_ALLOC);
- if ((type_ == ALLOC_BUF && alloc_->useProtect()) && !protect(top_, maxSize, true)) {
+ if ((type_ == ALLOC_BUF && userPtr != DontSetProtectRWE && useProtect()) && !setProtectMode(PROTECT_RWE, false)) {
alloc_->free(top_);
throw Error(ERR_CANT_PROTECT);
}
virtual ~CodeArray()
{
if (isAllocType()) {
- if (alloc_->useProtect()) protect(top_, maxSize_, false);
+ if (useProtect()) setProtectModeRW(false);
alloc_->free(top_);
}
}
+ bool setProtectMode(ProtectMode mode, bool throwException = true)
+ {
+ bool isOK = protect(top_, maxSize_, mode);
+ if (isOK) return true;
+ if (throwException) throw Error(ERR_CANT_PROTECT);
+ return false;
+ }
+ bool setProtectModeRE(bool throwException = true) { return setProtectMode(PROTECT_RE, throwException); }
+ bool setProtectModeRW(bool throwException = true) { return setProtectMode(PROTECT_RW, throwException); }
void resetSize()
{
size_ = 0;
void dq(uint64 code) { db(code, 8); }
const uint8 *getCode() const { return top_; }
template<class F>
- const F getCode() const { return CastTo<F>(top_); }
+ const F getCode() const { return reinterpret_cast<F>(top_); }
const uint8 *getCurr() const { return &top_[size_]; }
template<class F>
- const F getCurr() const { return CastTo<F>(&top_[size_]); }
+ const F getCurr() const { return reinterpret_cast<F>(&top_[size_]); }
size_t getSize() const { return size_; }
void setSize(size_t size)
{
change exec permission of memory
@param addr [in] buffer address
@param size [in] buffer size
- @param canExec [in] true(enable to exec), false(disable to exec)
+ @param protectMode [in] mode(RW/RWE/RE)
@return true(success), false(failure)
*/
- static inline bool protect(const void *addr, size_t size, bool canExec)
+ static inline bool protect(const void *addr, size_t size, int protectMode)
{
#if defined(_WIN32)
+ const DWORD c_rw = PAGE_READWRITE;
+ const DWORD c_rwe = PAGE_EXECUTE_READWRITE;
+ const DWORD c_re = PAGE_EXECUTE_READ;
+ DWORD mode;
+#else
+ const int c_rw = PROT_READ | PROT_WRITE;
+ const int c_rwe = PROT_READ | PROT_WRITE | PROT_EXEC;
+ const int c_re = PROT_READ | PROT_EXEC;
+ int mode;
+#endif
+ switch (protectMode) {
+ case PROTECT_RW: mode = c_rw; break;
+ case PROTECT_RWE: mode = c_rwe; break;
+ case PROTECT_RE: mode = c_re; break;
+ default:
+ return false;
+ }
+#if defined(_WIN32)
DWORD oldProtect;
- return VirtualProtect(const_cast<void*>(addr), size, canExec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldProtect) != 0;
+ return VirtualProtect(const_cast<void*>(addr), size, mode, &oldProtect) != 0;
#elif defined(__GNUC__)
size_t pageSize = sysconf(_SC_PAGESIZE);
size_t iaddr = reinterpret_cast<size_t>(addr);
size_t roundAddr = iaddr & ~(pageSize - static_cast<size_t>(1));
- int mode = PROT_READ | PROT_WRITE | (canExec ? PROT_EXEC : 0);
+#ifndef NDEBUG
+ if (pageSize != 4096) fprintf(stderr, "large page(%zd) is used. not tested enough.\n", pageSize);
+#endif
return mprotect(reinterpret_cast<void*>(roundAddr), size + (iaddr - roundAddr), mode) == 0;
#else
return true;
M_ripAddr
};
Address(uint32 sizeBit, bool broadcast, const RegExp& e)
- : Operand(0, MEM, sizeBit), e_(e), label_(0), mode_(M_ModRM), permitVsib_(false), broadcast_(broadcast)
+ : Operand(0, MEM, sizeBit), e_(e), label_(0), mode_(M_ModRM), broadcast_(broadcast)
{
e_.verify();
}
#ifdef XBYAK64
explicit Address(size_t disp)
- : Operand(0, MEM, 64), e_(disp), label_(0), mode_(M_64bitDisp), permitVsib_(false), broadcast_(false){ }
+ : Operand(0, MEM, 64), e_(disp), label_(0), mode_(M_64bitDisp), broadcast_(false){ }
Address(uint32 sizeBit, bool broadcast, const RegRip& addr)
- : Operand(0, MEM, sizeBit), e_(addr.disp_), label_(addr.label_), mode_(addr.isAddr_ ? M_ripAddr : M_rip), permitVsib_(false), broadcast_(broadcast) { }
+ : Operand(0, MEM, sizeBit), e_(addr.disp_), label_(addr.label_), mode_(addr.isAddr_ ? M_ripAddr : M_rip), broadcast_(broadcast) { }
#endif
- void permitVsib() const { permitVsib_ = true; }
RegExp getRegExp(bool optimize = true) const
{
return optimize ? e_.optimize() : e_;
}
Mode getMode() const { return mode_; }
- bool is32bit() const { verify(); return e_.getBase().getBit() == 32 || e_.getIndex().getBit() == 32; }
- bool isOnlyDisp() const { verify(); return !e_.getBase().getBit() && !e_.getIndex().getBit(); } // for mov eax
- size_t getDisp() const { verify(); return e_.getDisp(); }
+ bool is32bit() const { return e_.getBase().getBit() == 32 || e_.getIndex().getBit() == 32; }
+ bool isOnlyDisp() const { return !e_.getBase().getBit() && !e_.getIndex().getBit(); } // for mov eax
+ size_t getDisp() const { return e_.getDisp(); }
uint8 getRex() const
{
- verify();
if (mode_ != M_ModRM) return 0;
return getRegExp().getRex();
}
- bool is64bitDisp() const { verify(); return mode_ == M_64bitDisp; } // for moffset
+ bool is64bitDisp() const { return mode_ == M_64bitDisp; } // for moffset
bool isBroadcast() const { return broadcast_; }
const Label* getLabel() const { return label_; }
bool operator==(const Address& rhs) const
{
- return getBit() == rhs.getBit() && e_ == rhs.e_ && label_ == rhs.label_ && mode_ == rhs.mode_ && permitVsib_ == rhs.permitVsib_ && broadcast_ == rhs.broadcast_;
+ return getBit() == rhs.getBit() && e_ == rhs.e_ && label_ == rhs.label_ && mode_ == rhs.mode_ && broadcast_ == rhs.broadcast_;
}
bool operator!=(const Address& rhs) const { return !operator==(rhs); }
+ bool isVsib() const { return e_.isVsib(); }
private:
RegExp e_;
const Label* label_;
Mode mode_;
- mutable bool permitVsib_;
bool broadcast_;
- void verify() const { if (e_.isVsib() && !permitVsib_) throw Error(ERR_BAD_VSIB_ADDRESSING); }
};
inline const Address& Operand::getAddress() const
Label(const Label& rhs);
Label& operator=(const Label& rhs);
~Label();
+ void clear() { mgr = 0; id = 0; }
int getId() const { return id; }
const uint8 *getAddress() const;
};
typedef XBYAK_STD_UNORDERED_MAP<int, ClabelVal> ClabelDefList;
typedef XBYAK_STD_UNORDERED_MULTIMAP<int, const JmpLabel> ClabelUndefList;
+ typedef XBYAK_STD_UNORDERED_SET<Label*> LabelPtrList;
CodeArray *base_;
// global : stateList_.front(), local : stateList_.back()
mutable int labelId_;
ClabelDefList clabelDefList_;
ClabelUndefList clabelUndefList_;
+ LabelPtrList labelPtrList_;
int getId(const Label& label) const
{
return true;
}
friend class Label;
- void incRefCount(int id) { clabelDefList_[id].refCount++; }
- void decRefCount(int id)
+ void incRefCount(int id, Label *label)
{
+ clabelDefList_[id].refCount++;
+ labelPtrList_.insert(label);
+ }
+ void decRefCount(int id, Label *label)
+ {
+ labelPtrList_.erase(label);
ClabelDefList::iterator i = clabelDefList_.find(id);
if (i == clabelDefList_.end()) return;
if (i->second.refCount == 1) {
#endif
return !list.empty();
}
+ // detach all labels linked to LabelManager
+ void resetLabelPtrList()
+ {
+ for (LabelPtrList::iterator i = labelPtrList_.begin(), ie = labelPtrList_.end(); i != ie; ++i) {
+ (*i)->clear();
+ }
+ labelPtrList_.clear();
+ }
public:
LabelManager()
{
reset();
}
+ ~LabelManager()
+ {
+ resetLabelPtrList();
+ }
void reset()
{
base_ = 0;
stateList_.push_back(SlabelState());
clabelDefList_.clear();
clabelUndefList_.clear();
+ resetLabelPtrList();
}
void enterLocal()
{
SlabelState& st = *label.c_str() == '.' ? stateList_.back() : stateList_.front();
define_inner(st.defList, st.undefList, label, base_->getSize());
}
- void defineClabel(const Label& label)
+ void defineClabel(Label& label)
{
define_inner(clabelDefList_, clabelUndefList_, getId(label), base_->getSize());
label.mgr = this;
+ labelPtrList_.insert(&label);
}
void assign(Label& dst, const Label& src)
{
if (i == clabelDefList_.end()) throw Error(ERR_LABEL_ISNOT_SET_BY_L);
define_inner(clabelDefList_, clabelUndefList_, dst.id, i->second.offset);
dst.mgr = this;
+ labelPtrList_.insert(&dst);
}
bool getOffset(size_t *offset, std::string& label) const
{
{
id = rhs.id;
mgr = rhs.mgr;
- if (mgr) mgr->incRefCount(id);
+ if (mgr) mgr->incRefCount(id, this);
}
inline Label& Label::operator=(const Label& rhs)
{
if (id) throw Error(ERR_LABEL_IS_ALREADY_SET_BY_L);
id = rhs.id;
mgr = rhs.mgr;
- if (mgr) mgr->incRefCount(id);
+ if (mgr) mgr->incRefCount(id, this);
return *this;
}
inline Label::~Label()
{
- if (id && mgr) mgr->decRefCount(id);
+ if (id && mgr) mgr->decRefCount(id, this);
}
inline const uint8* Label::getAddress() const
{
T_B32 = 1 << 26, // m32bcst
T_B64 = 1 << 27, // m64bcst
T_M_K = 1 << 28, // mem{k}
+ T_VSIB = 1 << 29,
+ T_MEM_EVEX = 1 << 30, // use evex if mem
T_XXX
};
void vex(const Reg& reg, const Reg& base, const Operand *v, int type, int code, bool x = false)
if ((a > 0 && a != v) + (b > 0 && b != v) + (c > 0 && c != v) > 0) return Error(err);
return v;
}
- int evex(const Reg& reg, const Reg& base, const Operand *v, int type, int code, bool x = false, bool b = false, int aaa = 0, uint32 VL = 0)
+ int evex(const Reg& reg, const Reg& base, const Operand *v, int type, int code, bool x = false, bool b = false, int aaa = 0, uint32 VL = 0, bool Hi16Vidx = false)
{
if (!(type & (T_EVEX | T_MUST_EVEX))) throw Error(ERR_EVEX_IS_INVALID);
int w = (type & T_EW1) ? 1 : 0;
}
}
}
- bool Vp = !(v ? v->isExtIdx2() : 0);
+ bool Vp = !((v ? v->isExtIdx2() : 0) | Hi16Vidx);
bool z = reg.hasZero() || base.hasZero() || (v ? v->hasZero() : false);
if (aaa == 0) aaa = verifyDuplicate(base.getOpmaskIdx(), reg.getOpmaskIdx(), (v ? v->getOpmaskIdx() : 0), ERR_OPMASK_IS_ALREADY_SET);
db(0x62);
// reg is reg field of ModRM
// immSize is the size for immediate value
// disp8N = 0(normal), disp8N = 1(force disp32), disp8N = {2, 4, 8} ; compressed displacement
- void opAddr(const Address &addr, int reg, int immSize = 0, int disp8N = 0)
+ void opAddr(const Address &addr, int reg, int immSize = 0, int disp8N = 0, bool permitVisb = false)
{
+ if (!permitVisb && addr.isVsib()) throw Error(ERR_BAD_VSIB_ADDRESSING);
if (addr.getMode() == Address::M_ModRM) {
setSIB(addr.getRegExp(), reg, disp8N);
} else if (addr.getMode() == Address::M_rip || addr.getMode() == Address::M_ripAddr) {
}
void opPushPop(const Operand& op, int code, int ext, int alt)
{
- if (op.isREG()) {
- if (op.isBit(16)) db(0x66);
- if (op.getReg().getIdx() >= 8) db(0x41);
- db(alt | (op.getIdx() & 7));
- } else if (op.isMEM()) {
- opModM(op.getAddress(), Reg(ext, Operand::REG, op.getBit()), code);
- } else {
- throw Error(ERR_BAD_COMBINATION);
+ int bit = op.getBit();
+ if (bit == 16 || bit == BIT) {
+ if (bit == 16) db(0x66);
+ if (op.isREG()) {
+ if (op.getReg().getIdx() >= 8) db(0x41);
+ db(alt | (op.getIdx() & 7));
+ return;
+ }
+ if (op.isMEM()) {
+ opModM(op.getAddress(), Reg(ext, Operand::REG, 32), code);
+ return;
+ }
}
+ throw Error(ERR_BAD_COMBINATION);
}
void verifyMemHasSize(const Operand& op) const
{
const Address& addr = op2.getAddress();
const RegExp& regExp = addr.getRegExp();
const Reg& base = regExp.getBase();
+ const Reg& index = regExp.getIndex();
if (BIT == 64 && addr.is32bit()) db(0x67);
int disp8N = 0;
- bool x = regExp.getIndex().isExtIdx();
- if ((type & T_MUST_EVEX) || r.hasEvex() || (p1 && p1->hasEvex()) || addr.isBroadcast() || addr.getOpmaskIdx()) {
+ bool x = index.isExtIdx();
+ if ((type & (T_MUST_EVEX|T_MEM_EVEX)) || r.hasEvex() || (p1 && p1->hasEvex()) || addr.isBroadcast() || addr.getOpmaskIdx()) {
int aaa = addr.getOpmaskIdx();
if (aaa && !(type & T_M_K)) throw Error(ERR_INVALID_OPMASK_WITH_MEMORY);
bool b = false;
if (!(type & (T_B32 | T_B64))) throw Error(ERR_INVALID_BROADCAST);
b = true;
}
- int VL = regExp.isVsib() ? regExp.getIndex().getBit() : 0;
- disp8N = evex(r, base, p1, type, code, x, b, aaa, VL);
+ int VL = regExp.isVsib() ? index.getBit() : 0;
+ disp8N = evex(r, base, p1, type, code, x, b, aaa, VL, index.isExtIdx2());
} else {
vex(r, base, p1, type, code, x);
}
- opAddr(addr, r.getIdx(), (imm8 != NONE) ? 1 : 0, disp8N);
+ opAddr(addr, r.getIdx(), (imm8 != NONE) ? 1 : 0, disp8N, (type & T_VSIB) != 0);
} else {
const Reg& base = op2.getReg();
if ((type & T_MUST_EVEX) || r.hasEvex() || (p1 && p1->hasEvex()) || base.hasEvex()) {
}
if (!isOK) throw Error(ERR_BAD_VSIB_ADDRESSING);
}
- addr.permitVsib();
- opAVX_X_X_XM(isAddrYMM ? Ymm(x1.getIdx()) : x1, isAddrYMM ? Ymm(x2.getIdx()) : x2, addr, type | T_YMM, code);
+ opAVX_X_X_XM(isAddrYMM ? Ymm(x1.getIdx()) : x1, isAddrYMM ? Ymm(x2.getIdx()) : x2, addr, type, code);
}
enum {
xx_yy_zz = 0,
{
if (x.hasZero()) throw Error(ERR_INVALID_ZERO);
checkGather2(x, addr.getRegExp().getIndex(), mode);
- addr.permitVsib();
opVex(x, 0, addr, type, code);
}
/*
{
if (addr.hasZero()) throw Error(ERR_INVALID_ZERO);
if (addr.getRegExp().getIndex().getKind() != kind) throw Error(ERR_BAD_VSIB_ADDRESSING);
- addr.permitVsib();
opVex(x, 0, addr, type, code);
}
public:
const Segment es, cs, ss, ds, fs, gs;
#endif
void L(const std::string& label) { labelMgr_.defineSlabel(label); }
- void L(const Label& label) { labelMgr_.defineClabel(label); }
+ void L(Label& label) { labelMgr_.defineClabel(label); }
+ Label L() { Label label; L(label); return label; }
void inLocalLabel() { labelMgr_.enterLocal(); }
void outLocalLabel() { labelMgr_.leaveLocal(); }
/*
// call(function pointer)
#ifdef XBYAK_VARIADIC_TEMPLATE
template<class Ret, class... Params>
- void call(Ret(*func)(Params...)) { call(CastTo<const void*>(func)); }
+ void call(Ret(*func)(Params...)) { call(reinterpret_cast<const void*>(func)); }
#endif
void call(const void *addr) { opJmpAbs(addr, T_NEAR, 0, 0xE8); }
MUST call ready() to complete generating code if you use AutoGrow mode.
It is not necessary for the other mode if hasUndefinedLabel() is true.
*/
- void ready()
+ void ready(ProtectMode mode = PROTECT_RWE)
{
if (hasUndefinedLabel()) throw Error(ERR_LABEL_IS_NOT_FOUND);
- if (isAutoGrow()) calcJmpAddress();
+ if (isAutoGrow()) {
+ calcJmpAddress();
+ if (useProtect()) setProtectMode(mode);
+ }
}
+ // set read/exec
+ void readyRE() { return ready(PROTECT_RE); }
#ifdef XBYAK_TEST
void dump(bool doClear = true)
{