this repo has no description
1#include <functional>
2#include <stdint.h>
3#include <iostream>
4#include <type_traits>
5#include <stdexcept>
6#include <vector>
7#include <string>
8
9struct AssemblyString
10{
11 std::string name;
12 std::string value;
13};
14static std::vector<AssemblyString> _strings;
15
16template <typename Retval>
17void handle_retval() { std::cout << "\t// retval - nop (void or 4-byte integer)\n"; }
18
19template <> void handle_retval<long long>()
20{
21 std::cout << "\t// retval - 8-byte integer\n"
22 "\tmovq %rax, %rdx\n"
23 "\tshrq $32, %rdx\n"; // place upper 32-bits into edx
24 std::cout << "\t// end retval\n";
25}
26template <> void handle_retval<void*>() { handle_retval<long long>(); }
27template <> void handle_retval<unsigned long long>() { handle_retval<long long>(); }
28
29class Context
30{
31public:
32 Context(const char* name)
33 : _name(name)
34 {
35 }
36
37 bool has_int_registers_left() const
38 {
39 return _int_count < 6;
40 }
41
42 const char* next_int_register(bool _32bit = false)
43 {
44 if (!has_int_registers_left())
45 throw std::logic_error("No registers left");
46
47 const char* ARGUMENT_REGS[] = { "%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9" };
48 const char* ARGUMENT_REGS32[] = { "%edi", "%esi", "%edx", "%ecx", "%r8", "%r9" };
49 return _32bit ? ARGUMENT_REGS32[_int_count++] : ARGUMENT_REGS[_int_count++];
50 }
51
52 bool has_fp_registers_left() const
53 {
54 return _fp_count < 8;
55 }
56
57 const char* next_fp_register()
58 {
59 if (!has_fp_registers_left())
60 throw std::logic_error("No fp registers left");
61
62 const char* FP_ARGUMENT_REGS[] = { "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7" };
63 return FP_ARGUMENT_REGS[_fp_count++];
64 }
65
66 int esp_advance(int how_much)
67 {
68 int rv = _esp_offset;
69 _esp_offset += how_much;
70 return rv;
71 }
72
73 int next_int_stack_offset()
74 {
75 if (has_int_registers_left())
76 throw std::logic_error("Registers are still left");
77
78 int offset = (_int_count-6) * 8;
79 offset += std::max(0, (_fp_count-8) * 16);
80
81 _int_count++;
82 return offset;
83 }
84
85 int next_fp_stack_offset()
86 {
87 if (has_fp_registers_left())
88 throw std::logic_error("FP registers are still left");
89
90 int offset = (_fp_count-8) * 16;
91 offset += std::max(0, (_int_count-6) * 8);
92
93 _fp_count++;
94 return offset;
95 }
96
97protected:
98 const char* _name;
99 // Next offset in 32-bit argument stack
100 int _esp_offset = 4;
101 // How many integer arguments have been converted so far
102 int _int_count = 0;
103 // How many floating point arguments have been converted so far
104 int _fp_count = 0;
105};
106
107template <typename Argtype>
108 void copy_argument(Context& ctxt)
109{
110 std::cout << "\tmovl " << ctxt.esp_advance(4) << "(%rsp), " << ctxt.next_int_register(true) << std::endl;
111}
112
113template <>
114 void copy_argument<long long>(Context& ctxt)
115{
116 std::cout << "\tmovq " << ctxt.esp_advance(8) << "(%rsp), " << ctxt.next_int_register() << std::endl;
117}
118
119template <typename Argtype> void count_argument(int& ints, int& fps) { ints++; }
120template <> void count_argument<float>(int& ints, int& fps) { fps++; }
121template <> void count_argument<double>(int& ints, int& fps) { fps++; }
122
123template <typename ...Args>
124class handle_arguments;
125
126template <>
127class handle_arguments<>
128{
129public:
130 static void generate(Context& context, int index = 0)
131 {
132 }
133
134 static void count(int& ints, int& fps)
135 {
136 }
137};
138
139template <typename Arg0, typename ...Args>
140class handle_arguments<Arg0, Args...>
141{
142public:
143 static void generate(Context& ctxt, int index = 0)
144 {
145 std::cout << "\t// argument #" << index << "\n";
146 copy_argument<Arg0>(ctxt);
147 handle_arguments<Args...>::generate(ctxt, index+1);
148 }
149
150 static void count(int& ints, int& fps)
151 {
152 count_argument<Arg0>(ints, fps);
153 handle_arguments<Args...>::count(ints, fps);
154 }
155};
156
157template <typename Retval, typename ...Args>
158class Stubifier;
159
160template <typename Retval, typename ...Args>
161class Stubifier<Retval(Args...)> : public Context
162{
163public:
164 Stubifier(const char* name)
165 : Context(name)
166 {
167 }
168
169 void generate()
170 {
171 int stackalign = 4; // for return address of the call to this stub
172 int ints = 0, fps = 0;
173
174 _strings.push_back(AssemblyString{ std::string("_name_") + _name, _name });
175
176 std::cout << ".globl _" << _name << "\n_"
177 << _name << ":\n"
178 "\tprologue\n";
179
180 std::cout << "\t// Get real implementation\n";
181 std::cout << "\tmovq _" << _name << "_impl(%rip), %r10\n";
182 std::cout << "\ttestq %r10, %r10\n"
183 "\tjnz 1f\n"
184 "\tloadfunc _name_" << _name << ", _" << _name << "_impl\n";
185
186 std::cout << "1:\n";
187 handle_arguments<Args...>::count(ints, fps);
188
189 if (ints >= 1)
190 {
191 stackalign += 4; // calee save edi
192 if (ints >= 2)
193 {
194 stackalign += 4; // calee save esi
195 if (ints > 6)
196 stackalign += (ints-6) * 8;
197 }
198 }
199 if (fps > 8)
200 stackalign += (fps-8) * 16;
201
202 // The stack must be 16-byte aligned
203 stackalign = (stackalign + 15) / 16;
204 stackalign *= 16;
205 stackalign -= 4; // subtract the 4 bytes we had from the start
206
207 // Arguments on the stack are now farther away
208 _esp_offset += stackalign;
209
210 std::cout << "\t// shift stack for alignment and/or extra stuff on stack\n";
211 std::cout << "\tsubq $" << stackalign << ", %rsp\n";
212
213 // esi and edi are calee save
214 if (ints >= 1)
215 {
216 std::cout << "\t// calee save registers\n";
217 std::cout << "\tmovl %edi, " << stackalign-4 << "(%rsp)\n";
218 if (ints >= 2)
219 std::cout << "\tmovl %esi, " << stackalign-8 << "(%rsp)\n";
220 }
221
222 handle_arguments<Args...>::generate(*this);
223
224 // call the implementation
225 std::cout << "\t// call the implementation\n"
226 "\tcallq *%r10\n";
227
228 handle_retval<Retval>();
229
230 // restore edi and esi
231 if (ints >= 1)
232 {
233 std::cout << "\t// restore calee save registers\n";
234 if (ints >= 2)
235 std::cout << "\tmovl " << stackalign-8 << "(%rsp), %esi\n";
236 std::cout << "\tmovl " << stackalign-4 << "(%rsp), %edi\n";
237 }
238
239 std::cout << "\taddq $" << stackalign << ", %rsp\n";
240
241 std::cout << "\tepilogue\n"
242 "// end of " << _name << "\n\n";
243 std::cout << ".zerofill __DATA,__bss,_" << _name << "_impl,8,3\n";
244 }
245
246};
247
248void print_init()
249{
250 std::cout << ".zerofill __DATA,__bss,_lib_handle,8,3\n";
251
252 std::cout << ".code64\n";
253 std::cout << "_initializer:\n"
254 "\tpushq %rdi\n"
255 "\tmovq L__darling_elfcalls$non_lazy_ptr(%rip), %rax\n"
256 "\tmovq (%rax), %rax\n"
257 "\tmovq _library_name(%rip), %rdi\n"
258 "\tcallq *(%rax)\n"
259 "\tmovq %rax, _lib_handle(%rip)\n"
260 "\tpopq %rdi\n"
261 "\tret\n\n";
262
263 std::cout << "_destructor:\n"
264 "\tpushq %rdi\n"
265 "\tmovq L__darling_elfcalls$non_lazy_ptr(%rip), %rcx\n"
266 "\tmovq (%rcx), %rcx\n"
267 "\tmovq _lib_handle(%rip), %rdi\n"
268 "\tcallq *8(%rcx)\n"
269 "\tpopq %rdi\n"
270 "\tret\n\n";
271
272 std::cout << ".code32\n";
273 std::cout << ".section __DATA,__mod_init_func,mod_init_funcs\n"
274 ".align 2\n"
275 ".long _initializer\n"
276 ".section __DATA,__mod_term_func,mod_term_funcs\n"
277 ".align 2\n"
278 ".long _destructor\n\n";
279}
280
281#define STUBIFY(name) Stubifier<decltype(name)>(#name).generate()
282
283void produce_stubs();
284extern const char* library_name;
285
286int main()
287{
288 _strings.push_back(AssemblyString{ "_library_name", library_name });
289
290 std::cout << ".section __TEXT,__text,regular,pure_instructions\n\n";
291
292 std::cout << ".macro prologue\n"
293 "\tsubl $$8, %esp\n"
294 "\tmovl $$0x33, 4(%esp)\n"
295 "\tleal 2f, %eax\n"
296 "\tmovl %eax, (%esp)\n"
297 "\tlret\n"
298 /* Now in 64-bit mode */
299 "2:\n" \
300 ".code64\n" \
301 ".endmacro\n\n";
302
303 std::cout << ".macro epilogue\n"
304 /* Transition 64->32 */
305 "\tsubq $$8, %rsp\n"
306 "\tleaq 3f, %rcx\n"
307 "\tmovl $$0x23, 4(%rsp)\n"
308 "\tmovl %ecx, (%rsp)\n"
309 "\tlret\n"
310 "3:\n"
311 ".code32\n"
312 "\tpush $$0x2b\n"
313 "\tpop %ds\n"
314 "\tret\n"
315 ".endmacro\n\n";
316
317 std::cout << ".macro loadfunc\n"
318 "\tpushq %rdi\n"
319 "\tpushq %rsi\n"
320 "\tmovq _lib_handle(%rip), %rdi\n"
321 "\tmovq $0(%rip), %rsi\n"
322 "\tmovq L__darling_elfcalls$$non_lazy_ptr(%rip), %rax\n"
323 "\tmovq (%rax), %rax\n"
324 "\tcallq *16(%rax)\n"
325 "\tmovq %rax, $1\n"
326 "\tmovq %rax, %r10\n"
327 "\tpopq %rsi\n"
328 "\tpopq %rdi\n"
329 ".endmacro\n\n";
330
331 print_init();
332 produce_stubs();
333
334 std::cout << ".section __TEXT,__cstring,cstring_literals\n";
335 for (const AssemblyString& str : _strings)
336 {
337 std::cout << "L_." << str.name.c_str() << ": .asciz \"" << str.value.c_str() << "\"\n";
338 std::cout << str.name.c_str() << ": .long L_." << str.name.c_str() << std::endl;
339 }
340 std::cout << std::endl;
341
342 std::cout << ".section __IMPORT,__pointers,non_lazy_symbol_pointers\n"
343 "L__darling_elfcalls$non_lazy_ptr:\n"
344 ".indirect_symbol __darling_elfcalls\n"
345 ".long 0\n\n";
346
347 std::cout << ".subsections_via_symbols\n\n";
348
349 return 0;
350}
351
352#include "produce_stubs.h"
353
354