Add <functional> to files that use std::function
[lsnes.git] / src / library / assembler.cpp
blob8e962b444364229c95f43f2007de5bf00f2338c6
1 #include "assembler.hpp"
2 #include "serialization.hpp"
3 #include "string.hpp"
4 #include "hex.hpp"
5 #include <functional>
6 #include <cstring>
7 #include <iostream>
8 #include <fstream>
9 #if !defined(_WIN32) && !defined(_WIN64)
10 #define _USE_BSD
11 #include <unistd.h>
12 #include <sys/mman.h>
13 #else
14 #include <windows.h>
15 #endif
17 #ifndef MAP_ANONYMOUS
18 #ifdef MAP_ANON
19 #define MAP_ANONYMOUS MAP_ANON
20 #endif
21 #endif
23 namespace assembler
25 label_list::operator label&()
27 labels.push_back(label());
28 return labels.back();
31 label_list::operator label*()
33 labels.push_back(label());
34 return &labels.back();
37 label& label_list::external(void* addr)
39 labels.push_back(label());
40 labels.back() = label(addr);
41 return labels.back();
44 assembler::assembler()
48 void assembler::_label(label& l)
50 l.set(data.size());
53 void assembler::_label(label& l, const std::string& globalname)
55 l.set(data.size());
56 globals[globalname] = &l;
59 void assembler::byte(uint8_t b)
61 data.push_back(b);
64 void assembler::byte(std::initializer_list<uint8_t> b)
66 for(auto i : b)
67 data.push_back(i);
70 void assembler::byte(const uint8_t* b, size_t l)
72 for(size_t i = 0; i < l; i++)
73 data.push_back(b[i]);
76 void assembler::relocation(std::function<void(uint8_t* location, size_t target, size_t source)> promise,
77 const label& target)
79 reloc r;
80 r.promise = promise;
81 r.target = &target;
82 r.source = data.size();
83 relocs.push_back(r);
86 void assembler::align(size_t multiple)
88 if(!multiple)
89 return;
90 while(data.size() % multiple)
91 data.push_back(0);
94 void assembler::pad(size_t amount)
96 for(size_t i = 0; i < amount; i++)
97 data.push_back(0);
100 size_t assembler::size()
102 return data.size();
105 std::map<std::string, void*> assembler::flush(void* base)
107 memcpy(base, &data[0], data.size());
108 for(auto i : relocs) {
109 i.promise((uint8_t*)base + i.source, i.target->resolve((addr_t)base), (addr_t)base + i.source);
111 std::map<std::string, void*> ret;
112 for(auto i : globals) {
113 ret[i.first] = reinterpret_cast<void*>(i.second->resolve((addr_t)base));
115 return ret;
118 void assembler::dump(const std::string& basename, const std::string& name, void* base,
119 std::map<std::string, void*> map)
121 static unsigned x = 0;
122 std::string realbase = (stringfmt() << basename << "." << (x++)).str();
123 //Dump symbol map.
124 std::ofstream symbols1(realbase + ".syms");
125 symbols1 << hex::to<size_t>((size_t)base, false) << " __base(" << name << ")" << std::endl;
126 for(auto i : map)
127 symbols1 << hex::to<size_t>((size_t)i.second, false) << " " << i.first << std::endl;
128 symbols1 << hex::to<size_t>((size_t)base + data.size(), false) << " __end" << std::endl;
129 symbols1.close();
131 //Dump generated binary.
132 std::ofstream symbols2(realbase + ".bin", std::ios::binary);
133 for(unsigned i = 0; i < data.size(); i++)
134 symbols2 << ((char*)base)[i];
135 symbols2.close();
138 void i386_reloc_rel8(uint8_t* location, size_t target, size_t source)
140 int8_t d = target - source - 1;
141 if(source + d + 1 != target) {
142 std::cerr << "Out-of-range offset: " << d << std::endl;
143 throw std::runtime_error("Relative reference (8-bit) out of range");
145 serialization::s8l(location, d);
148 void i386_reloc_rel16(uint8_t* location, size_t target, size_t source)
150 int16_t d = target - source - 2;
151 if(source + d + 2 != target) {
152 std::cerr << "Out-of-range offset: " << d << std::endl;
153 throw std::runtime_error("Relative reference (16-bit) out of range");
155 serialization::s32l(location, d);
158 void i386_reloc_rel32(uint8_t* location, size_t target, size_t source)
160 int32_t d = target - source - 4;
161 if(source + d + 4 != target) {
162 std::cerr << "Out-of-range offset: " << d << std::endl;
163 throw std::runtime_error("Relative reference (32-bit) out of range");
165 serialization::s32l(location, d);
168 void i386_reloc_abs32(uint8_t* location, size_t target, size_t source)
170 if(target > 0xFFFFFFFFU)
171 throw std::runtime_error("Absolute reference out of range");
172 serialization::u32l(location, target);
175 void i386_reloc_abs64(uint8_t* location, size_t target, size_t source)
177 serialization::u64l(location, target);
180 uint8_t i386_modrm(uint8_t reg, uint8_t mod, uint8_t rm)
182 return (mod & 3) * 64 + (reg & 7) * 8 + (rm & 7);
185 uint8_t i386_sib(uint8_t base, uint8_t index, uint8_t scale)
187 return (scale & 3) * 64 + (index & 7) * 8 + (base & 7);
191 dynamic_code::dynamic_code(size_t size)
193 #if !defined(_WIN32) && !defined(_WIN64)
194 asize = (size + getpagesize() - 1) / getpagesize() * getpagesize();
195 base = (uint8_t*)mmap(NULL, asize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
196 if(base == MAP_FAILED)
197 throw std::runtime_error("Failed to allocate memory for routine");
198 #else
199 asize = (size + 4095) >> 12 << 12; //Windows is always i386/amd64, right?
200 base = VirtualAlloc(NULL, asize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
201 if(!base)
202 throw std::runtime_error("Failed to allocate memory for routine");
203 #endif
206 dynamic_code::~dynamic_code()
208 #if !defined(_WIN32) && !defined(_WIN64)
209 munmap(base, asize);
210 #else
211 VirtualFree(base, 0, MEM_RELEASE);
212 #endif
215 void dynamic_code::commit()
217 #if !defined(_WIN32) && !defined(_WIN64)
218 if(mprotect(base, asize, PROT_READ | PROT_EXEC) < 0)
219 throw std::runtime_error("Failed to mark routine as executable");
220 #else
221 long unsigned dummy;
222 if(!VirtualProtect(base, asize, PAGE_EXECUTE_READ, &dummy))
223 throw std::runtime_error("Failed to mark routine as executable");
224 #endif
227 uint8_t* dynamic_code::pointer() throw()
229 return reinterpret_cast<uint8_t*>(base);