Lua: Don't lua_error() out of context with pending dtors
[lsnes.git] / src / library / assembler.cpp
blob604c57b1a91e971f7b25525cdc1861273f5270d5
1 #include "assembler.hpp"
2 #include "serialization.hpp"
3 #include "string.hpp"
4 #include "hex.hpp"
5 #include <cstring>
6 #include <iostream>
7 #include <fstream>
8 #if !defined(_WIN32) && !defined(_WIN64)
9 #define _USE_BSD
10 #include <unistd.h>
11 #include <sys/mman.h>
12 #else
13 #include <windows.h>
14 #endif
16 #ifndef MAP_ANONYMOUS
17 #ifdef MAP_ANON
18 #define MAP_ANONYMOUS MAP_ANON
19 #endif
20 #endif
22 namespace assembler
24 label_list::operator label&()
26 labels.push_back(label());
27 return labels.back();
30 label_list::operator label*()
32 labels.push_back(label());
33 return &labels.back();
36 label& label_list::external(void* addr)
38 labels.push_back(label());
39 labels.back() = label(addr);
40 return labels.back();
43 assembler::assembler()
47 void assembler::_label(label& l)
49 l.set(data.size());
52 void assembler::_label(label& l, const std::string& globalname)
54 l.set(data.size());
55 globals[globalname] = &l;
58 void assembler::byte(uint8_t b)
60 data.push_back(b);
63 void assembler::byte(std::initializer_list<uint8_t> b)
65 for(auto i : b)
66 data.push_back(i);
69 void assembler::byte(const uint8_t* b, size_t l)
71 for(size_t i = 0; i < l; i++)
72 data.push_back(b[i]);
75 void assembler::relocation(std::function<void(uint8_t* location, size_t target, size_t source)> promise,
76 const label& target)
78 reloc r;
79 r.promise = promise;
80 r.target = &target;
81 r.source = data.size();
82 relocs.push_back(r);
85 void assembler::align(size_t multiple)
87 if(!multiple)
88 return;
89 while(data.size() % multiple)
90 data.push_back(0);
93 void assembler::pad(size_t amount)
95 for(size_t i = 0; i < amount; i++)
96 data.push_back(0);
99 size_t assembler::size()
101 return data.size();
104 std::map<std::string, void*> assembler::flush(void* base)
106 memcpy(base, &data[0], data.size());
107 for(auto i : relocs) {
108 i.promise((uint8_t*)base + i.source, i.target->resolve((addr_t)base), (addr_t)base + i.source);
110 std::map<std::string, void*> ret;
111 for(auto i : globals) {
112 ret[i.first] = reinterpret_cast<void*>(i.second->resolve((addr_t)base));
114 return ret;
117 void assembler::dump(const std::string& basename, const std::string& name, void* base,
118 std::map<std::string, void*> map)
120 static unsigned x = 0;
121 std::string realbase = (stringfmt() << basename << "." << (x++)).str();
122 //Dump symbol map.
123 std::ofstream symbols1(realbase + ".syms");
124 symbols1 << hex::to<size_t>((size_t)base, false) << " __base(" << name << ")" << std::endl;
125 for(auto i : map)
126 symbols1 << hex::to<size_t>((size_t)i.second, false) << " " << i.first << std::endl;
127 symbols1 << hex::to<size_t>((size_t)base + data.size(), false) << " __end" << std::endl;
128 symbols1.close();
130 //Dump generated binary.
131 std::ofstream symbols2(realbase + ".bin", std::ios::binary);
132 for(unsigned i = 0; i < data.size(); i++)
133 symbols2 << ((char*)base)[i];
134 symbols2.close();
137 void i386_reloc_rel8(uint8_t* location, size_t target, size_t source)
139 int8_t d = target - source - 1;
140 if(source + d + 1 != target) {
141 std::cerr << "Out-of-range offset: " << d << std::endl;
142 throw std::runtime_error("Relative reference (8-bit) out of range");
144 serialization::s8l(location, d);
147 void i386_reloc_rel16(uint8_t* location, size_t target, size_t source)
149 int16_t d = target - source - 2;
150 if(source + d + 2 != target) {
151 std::cerr << "Out-of-range offset: " << d << std::endl;
152 throw std::runtime_error("Relative reference (16-bit) out of range");
154 serialization::s32l(location, d);
157 void i386_reloc_rel32(uint8_t* location, size_t target, size_t source)
159 int32_t d = target - source - 4;
160 if(source + d + 4 != target) {
161 std::cerr << "Out-of-range offset: " << d << std::endl;
162 throw std::runtime_error("Relative reference (32-bit) out of range");
164 serialization::s32l(location, d);
167 void i386_reloc_abs32(uint8_t* location, size_t target, size_t source)
169 if(target > 0xFFFFFFFFU)
170 throw std::runtime_error("Absolute reference out of range");
171 serialization::u32l(location, target);
174 void i386_reloc_abs64(uint8_t* location, size_t target, size_t source)
176 serialization::u64l(location, target);
179 uint8_t i386_modrm(uint8_t reg, uint8_t mod, uint8_t rm)
181 return (mod & 3) * 64 + (reg & 7) * 8 + (rm & 7);
184 uint8_t i386_sib(uint8_t base, uint8_t index, uint8_t scale)
186 return (scale & 3) * 64 + (index & 7) * 8 + (base & 7);
190 dynamic_code::dynamic_code(size_t size)
192 #if !defined(_WIN32) && !defined(_WIN64)
193 asize = (size + getpagesize() - 1) / getpagesize() * getpagesize();
194 base = (uint8_t*)mmap(NULL, asize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
195 if(base == MAP_FAILED)
196 throw std::runtime_error("Failed to allocate memory for routine");
197 #else
198 asize = (size + 4095) >> 12 << 12; //Windows is always i386/amd64, right?
199 base = VirtualAlloc(NULL, asize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
200 if(!base)
201 throw std::runtime_error("Failed to allocate memory for routine");
202 #endif
205 dynamic_code::~dynamic_code()
207 #if !defined(_WIN32) && !defined(_WIN64)
208 munmap(base, asize);
209 #else
210 VirtualFree(base, 0, MEM_RELEASE);
211 #endif
214 void dynamic_code::commit()
216 #if !defined(_WIN32) && !defined(_WIN64)
217 if(mprotect(base, asize, PROT_READ | PROT_EXEC) < 0)
218 throw std::runtime_error("Failed to mark routine as executable");
219 #else
220 long unsigned dummy;
221 if(!VirtualProtect(base, asize, PAGE_EXECUTE_READ, &dummy))
222 throw std::runtime_error("Failed to mark routine as executable");
223 #endif
226 uint8_t* dynamic_code::pointer() throw()
228 return reinterpret_cast<uint8_t*>(base);