Brainfuck compiler
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

736 lines
20 KiB

  1. // This is an exercise in futility more than anything else
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <stdint.h>
  6. #include <stdbool.h>
  7. #include <assert.h>
  8. #include <errno.h>
  9. #ifdef __unix__
  10. #include <fcntl.h>
  11. #endif
  12. #define exit_fatal(...) \
  13. do { \
  14. fprintf (stderr, "fatal: " __VA_ARGS__); \
  15. exit (EXIT_FAILURE); \
  16. } while (0)
  17. // --- Safe memory management --------------------------------------------------
  18. static void *
  19. xcalloc (size_t m, size_t n)
  20. {
  21. void *p = calloc (m, n);
  22. if (!p)
  23. exit_fatal ("calloc: %s\n", strerror (errno));
  24. return p;
  25. }
  26. static void *
  27. xrealloc (void *o, size_t n)
  28. {
  29. void *p = realloc (o, n);
  30. if (!p && n)
  31. exit_fatal ("realloc: %s\n", strerror (errno));
  32. return p;
  33. }
  34. // --- Dynamically allocated strings -------------------------------------------
  35. struct str
  36. {
  37. char *str; ///< String data, null terminated
  38. size_t alloc; ///< How many bytes are allocated
  39. size_t len; ///< How long the string actually is
  40. };
  41. static void
  42. str_init (struct str *self)
  43. {
  44. self->len = 0;
  45. self->str = xcalloc (1, (self->alloc = 16));
  46. }
  47. static void
  48. str_ensure_space (struct str *self, size_t n)
  49. {
  50. // We allocate at least one more byte for the terminating null character
  51. size_t new_alloc = self->alloc;
  52. while (new_alloc <= self->len + n)
  53. new_alloc <<= 1;
  54. if (new_alloc != self->alloc)
  55. self->str = xrealloc (self->str, (self->alloc = new_alloc));
  56. }
  57. static void
  58. str_append_data (struct str *self, const void *data, size_t n)
  59. {
  60. str_ensure_space (self, n);
  61. memcpy (self->str + self->len, data, n);
  62. self->str[self->len += n] = '\0';
  63. }
  64. static void
  65. str_append_c (struct str *self, char c)
  66. {
  67. str_append_data (self, &c, 1);
  68. }
  69. // --- Application -------------------------------------------------------------
  70. enum command
  71. {
  72. RIGHT, LEFT, INC, DEC, IN, OUT, BEGIN, END,
  73. SET, EAT, INCACC, DECACC
  74. };
  75. bool grouped[] = { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 };
  76. struct instruction { enum command cmd; int offset; size_t arg; };
  77. #define INSTRUCTION(c, o, a) (struct instruction) { (c), (o), (a) }
  78. // - - Debugging - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  79. #ifdef DEBUG
  80. static void
  81. debug_dump_instruction (FILE *fp, const struct instruction *in)
  82. {
  83. const char *name;
  84. switch (in->cmd)
  85. {
  86. case RIGHT: name = "RIGHT "; break;
  87. case LEFT: name = "LEFT "; break;
  88. case INC: name = "INC "; break;
  89. case DEC: name = "DEC "; break;
  90. case OUT: name = "OUT "; break;
  91. case IN: name = "IN "; break;
  92. case BEGIN: name = "BEGIN "; break;
  93. case END: name = "END "; break;
  94. case SET: name = "SET "; break;
  95. case EAT: name = "EAT "; break;
  96. case INCACC: name = "INCACC"; break;
  97. case DECACC: name = "DECACC"; break;
  98. }
  99. fprintf (fp, "%s %zu", name, in->arg);
  100. if (in->offset != 0)
  101. fprintf (fp, " [%d]", in->offset);
  102. fprintf (fp, "\n");
  103. }
  104. static void
  105. debug_dump (const char *filename, struct instruction *in, size_t len)
  106. {
  107. FILE *fp = fopen (filename, "w");
  108. long indent = 0;
  109. for (size_t i = 0; i < len; i++)
  110. {
  111. if (in[i].cmd == END)
  112. indent--;
  113. for (long k = 0; k < indent; k++)
  114. fputs (" ", fp);
  115. debug_dump_instruction (fp, &in[i]);
  116. if (in[i].cmd == BEGIN)
  117. indent++;
  118. }
  119. fclose (fp);
  120. }
  121. #else
  122. #define debug_dump(...)
  123. #endif
  124. // - - Optimization passes - - - - - - - - - - - - - - - - - - - - - - - - - - -
  125. static size_t
  126. optimize_assignment (struct instruction *irb, size_t irb_len)
  127. {
  128. size_t in = 0, out = 0;
  129. for (; in < irb_len; in++, out++)
  130. {
  131. if (in + 2 < irb_len
  132. && irb[in ].cmd == BEGIN
  133. && irb[in + 1].cmd == DEC && irb[in + 1].arg == 1
  134. && irb[in + 2].cmd == END)
  135. {
  136. irb[out] = INSTRUCTION (SET, 0, 0);
  137. in += 2;
  138. }
  139. else if (out && irb[out - 1].cmd == SET && irb[in].cmd == INC)
  140. irb[--out].arg += irb[in].arg;
  141. else if (out != in)
  142. irb[out] = irb[in];
  143. }
  144. return out;
  145. }
  146. // Add offsets to INC/DEC/SET stuck between LEFT/RIGHT
  147. // and compress the LEFT/RIGHT sequences
  148. static size_t
  149. optimize_offseted_inc_dec (struct instruction *irb, size_t irb_len)
  150. {
  151. size_t in = 0, out = 0;
  152. for (in = 0, out = 0; in < irb_len; in++, out++)
  153. {
  154. intptr_t dir = 0;
  155. if (irb[in].cmd == RIGHT)
  156. dir = irb[in].arg;
  157. else if (irb[in].cmd == LEFT)
  158. dir = -(intptr_t) irb[in].arg;
  159. else
  160. {
  161. irb[out] = irb[in];
  162. continue;
  163. }
  164. while (in + 2 < irb_len)
  165. {
  166. // An immediate offset has its limits on x86-64
  167. if (dir < INT8_MIN || dir > INT8_MAX)
  168. break;
  169. intptr_t diff;
  170. if (irb[in + 2].cmd == RIGHT)
  171. diff = irb[in + 2].arg;
  172. else if (irb[in + 2].cmd == LEFT)
  173. diff = -(intptr_t) irb[in + 2].arg;
  174. else
  175. break;
  176. int cmd = irb[in + 1].cmd;
  177. if (cmd != INC && cmd != DEC && cmd != SET)
  178. break;
  179. irb[out] = irb[in + 1];
  180. irb[out].offset = dir;
  181. dir += diff;
  182. out += 1;
  183. in += 2;
  184. }
  185. for (; in + 1 < irb_len; in++)
  186. {
  187. if (irb[in + 1].cmd == RIGHT)
  188. dir += irb[in + 1].arg;
  189. else if (irb[in + 1].cmd == LEFT)
  190. dir -= (intptr_t) irb[in + 1].arg;
  191. else
  192. break;
  193. }
  194. if (!dir)
  195. out--;
  196. else if (dir > 0)
  197. irb[out] = INSTRUCTION (RIGHT, 0, dir);
  198. else
  199. irb[out] = INSTRUCTION (LEFT, 0, -dir);
  200. }
  201. return out;
  202. }
  203. // Try to eliminate loops that eat a cell and add/subtract its value
  204. // to/from some other cell
  205. static size_t
  206. optimize_inc_dec_loops (struct instruction *irb, size_t irb_len)
  207. {
  208. size_t in = 0, out = 0;
  209. for (in = 0, out = 0; in < irb_len; in++, out++)
  210. {
  211. irb[out] = irb[in];
  212. if (irb[in].cmd != BEGIN)
  213. continue;
  214. bool ok = false;
  215. size_t count = 0;
  216. for (size_t k = in + 1; k < irb_len; k++)
  217. {
  218. if (irb[k].cmd == END)
  219. {
  220. ok = true;
  221. break;
  222. }
  223. if (irb[k].cmd != INC
  224. && irb[k].cmd != DEC)
  225. break;
  226. count++;
  227. }
  228. if (!ok)
  229. continue;
  230. // Stable sort operations by their offsets, put [0] first
  231. bool sorted;
  232. do
  233. {
  234. sorted = true;
  235. for (size_t k = 1; k < count; k++)
  236. {
  237. if (irb[in + k].offset == 0)
  238. continue;
  239. if (irb[in + k + 1].offset != 0
  240. && irb[in + k].offset <= irb[in + k + 1].offset)
  241. continue;
  242. struct instruction tmp = irb[in + k + 1];
  243. irb[in + k + 1] = irb[in + k];
  244. irb[in + k] = tmp;
  245. sorted = false;
  246. }
  247. }
  248. while (!sorted);
  249. // Abort the optimization on duplicate offsets (complication with [0])
  250. for (size_t k = 1; k < count; k++)
  251. if (irb[in + k].offset == irb[in + k + 1].offset)
  252. ok = false;
  253. // XXX: can't make the code longer either
  254. for (size_t k = 1; k <= count; k++)
  255. if (irb[in + k].arg != 1)
  256. ok = false;
  257. if (!ok
  258. || irb[in + 1].cmd != DEC
  259. || irb[in + 1].offset != 0)
  260. continue;
  261. int min_safe_left_offset = 0;
  262. if (in > 1 && irb[in - 1].cmd == RIGHT)
  263. min_safe_left_offset = -irb[in - 1].arg;
  264. bool cond_needed_for_safety = false;
  265. for (size_t k = 0; k < count; k++)
  266. if (irb[in + k + 1].offset < min_safe_left_offset)
  267. {
  268. cond_needed_for_safety = true;
  269. break;
  270. }
  271. in++;
  272. if (cond_needed_for_safety)
  273. out++;
  274. irb[out] = INSTRUCTION (EAT, 0, 0);
  275. for (size_t k = 1; k < count; k++)
  276. irb[out + k] = INSTRUCTION (irb[in + k].cmd == INC
  277. ? INCACC : DECACC, irb[in + k].offset, 0);
  278. in += count;
  279. out += count;
  280. if (cond_needed_for_safety)
  281. irb[out] = INSTRUCTION (END, 0, 0);
  282. else
  283. out--;
  284. }
  285. return out;
  286. }
  287. // - - Loop pairing - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  288. static void
  289. pair_loops (struct instruction *irb, size_t irb_len)
  290. {
  291. size_t nesting = 0;
  292. size_t *stack = xcalloc (sizeof *stack, irb_len);
  293. for (size_t i = 0; i < irb_len; i++)
  294. {
  295. switch (irb[i].cmd)
  296. {
  297. case BEGIN:
  298. stack[nesting++] = i;
  299. break;
  300. case END:
  301. if (nesting <= 0)
  302. exit_fatal ("unbalanced loops\n");
  303. --nesting;
  304. irb[stack[nesting]].arg = i + 1;
  305. // Looping can be disabled by optimizations
  306. if (irb[i].arg)
  307. irb[i].arg = stack[nesting] + 1;
  308. default:
  309. break;
  310. }
  311. }
  312. free (stack);
  313. if (nesting != 0)
  314. exit_fatal ("unbalanced loops\n");
  315. }
  316. // - - Main - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  317. int
  318. main (int argc, char *argv[])
  319. {
  320. if (argc > 3)
  321. exit_fatal ("usage: %s [INPUT-FILE]\n", argv[0]);
  322. FILE *input_file = stdin;
  323. if (argc > 1 && !(input_file = fopen (argv[1], "r")))
  324. exit_fatal ("fopen: %s: %s\n", argv[1], strerror (errno));
  325. const char *output_path = "a.out";
  326. if (argc > 2)
  327. output_path = argv[2];
  328. struct str buffer;
  329. str_init (&buffer);
  330. int c;
  331. while ((c = fgetc (input_file)) != EOF)
  332. str_append_c (&buffer, c);
  333. if (ferror (input_file))
  334. exit_fatal ("can't read program\n");
  335. fclose (input_file);
  336. // - - Decode, group and optimize - - - - - - - - - - - - - - - - - - - - - - -
  337. // This is our Intermediate Representation Buffer
  338. struct instruction *irb = xcalloc (sizeof *irb, buffer.len);
  339. size_t irb_len = 0;
  340. for (size_t i = 0; i < buffer.len; i++)
  341. {
  342. enum command cmd;
  343. switch (buffer.str[i])
  344. {
  345. case '>': cmd = RIGHT; break;
  346. case '<': cmd = LEFT; break;
  347. case '+': cmd = INC; break;
  348. case '-': cmd = DEC; break;
  349. case '.': cmd = OUT; break;
  350. case ',': cmd = IN; break;
  351. case '[': cmd = BEGIN; break;
  352. case ']': cmd = END; break;
  353. default: continue;
  354. }
  355. // The most basic optimization is to group identical commands together
  356. if (!irb_len || !grouped[cmd] || irb[irb_len - 1].cmd != cmd)
  357. irb_len++;
  358. irb[irb_len - 1].cmd = cmd;
  359. irb[irb_len - 1].arg++;
  360. }
  361. debug_dump ("bf-no-opt.txt", irb, irb_len);
  362. irb_len = optimize_assignment (irb, irb_len);
  363. debug_dump ("bf-pre-offsets.txt", irb, irb_len);
  364. irb_len = optimize_offseted_inc_dec (irb, irb_len);
  365. debug_dump ("bf-pre-incdec-unloop.txt", irb, irb_len);
  366. irb_len = optimize_inc_dec_loops (irb, irb_len);
  367. debug_dump ("bf-optimized.txt", irb, irb_len);
  368. pair_loops (irb, irb_len);
  369. debug_dump ("bf-final.txt", irb, irb_len);
  370. // - - Code generation - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  371. str_init (&buffer);
  372. size_t *offsets = xcalloc (sizeof *offsets, irb_len + 1);
  373. bool *sets_flags = xcalloc (sizeof *sets_flags, irb_len);
  374. #define CODE(x) { char t[] = x; str_append_data (&buffer, t, sizeof t - 1); }
  375. #define LE(v) (uint8_t[]) { v, v>>8, v>>16, v>>24, v>>32, v>>40, v>>48, v>>56 }
  376. #define DB(x) { uint64_t v = (x); str_append_data (&buffer, LE (v), 1); }
  377. #define DW(x) { uint64_t v = (x); str_append_data (&buffer, LE (v), 2); }
  378. #define DD(x) { uint64_t v = (x); str_append_data (&buffer, LE (v), 4); }
  379. #define DQ(x) { uint64_t v = (x); str_append_data (&buffer, LE (v), 8); }
  380. enum
  381. {
  382. ELF_LOAD_CODE = 0x400000, // where code is loaded (usual)
  383. ELF_LOAD_DATA = 0x800000 // where the tape is placed
  384. };
  385. CODE ("\xB8") DD (ELF_LOAD_DATA) // mov rax, "ELF_LOAD_DATA"
  386. CODE ("\x30\xDB") // xor bl, bl
  387. for (size_t i = 0; i < irb_len; i++)
  388. {
  389. offsets[i] = buffer.len;
  390. size_t arg = irb[i].arg;
  391. assert (arg <= UINT32_MAX);
  392. int offset = irb[i].offset;
  393. assert (offset <= INT8_MAX && offset >= INT8_MIN);
  394. // Don't save what we've just loaded
  395. if (irb[i].cmd == LEFT || irb[i].cmd == RIGHT)
  396. if (i < 2 || i + 1 >= irb_len
  397. || (irb[i - 2].cmd != LEFT && irb[i - 2].cmd != RIGHT)
  398. || irb[i - 1].cmd != BEGIN
  399. || irb[i + 1].cmd != END)
  400. CODE ("\x88\x18") // mov [rax], bl
  401. switch (irb[i].cmd)
  402. {
  403. case RIGHT:
  404. // add rax, "arg" -- optimistic, no boundary checking
  405. if (arg > INT8_MAX) { CODE ("\x48\x05") DD (arg) }
  406. else { CODE ("\x48\x83\xC0") DB (arg) }
  407. break;
  408. case LEFT:
  409. // sub rax, "arg" -- optimistic, no boundary checking
  410. if (arg > INT8_MAX) { CODE ("\x48\x2D") DD (arg) }
  411. else { CODE ("\x48\x83\xE8") DB (arg) }
  412. break;
  413. case EAT:
  414. // NOTE: the kernel destroys rcx and r11 on syscalls,
  415. // there must be no OUT or IN between EAT and INCACC/DECACC
  416. CODE ("\x88\xD9" "\x30\xDB") // mov cl, bl; xor bl, bl
  417. sets_flags[i] = true;
  418. break;
  419. case INCACC:
  420. if (offset)
  421. {
  422. CODE ("\x00\x48") DB (offset) // add [rax+"offset"], cl
  423. }
  424. else
  425. {
  426. CODE ("\x00\xCB") // add bl, cl
  427. sets_flags[i] = true;
  428. }
  429. break;
  430. case DECACC:
  431. if (offset)
  432. {
  433. CODE ("\x28\x48") DB (offset) // sub [rax+"offset"], cl
  434. }
  435. else
  436. {
  437. CODE ("\x28\xCB") // sub bl, cl
  438. sets_flags[i] = true;
  439. }
  440. break;
  441. case INC:
  442. if (offset)
  443. {
  444. CODE ("\x80\x40") DB (offset) // add byte [rax+"offset"], "arg"
  445. }
  446. else
  447. {
  448. CODE ("\x80\xC3") // add bl, "arg"
  449. sets_flags[i] = true;
  450. }
  451. DB (arg)
  452. break;
  453. case DEC:
  454. if (offset)
  455. {
  456. CODE ("\x80\x68") DB (offset) // sub byte [rax+"offset"], "arg"
  457. }
  458. else
  459. {
  460. CODE ("\x80\xEB") // sub bl, "arg"
  461. sets_flags[i] = true;
  462. }
  463. DB (arg)
  464. break;
  465. case SET:
  466. if (offset)
  467. {
  468. CODE ("\xC6\x40") DB (offset) // mov byte [rax+"offset"], "arg"
  469. }
  470. else
  471. CODE ("\xB3") // mov bl, "arg"
  472. DB (arg)
  473. break;
  474. case OUT:
  475. CODE ("\xE8") DD (0) // call "write"
  476. break;
  477. case IN:
  478. CODE ("\xE8") DD (0) // call "read"
  479. break;
  480. case BEGIN:
  481. // Don't test the register when the flag has been set already;
  482. // this doesn't have much of an effect in practice
  483. if (!i || !sets_flags[i - 1])
  484. CODE ("\x84\xDB") // test bl, bl
  485. CODE ("\x0F\x84\x00\x00\x00\x00") // jz "offsets[arg]"
  486. break;
  487. case END:
  488. // We know that the cell is zero, make this an "if", not a "loop";
  489. // this doesn't have much of an effect in practice
  490. if (!arg)
  491. break;
  492. if (!i || !sets_flags[i - 1])
  493. CODE ("\x84\xDB") // test bl, bl
  494. CODE ("\x0F\x85\x00\x00\x00\x00") // jnz "offsets[arg]"
  495. break;
  496. }
  497. // No sense in reading it out when we overwrite it immediately;
  498. // this doesn't have much of an effect in practice
  499. if (irb[i].cmd == LEFT || irb[i].cmd == RIGHT)
  500. if (i + 1 >= irb_len
  501. || irb[i + 1].cmd != SET
  502. || irb[i + 1].offset != 0)
  503. CODE ("\x8A\x18") // mov bl, [rax]
  504. }
  505. // When there is a loop at the end we need to be able to jump past it
  506. offsets[irb_len] = buffer.len;
  507. // Write an epilog which handles all the OS interfacing
  508. //
  509. // System V x86-64 ABI:
  510. // rax <-> both syscall number and return value
  511. // args -> rdi, rsi, rdx, r10, r8, r9
  512. // trashed <- rcx, r11
  513. enum { SYS_READ = 0, SYS_WRITE = 1, SYS_EXIT = 60 };
  514. CODE ("\xB8") DD (SYS_EXIT) // mov eax, 0x3c
  515. CODE ("\x48\x31\xFF") // xor rdi, rdi
  516. CODE ("\x0F\x05") // syscall
  517. size_t fatal_offset = buffer.len;
  518. CODE ("\x48\x89\xF7") // mov rdi, rsi -- use the string in rsi
  519. CODE ("\x30\xC0") // xor al, al -- look for the nil byte
  520. CODE ("\x48\x31\xC9") // xor rcx, rcx
  521. CODE ("\x48\xF7\xD1") // not rcx -- start from -1
  522. CODE ("\xFC" "\xF2\xAE") // cld; repne scasb -- decrement until found
  523. CODE ("\x48\xF7\xD1") // not rcx
  524. CODE ("\x48\x8D\x51\xFF") // lea rdx, [rcx-1] -- save length in rdx
  525. CODE ("\xB8") DD (SYS_WRITE) // mov eax, "SYS_WRITE"
  526. CODE ("\xBF") DD (2) // mov edi, "STDERR_FILENO"
  527. CODE ("\x0F\x05") // syscall
  528. CODE ("\xB8") DD (SYS_EXIT) // mov eax, "SYS_EXIT"
  529. CODE ("\xBF") DD (1) // mov edi, "EXIT_FAILURE"
  530. CODE ("\x0F\x05") // syscall
  531. size_t read_offset = buffer.len;
  532. CODE ("\x50") // push rax -- save tape position
  533. CODE ("\xB8") DD (SYS_READ) // mov eax, "SYS_READ"
  534. CODE ("\x48\x89\xC7") // mov rdi, rax -- STDIN_FILENO
  535. CODE ("\x66\x6A\x00") // push word 0 -- the default value for EOF
  536. CODE ("\x48\x89\xE6") // mov rsi, rsp -- the char starts at rsp
  537. CODE ("\xBA") DD (1) // mov edx, 1 -- count
  538. CODE ("\x0F\x05") // syscall
  539. CODE ("\x66\x5B") // pop bx
  540. CODE ("\x48\x83\xF8\x00") // cmp rax, 0
  541. CODE ("\x48\x8D\x35") DD (4) // lea rsi, [rel read_message]
  542. CODE ("\x7C") // jl "fatal_offset" -- write failure message
  543. DB ((intptr_t) fatal_offset - (intptr_t) (buffer.len + 1))
  544. CODE ("\x58") // pop rax -- restore tape position
  545. CODE ("\xC3") // ret
  546. CODE ("fatal: read failed\n\0")
  547. size_t write_offset = buffer.len;
  548. CODE ("\x50") // push rax -- save tape position
  549. CODE ("\xB8") DD (SYS_WRITE) // mov eax, "SYS_WRITE"
  550. CODE ("\x48\x89\xC7") // mov rdi, rax -- STDOUT_FILENO
  551. CODE ("\x66\x53") // push bx
  552. CODE ("\x48\x89\xE6") // mov rsi, rsp -- the char starts at rsp
  553. CODE ("\xBA") DD (1) // mov edx, 1 -- count
  554. CODE ("\x0F\x05") // syscall
  555. CODE ("\x66\x5B") // pop bx
  556. CODE ("\x48\x83\xF8\x00") // cmp rax, 0
  557. CODE ("\x48\x8D\x35") DD (4) // lea rsi, [rel write_message]
  558. CODE ("\x7C") // jl "fatal_offset" -- write failure message
  559. DB ((intptr_t) fatal_offset - (intptr_t) (buffer.len + 1))
  560. CODE ("\x58") // pop rax -- restore tape position
  561. CODE ("\xC3") // ret
  562. CODE ("fatal: write failed\n\0")
  563. // Now that we know where each instruction is, fill in relative jumps
  564. for (size_t i = 0; i < irb_len; i++)
  565. {
  566. if (!irb[i].arg)
  567. continue;
  568. // This must accurately reflect the code generators
  569. intptr_t target, fixup = offsets[i];
  570. if (irb[i].cmd == BEGIN || irb[i].cmd == END)
  571. {
  572. fixup += (i && sets_flags[i - 1]) ? 2 : 4;
  573. target = offsets[irb[i].arg];
  574. }
  575. else if (irb[i].cmd == IN) { fixup++; target = read_offset; }
  576. else if (irb[i].cmd == OUT) { fixup++; target = write_offset; }
  577. else continue;
  578. uint64_t v = target - (fixup + 4);
  579. memcpy (buffer.str + fixup, LE (v), 4);
  580. }
  581. free (offsets);
  582. free (sets_flags);
  583. // - - Output - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  584. // Now that we know how long the machine code is, we can write the header.
  585. // Note that for PIE we would need to depend on the dynamic linker, so no.
  586. //
  587. // Recommended reading:
  588. // http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html
  589. // man 5 elf
  590. struct str code = buffer;
  591. str_init (&buffer);
  592. enum
  593. {
  594. ELF_HEADER_SIZE = 64, // size of the ELF header
  595. ELF_PROGRAM_ENTRY_SIZE = 56, // size of a program header
  596. ELF_META_SIZE = ELF_HEADER_SIZE + 2 * ELF_PROGRAM_ENTRY_SIZE
  597. };
  598. // ELF header
  599. CODE ("\x7F" "ELF\x02\x01\x01") // ELF, 64-bit, little endian, v1
  600. CODE ("\x00\x00" "\0\0\0\0\0\0\0") // Unix System V ABI, v0, padding
  601. DW (2) DW (62) DD (1) // executable, x86-64, v1
  602. DQ (ELF_LOAD_CODE + ELF_META_SIZE) // entry point address
  603. DQ (ELF_HEADER_SIZE) DQ (0) // program, section header offset
  604. DD (0) // no processor-specific flags
  605. DW (ELF_HEADER_SIZE) // ELF header size
  606. DW (ELF_PROGRAM_ENTRY_SIZE) DW (2) // program hdr tbl entry size, count
  607. DW (0) DW (0) // section hdr tbl entry size, count
  608. DW (0) // no section index for strings
  609. // Program header for code
  610. // The entry point address seems to require alignment, so map start of file
  611. DD (1) DD (5) // PT_LOAD, PF_R | PF_X
  612. DQ (0) // offset within the file
  613. DQ (ELF_LOAD_CODE) // address in virtual memory
  614. DQ (ELF_LOAD_CODE) // address in physical memory
  615. DQ (code.len + ELF_META_SIZE) // length within the file
  616. DQ (code.len + ELF_META_SIZE) // length within memory
  617. DQ (4096) // segment alignment
  618. // Program header for the tape
  619. DD (1) DD (6) // PT_LOAD, PF_R | PF_W
  620. DQ (0) // offset within the file
  621. DQ (ELF_LOAD_DATA) // address in virtual memory
  622. DQ (ELF_LOAD_DATA) // address in physical memory
  623. DQ (0) // length within the file
  624. DQ (1 << 20) // one megabyte of memory
  625. DQ (4096) // segment alignment
  626. // The section header table is optional and we don't need it for anything
  627. FILE *output_file;
  628. #ifdef __unix__
  629. int output_fd;
  630. if ((output_fd = open (output_path, O_CREAT | O_WRONLY, 0777)) < 0)
  631. exit_fatal ("open: %s: %s\n", output_path, strerror (errno));
  632. if (!(output_file = fdopen (output_fd, "w")))
  633. exit_fatal ("fdopen: %s\n", strerror (errno));
  634. #else
  635. if (!(output_file = fopen (output_path, "w")))
  636. exit_fatal ("fopen: %s: %s\n", output_path, strerror (errno));
  637. #endif
  638. fwrite (buffer.str, buffer.len, 1, output_file);
  639. fwrite (code.str, code.len, 1, output_file);
  640. fclose (output_file);
  641. return 0;
  642. }