1 #pragma once
2
3 #include <memory>
4 #include <functional>
5 #include <algorithm>
6 #include <cstdint>
7 #include <string>
8 #include <vector>
9 #include <deque>
10 #include <unordered_map>
11 #include <map>
12 #include <list>
13 #include <type_traits>
14 #include <set>
15 #include "types/base_types.h"
16 #include "types/buffer/abstractbuffer.h"
17 #include "types/buffer/bufferview.h"
18 #include "support/utils.h"
19 #include "redasm_macros.h"
20 #include "redasm_context.h"
21
22 #define ENTRY_FUNCTION "__redasm_entry__"
23 #define START_FUNCTION "__redasm_start__"
24 #define REGISTER_INVALID s64(-1)
25 #define BRANCH_DIRECTION(instruction, destination) (static_cast<s64>(destination) - static_cast<s64>(instruction->address))
26
27 namespace REDasm {
28
29 constexpr size_t npos = static_cast<size_t>(-1);
30
log(const std::string & s)31 inline void log(const std::string& s) { Context::settings.logCallback(s); }
problem(const std::string & s)32 inline void problem(const std::string& s) { Context::problem(s); }
logproblem(const std::string & s)33 inline void logproblem(const std::string& s) { REDasm::log(s); Context::problem(s); }
34
status(const std::string & s)35 inline void status(const std::string& s) {
36 CONTEXT_DEBOUNCE_CHECK
37 Context::settings.statusCallback(s);
38 }
39
statusProgress(const std::string & s,size_t p)40 inline void statusProgress(const std::string& s, size_t p) {
41 CONTEXT_DEBOUNCE_CHECK
42 Context::settings.statusCallback(s);
43 Context::settings.progressCallback(p);
44 }
45
statusAddress(const std::string & s,address_t address)46 inline void statusAddress(const std::string& s, address_t address) {
47 CONTEXT_DEBOUNCE_CHECK
48 Context::settings.statusCallback(s + " @ " + REDasm::hex(address));
49 }
50
makePath(const std::string & p,T...args)51 template<typename... T> std::string makePath(const std::string& p, T... args) {
52 std::string path = p;
53 std::deque<std::string> v = { args... };
54
55 for(size_t i = 0; i < v.size(); i++)
56 {
57 if(!path.empty() && (path.back() != Context::dirSeparator[0]))
58 path += Context::dirSeparator;
59
60 path += v[i];
61 }
62
63 return path;
64 }
65
66 std::string fileName(const std::string& path);
67 std::string fileNameOnly(const std::string& path);
68 std::string filePath(const std::string& path);
69
makeRntPath(const std::string & p,T...args)70 template<typename...T> std::string makeRntPath(const std::string& p, T... args) { return REDasm::makePath(Context::settings.searchPath, p, args...); }
makeDbPath(const std::string & p,T...args)71 template<typename...T> std::string makeDbPath(const std::string& p, T... args) { return REDasm::makeRntPath("database", p, args...); }
makeLoaderPath(const std::string & p,T...args)72 template<typename...T> std::string makeLoaderPath(const std::string& p, T... args) { return REDasm::makeDbPath("loaders", p, args...); }
makeSignaturePath(const std::string & p,T...args)73 template<typename...T> std::string makeSignaturePath(const std::string& p, T... args) { return REDasm::makeDbPath("signatures", p, args...); }
74
75 enum class SegmentType: u32 {
76 None = 0x00000000,
77 Code = 0x00000001,
78 Data = 0x00000002,
79 Bss = 0x00000004,
80 };
81
82 ENUM_FLAGS_OPERATORS(SegmentType)
83
84 enum class InstructionType: u32 {
85 None = 0x00000000, Stop = 0x00000001, Nop = 0x00000002,
86 Jump = 0x00000004, Call = 0x00000008,
87 Add = 0x00000010, Sub = 0x00000020, Mul = 0x00000040, Div = 0x0000080, Mod = 0x00000100, Lsh = 0x00000200, Rsh = 0x00000400,
88 And = 0x00000800, Or = 0x00001000, Xor = 0x00002000, Not = 0x0004000,
89 Push = 0x00008000, Pop = 0x00010000,
90 Compare = 0x00020000, Load = 0x00040000, Store = 0x00080000,
91
92 Conditional = 0x01000000, Privileged = 0x02000000,
93 Invalid = 0x10000000,
94 Branch = Jump | Call,
95 ConditionalJump = Conditional | Jump,
96 ConditionalCall = Conditional | Call,
97 };
98
99 ENUM_FLAGS_OPERATORS(InstructionType)
100
101 enum class OperandType : u32 {
102 None = 0x00000000,
103 Constant = 0x00000001, // Simple constant
104 Register = 0x00000002, // Register
105 Immediate = 0x00000004, // Immediate Value
106 Memory = 0x00000008, // Direct Memory Pointer
107 Displacement = 0x00000010, // Indirect Memory Pointer
108
109 Local = 0x00010000, // Local Variable
110 Argument = 0x00020000, // Function Argument
111 Target = 0x00040000, // Branch destination
112 };
113
114 ENUM_FLAGS_OPERATORS(OperandType)
115
116 struct Segment
117 {
SegmentSegment118 Segment(): offset(0), address(0), endaddress(0), type(SegmentType::None) { }
SegmentSegment119 Segment(const std::string& name, offset_t offset, address_t address, u64 psize, u64 vsize, SegmentType type): name(name), offset(offset), endoffset(offset + psize), address(address), endaddress(address + vsize), type(type) { }
sizeSegment120 constexpr s64 size() const { return static_cast<s64>(endaddress - address); }
rawSizeSegment121 constexpr s64 rawSize() const { return static_cast<s64>(endoffset - offset); }
emptySegment122 constexpr bool empty() const { return this->size() <= 0; }
containsSegment123 constexpr bool contains(address_t address) const { return (address >= this->address) && (address < endaddress); }
containsOffsetSegment124 constexpr bool containsOffset(offset_t offset) const { return !is(SegmentType::Bss) && ((offset >= this->offset) && (offset < this->endoffset)); }
isSegment125 constexpr bool is(SegmentType t) const { return type & t; }
isPureCodeSegment126 constexpr bool isPureCode() const { return type == SegmentType::Code; }
127
128 std::string name;
129 offset_t offset, endoffset;
130 address_t address, endaddress;
131 SegmentType type;
132 };
133
134 struct RegisterOperand
135 {
RegisterOperandRegisterOperand136 RegisterOperand(): r(REGISTER_INVALID), tag(0) { }
RegisterOperandRegisterOperand137 RegisterOperand(register_id_t r, tag_t tag): r(r), tag(tag) { }
RegisterOperandRegisterOperand138 RegisterOperand(register_id_t r): r(r), tag(0) { }
139
140 register_id_t r;
141 tag_t tag;
142
isValidRegisterOperand143 bool isValid() const { return r != REGISTER_INVALID; }
144 };
145
146 struct DisplacementOperand
147 {
DisplacementOperandDisplacementOperand148 DisplacementOperand(): scale(1), displacement(0) { }
DisplacementOperandDisplacementOperand149 DisplacementOperand(const RegisterOperand& base, const RegisterOperand& index, s64 scale, s64 displacement): base(base), index(index), scale(scale), displacement(displacement) { }
150
151 RegisterOperand base, index;
152 s64 scale;
153 s64 displacement;
154 };
155
156 struct Operand
157 {
OperandOperand158 Operand(): type(OperandType::None), tag(0), size(0), index(-1), loc_index(-1), u_value(0) { }
OperandOperand159 Operand(OperandType type, s32 value, s64 idx, tag_t tag): type(type), tag(tag), size(0), index(idx), loc_index(-1), s_value(value) { }
OperandOperand160 Operand(OperandType type, u32 value, s64 idx, tag_t tag): type(type), tag(tag), size(0), index(idx), loc_index(-1), u_value(value) { }
OperandOperand161 Operand(OperandType type, s64 value, s64 idx, tag_t tag): type(type), tag(tag), size(0), index(idx), loc_index(-1), s_value(value) { }
OperandOperand162 Operand(OperandType type, u64 value, s64 idx, tag_t tag): type(type), tag(tag), size(0), index(idx), loc_index(-1), u_value(value) { }
163
164 OperandType type;
165 tag_t tag;
166 u64 size;
167 s64 index, loc_index;
168 RegisterOperand reg;
169 DisplacementOperand disp;
170 union { s64 s_value; u64 u_value; };
171
displacementIsDynamicOperand172 constexpr bool displacementIsDynamic() const { return is(OperandType::Displacement) && (disp.base.isValid() || disp.index.isValid()); }
displacementCanBeAddressOperand173 constexpr bool displacementCanBeAddress() const { return is(OperandType::Displacement) && (disp.displacement > 0); }
isCharacterOperand174 constexpr bool isCharacter() const { return is(OperandType::Constant) && (u_value <= 0xFF) && ::isprint(static_cast<u8>(u_value)); }
isNumericOperand175 constexpr bool isNumeric() const { return is(OperandType::Constant) || is(OperandType::Immediate) || is(OperandType::Memory); }
isTargetOperand176 constexpr bool isTarget() const { return type & OperandType::Target; }
isOperand177 constexpr bool is(OperandType t) const { return type & t; }
asTargetOperand178 void asTarget() { type |= OperandType::Target; }
179
checkCharacterOperand180 bool checkCharacter() {
181 if(!is(OperandType::Immediate) || (u_value > 0xFF) || !::isprint(static_cast<u8>(u_value)))
182 return false;
183
184 type = OperandType::Constant;
185 return true;
186 }
187 };
188
189 struct Instruction
190 {
InstructionInstruction191 Instruction(): address(0), type(InstructionType::None), size(0), id(0) { meta.userdata = nullptr; }
~InstructionInstruction192 ~Instruction() { reset(); }
193
194 std::function<void(void*)> free;
195
196 std::string mnemonic;
197 std::deque<Operand> operands;
198 address_t address;
199 InstructionType type;
200 u32 size;
201 instruction_id_t id; // Backend Specific
202
203 struct {
204 void* userdata; // It doesn't survive after AssemblerPlugin::decode() by design
205 std::set<address_t> targets; // Precalulated targets
206 } meta; // 'meta' is not serialized
207
isInstruction208 constexpr bool is(InstructionType t) const { return type & t; }
isInvalidInstruction209 constexpr bool isInvalid() const { return type == InstructionType::Invalid; }
opSizeInstruction210 inline void opSize(size_t index, u64 size) { operands[index].size = size; }
opSizeInstruction211 inline u64 opSize(size_t index) const { return operands[index].size; }
endAddressInstruction212 constexpr address_t endAddress() const { return address + size; }
213
targetsInstruction214 inline std::set<address_t> targets() const { return meta.targets; }
targetInstruction215 inline void target(address_t address) { meta.targets.insert(address); }
216
targetIdxInstruction217 inline void targetIdx(size_t idx) {
218 if(idx >= operands.size())
219 return;
220
221 operands[idx].asTarget();
222
223 if(operands[idx].isNumeric())
224 meta.targets.insert(operands[idx].u_value);
225 }
226
227 inline Operand* op(size_t idx = 0) { return (idx < operands.size()) ? &operands[idx] : nullptr; }
228 inline Instruction& mem(address_t v, tag_t tag = 0) { operands.emplace_back(OperandType::Memory, v, operands.size(), tag); return *this; }
229 template<typename T> Instruction& cnst(T v, tag_t tag = 0) { operands.emplace_back(OperandType::Constant, v, operands.size(), tag); return *this; }
230 template<typename T> Instruction& imm(T v, tag_t tag = 0) { operands.emplace_back(OperandType::Immediate, v, operands.size(), tag); return *this; }
231 template<typename T> Instruction& disp(register_id_t base, T displacement = 0) { return disp(base, REGISTER_INVALID, displacement); }
dispInstruction232 template<typename T> Instruction& disp(register_id_t base, register_id_t index, T displacement) { return disp(base, index, 1, displacement); }
233 template<typename T> Instruction& disp(register_id_t base, register_id_t index, s64 scale, T displacement);
argInstruction234 template<typename T> Instruction& arg(s64 locindex, register_id_t base, register_id_t index, T displacement) { return local(locindex, base, index, displacement, OperandType::Argument); }
235 template<typename T> Instruction& local(s64 locindex, register_id_t base, register_id_t index, T displacement, OperandType type = OperandType::Local);
236
237 Instruction& reg(register_id_t r, tag_t tag = 0) {
238 Operand op;
239 op.index = operands.size();
240 op.type = OperandType::Register;
241 op.reg = RegisterOperand(r, tag);
242
243 operands.emplace_back(op);
244 return *this;
245 }
246
targetInstruction247 const Operand* target() const {
248 for(const Operand& op : operands) {
249 if(op.isTarget())
250 return &op;
251 }
252
253 return nullptr;
254 }
255
resetInstruction256 void reset() {
257 type = InstructionType::None;
258 size = 0;
259 operands.clear();
260
261 if(free && meta.userdata) {
262 free(meta.userdata);
263 meta.userdata = nullptr;
264 }
265 }
266 };
267
disp(register_id_t base,register_id_t index,s64 scale,T displacement)268 template<typename T> Instruction& Instruction::disp(register_id_t base, register_id_t index, s64 scale, T displacement)
269 {
270 Operand op;
271 op.index = operands.size();
272
273 if((base == REGISTER_INVALID) && (index == REGISTER_INVALID))
274 {
275 op.type = OperandType::Memory;
276 op.u_value = scale * displacement;
277 }
278 else
279 {
280 op.type = OperandType::Displacement;
281 op.disp = DisplacementOperand(RegisterOperand(base), RegisterOperand(index), scale, displacement);
282 }
283
284 operands.emplace_back(op);
285 return *this;
286 }
287
local(s64 locindex,register_id_t base,register_id_t index,T displacement,OperandType type)288 template<typename T> Instruction& Instruction::local(s64 locindex, register_id_t base, register_id_t index, T displacement, OperandType type)
289 {
290 Operand op;
291 op.index = operands.size();
292 op.loc_index = locindex;
293 op.type = OperandType::Displacement | type;
294 op.disp = DisplacementOperand(RegisterOperand(base), RegisterOperand(index), 1, displacement);
295
296 operands.emplace_back(op);
297 return *this;
298 }
299
300 typedef std::shared_ptr<Instruction> InstructionPtr;
301 typedef std::deque<Operand> OperandList;
302 typedef std::deque<Segment> SegmentList;
303
304 } // namespace REDasm
305