Upload UI
[lsnes.git] / src / library / assembler.cpp
blob3dab8a7c53492fa1e5f5cc82ed61c14c5796abbe
1 #include "assembler.hpp"
2 #include "serialization.hpp"
3 #include <cstring>
4 #include <iostream>
5 #if !defined(_WIN32) && !defined(_WIN64)
6 #define _USE_BSD
7 #include <unistd.h>
8 #include <sys/mman.h>
9 #else
10 #include <windows.h>
11 #endif
13 #ifndef MAP_ANONYMOUS
14 #ifdef MAP_ANON
15 #define MAP_ANONYMOUS MAP_ANON
16 #endif
17 #endif
19 namespace assembler
21 assembler::assembler()
25 void assembler::_label(label& l)
27 l.set(data.size());
30 void assembler::_label(label& l, const std::string& globalname)
32 l.set(data.size());
33 globals[globalname] = &l;
36 void assembler::byte(uint8_t b)
38 data.push_back(b);
41 void assembler::byte(std::initializer_list<uint8_t> b)
43 for(auto i : b)
44 data.push_back(i);
47 void assembler::byte(const uint8_t* b, size_t l)
49 for(size_t i = 0; i < l; i++)
50 data.push_back(b[i]);
53 void assembler::relocation(std::function<void(uint8_t* location, size_t target, size_t source)> promise,
54 const label& target)
56 reloc r;
57 r.promise = promise;
58 r.target = &target;
59 r.source = data.size();
60 relocs.push_back(r);
63 void assembler::align(size_t multiple)
65 if(!multiple)
66 return;
67 while(data.size() % multiple)
68 data.push_back(0);
71 void assembler::pad(size_t amount)
73 for(size_t i = 0; i < amount; i++)
74 data.push_back(0);
77 size_t assembler::size()
79 return data.size();
82 std::map<std::string, void*> assembler::flush(void* base)
84 memcpy(base, &data[0], data.size());
85 for(auto i : relocs) {
86 i.promise((uint8_t*)base + i.source, i.target->resolve((addr_t)base), (addr_t)base + i.source);
88 std::map<std::string, void*> ret;
89 for(auto i : globals) {
90 ret[i.first] = reinterpret_cast<void*>(i.second->resolve((addr_t)base));
92 return ret;
95 void i386_reloc_rel8(uint8_t* location, size_t target, size_t source)
97 int8_t d = target - source - 1;
98 if(source + d + 1 != target) {
99 std::cerr << "Out-of-range offset: " << d << std::endl;
100 throw std::runtime_error("Relative reference (8-bit) out of range");
102 write8sle(location, d);
105 void i386_reloc_rel16(uint8_t* location, size_t target, size_t source)
107 int16_t d = target - source - 2;
108 if(source + d + 2 != target) {
109 std::cerr << "Out-of-range offset: " << d << std::endl;
110 throw std::runtime_error("Relative reference (16-bit) out of range");
112 write32sle(location, d);
115 void i386_reloc_rel32(uint8_t* location, size_t target, size_t source)
117 int32_t d = target - source - 4;
118 if(source + d + 4 != target) {
119 std::cerr << "Out-of-range offset: " << d << std::endl;
120 throw std::runtime_error("Relative reference (32-bit) out of range");
122 write32sle(location, d);
125 void i386_reloc_abs32(uint8_t* location, size_t target, size_t source)
127 if(target > 0xFFFFFFFFU)
128 throw std::runtime_error("Absolute reference out of range");
129 write32ule(location, target);
132 void i386_reloc_abs64(uint8_t* location, size_t target, size_t source)
134 write64ule(location, target);
137 uint8_t i386_modrm(uint8_t reg, uint8_t mod, uint8_t rm)
139 return (mod & 3) * 64 + (reg & 7) * 8 + (rm & 7);
142 uint8_t i386_sib(uint8_t base, uint8_t index, uint8_t scale)
144 return (scale & 3) * 64 + (index & 7) * 8 + (base & 7);
148 dynamic_code::dynamic_code(size_t size)
150 #if !defined(_WIN32) && !defined(_WIN64)
151 asize = (size + getpagesize() - 1) / getpagesize() * getpagesize();
152 base = (uint8_t*)mmap(NULL, asize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
153 if(base == MAP_FAILED)
154 throw std::runtime_error("Failed to allocate memory for routine");
155 #else
156 asize = (size + 4095) >> 12 << 12; //Windows is always i386/amd64, right?
157 base = VirtualAlloc(NULL, asize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
158 if(!base)
159 throw std::runtime_error("Failed to allocate memory for routine");
160 #endif
163 dynamic_code::~dynamic_code()
165 #if !defined(_WIN32) && !defined(_WIN64)
166 munmap(base, asize);
167 #else
168 VirtualFree(base, 0, MEM_RELEASE);
169 #endif
172 void dynamic_code::commit()
174 #if !defined(_WIN32) && !defined(_WIN64)
175 if(mprotect(base, asize, PROT_READ | PROT_EXEC) < 0)
176 throw std::runtime_error("Failed to mark routine as executable");
177 #else
178 long unsigned dummy;
179 if(!VirtualProtect(base, asize, PAGE_EXECUTE_READ, &dummy))
180 throw std::runtime_error("Failed to mark routine as executable");
181 #endif
184 uint8_t* dynamic_code::pointer() throw()
186 return reinterpret_cast<uint8_t*>(base);