A shell that runs x86_64 assembly
c
x86-64
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.

compile.c 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. #include "compile.h"
  2. static void _child_cleanup(asmsh_asmc_child_t *child);
  3. asmsh_asmc_ctx_t* asmsh_asmc_ctx_default()
  4. {
  5. char * const args[] = ASMSH_COMPILE_ARGS;
  6. return asmsh_asmc_ctx(ASMSH_COMPILE_AS, ASMSH_COMPILE_OBJ, args, 1, NULL);
  7. }
  8. asmsh_asmc_ctx_t* asmsh_asmc_ctx(const char *progname, \
  9. const char *result_path, char* const args[], int pre_spawn,
  10. struct timeval *ctimeout)
  11. {
  12. const struct timeval default_timeout = {.tv_sec = 1, .tv_usec = 250000};
  13. asmsh_asmc_ctx_t *res;
  14. int argc; // args count
  15. int errno_bck; // errno will take this value in err labels
  16. int tmpfd;
  17. char *temp_name;
  18. int i;
  19. char * const *ptr;
  20. if((res = malloc(sizeof(*res))) == NULL)
  21. {
  22. errno_bck = errno;
  23. goto err_alloc_res;
  24. }
  25. bzero(res, sizeof(res));
  26. if(asmsh_asmc_buildarg(progname, result_path, args,
  27. &res->args, &res->progname, &res->respath) < 0)
  28. {
  29. errno_bck = errno;
  30. goto err_buildarg;
  31. }
  32. res->child.pid = -1;
  33. res->pre_spawn = pre_spawn?1:0;
  34. if(res->pre_spawn)
  35. {
  36. asmsh_asmc_spawn(res);
  37. }
  38. memcpy(&(res->ctimeout), ctimeout?ctimeout:&default_timeout,
  39. sizeof(struct timeval));
  40. free(temp_name);
  41. return res;
  42. err_buildarg:
  43. free(res);
  44. err_alloc_res:
  45. errno = errno_bck;
  46. return NULL;
  47. }
  48. void asmsh_asmc_ctx_free(asmsh_asmc_ctx_t *ctx)
  49. {
  50. pid_t cpid;
  51. int wstatus;
  52. char **ptr;
  53. if(ctx->child.pid > 0)
  54. {
  55. cpid = waitpid(ctx->child.pid, &wstatus, WNOHANG);
  56. if(cpid != -1 && cpid != 0)
  57. {
  58. if(WCOREDUMP(wstatus))
  59. {
  60. dprintf(2, "Child %d segfault\n", cpid);
  61. ctx->child.pid = 0;
  62. }
  63. else if(WIFEXITED(wstatus))
  64. {
  65. if(WEXITSTATUS(wstatus))
  66. {
  67. dprintf(2, "Child exited with status %d\n",
  68. WEXITSTATUS(wstatus));
  69. }
  70. ctx->child.pid = 0;
  71. }
  72. }
  73. if(ctx->child.pid > 0)
  74. {
  75. if(kill(ctx->child.pid, SIGKILL) == -1)
  76. {
  77. perror("Unable to kill child process");
  78. }
  79. }
  80. ctx->child.pid = 0;
  81. close(ctx->child.pipe_stdin);
  82. }
  83. if(ctx->respath)
  84. {
  85. unlink(ctx->respath);
  86. }
  87. for(ptr = ctx->args; *ptr; ptr++) { free(*ptr); }
  88. free(ctx->args);
  89. free(ctx);
  90. }
  91. int asmsh_asmc_compile(asmsh_asmc_ctx_t *ctx, const char *instr, asmsh_bytecode_t *res)
  92. {
  93. char *reason = NULL;
  94. pid_t wpid;
  95. int wstatus;
  96. if(asmsh_asmc_syntax(instr, &reason) == -1)
  97. {
  98. dprintf(2, "Syntax error %s : '%s'\n",
  99. reason, instr);
  100. errno = EINVAL;
  101. return -1;
  102. }
  103. if(!ctx->pre_spawn && asmsh_asmc_spawn(ctx) == -1)
  104. {
  105. return -1;
  106. }
  107. wpid = waitpid(ctx->child.pid, &wstatus, WNOHANG);
  108. if(wpid == -1)
  109. {
  110. perror("Unable to check child state");
  111. ctx->child.pid = -1;
  112. _child_cleanup(&ctx->child);
  113. return -1;
  114. }
  115. else if (wpid)
  116. {
  117. if(WCOREDUMP(wstatus))
  118. {
  119. dprintf(2, "Child %d segfault\n", ctx->child.pid);
  120. }
  121. else if (WIFEXITED(wstatus))
  122. {
  123. dprintf(2, "Child %d exited with status %d\n",
  124. ctx->child.pid,
  125. WEXITSTATUS(wstatus));
  126. }
  127. else
  128. {
  129. dprintf(2, "Child %d in unknown status %d\n",
  130. ctx->child.pid, wstatus);
  131. _child_cleanup(&ctx->child);
  132. }
  133. return -1;
  134. }
  135. return asmsh_asmc_compile_unsafe(ctx, instr, res);
  136. }
  137. int asmsh_asmc_compile_unsafe(asmsh_asmc_ctx_t *ctx, const char *_instr,
  138. asmsh_bytecode_t *res)
  139. {
  140. int ret;
  141. size_t instr_sz;
  142. pid_t wpid;
  143. int wstatus, sigfd;
  144. sigset_t sigfdmask, oldmask;
  145. fd_set sigset;
  146. struct timeval stimeout;
  147. char *instr;
  148. sigemptyset(&sigfdmask);
  149. sigaddset(&sigfdmask, SIGCHLD);
  150. sigfillset(&sigfdmask);
  151. /// TODO : early (context ?) sigprocmask ?
  152. if((sigprocmask(SIG_BLOCK, &sigfdmask, &oldmask) == -1))
  153. {
  154. perror("Unable to sigmask");
  155. goto err_early;
  156. }
  157. if((sigfd = signalfd(-1, &sigfdmask, SFD_NONBLOCK)) == -1)
  158. {
  159. perror("Unable to sigfd");
  160. goto err_sigfd;
  161. }
  162. instr_sz = strlen(_instr);
  163. instr = alloca(instr_sz+1);
  164. memcpy(instr, _instr, instr_sz);
  165. instr[instr_sz] = '\n';
  166. instr[instr_sz+1] = '\0';
  167. instr_sz++;
  168. ret = write(ctx->child.pipe_stdin, instr, instr_sz);
  169. if(ret < 0)
  170. {
  171. perror("Unable to send instruction to child process");
  172. goto err;
  173. }
  174. else if(ret < instr_sz)
  175. {
  176. asmsh_log_error("Unable to write the whole instruction in one write, bailout !\n");
  177. close(ctx->child.pipe_stdin);
  178. goto err;
  179. }
  180. close(ctx->child.pipe_stdin);
  181. close(ctx->child.pipe_stderr);
  182. FD_ZERO(&sigset);
  183. FD_SET(sigfd, &sigset);
  184. stimeout = ctx->ctimeout;
  185. do
  186. {
  187. if(select(sigfd+1, &sigset, NULL, NULL, &ctx->ctimeout) == 0)
  188. {
  189. //compilation timeout t_t
  190. asmsh_log_error("Compilation timeout");
  191. goto err;
  192. }
  193. wpid = waitpid(ctx->child.pid, &wstatus, 0);
  194. }while(!wpid);
  195. if(wpid == -1)
  196. {
  197. perror("Unable to wait for as to exit");
  198. goto err_stderr;
  199. }
  200. if(WCOREDUMP(wstatus))
  201. {
  202. asmsh_log_error("as segfault will compiling");
  203. goto err_stderr;
  204. }
  205. if(!WIFEXITED(wstatus))
  206. {
  207. asmsh_log_error("as didn't exited");
  208. goto err_stderr;
  209. }
  210. if(WEXITSTATUS(wstatus))
  211. {
  212. asmsh_log_error("GNU as did not exited with 0 status");
  213. goto err_stderr;
  214. }
  215. if((sigprocmask(SIG_BLOCK, &oldmask, NULL) == -1))
  216. {
  217. perror("Unable to restore sigmask");
  218. goto err;
  219. }
  220. ctx->child.pid = 0;
  221. if(ctx->pre_spawn && asmsh_asmc_spawn(ctx) == -1)
  222. {
  223. perror("Unable to pre-spawn");
  224. goto err;
  225. }
  226. return asmh_asmc_bytecode_from_obj(ctx->respath, res);
  227. err_stderr:
  228. // TODO read stderr & log it
  229. err:
  230. close(sigfd);
  231. err_sigfd:
  232. sigprocmask(SIG_BLOCK, &oldmask, NULL);
  233. err_early:
  234. _child_cleanup(&ctx->child);
  235. return -1;
  236. }
  237. int asmsh_asmc_spawn(asmsh_asmc_ctx_t *ctx)
  238. {
  239. int fpid;
  240. int pipes[2][2];
  241. int err;
  242. int i;
  243. if(ctx->child.pid > 0)
  244. {
  245. errno = EUCLEAN;
  246. return -1;
  247. }
  248. for(i=0; i<2; i++)
  249. {
  250. if(pipe(pipes[i]) == -1)
  251. {
  252. goto err_pipe;
  253. }
  254. }
  255. fpid = fork();
  256. if(fpid == -1)
  257. {
  258. err = errno;
  259. goto err_fork;
  260. }
  261. else if (fpid == 0)
  262. {
  263. asmsh_asmc_child(pipes, ctx->progname, ctx->args);
  264. }
  265. close(pipes[0][0]);
  266. close(pipes[1][1]);
  267. ctx->child.pid = fpid;
  268. ctx->child.pipe_stdin = pipes[0][1];
  269. ctx->child.pipe_stderr = pipes[1][0];
  270. return 0;
  271. err_fork:
  272. i=1;
  273. err_pipe:
  274. for(i; i>=0; i--)
  275. {
  276. close(pipes[i][0]);
  277. close(pipes[i][1]);
  278. }
  279. errno = err;
  280. return -1;
  281. }
  282. void asmsh_asmc_child(const int std_pipes[2][2], const char *progname, char* const args[])
  283. {
  284. close(std_pipes[0][1]);
  285. if(dup2(std_pipes[0][0], 0) < 0)
  286. {
  287. perror("Unable to pipe stdin");
  288. exit(1);
  289. }
  290. execvp(progname, args);
  291. perror("execvp failed");
  292. close(std_pipes[0][0]);
  293. exit(2);
  294. }
  295. int asmsh_asmc_syntax(const char* instr, char **reason)
  296. {
  297. #define explain(ptr, reason){\
  298. if(ptr) {\
  299. *ptr = strdup(reason);\
  300. if(!*ptr) { \
  301. int e = errno;\
  302. perror("Unable to strdup to explain syntax error");\
  303. errno=e;return -1;}}}
  304. const unsigned char *ptr;
  305. switch(*instr)
  306. {
  307. case '.':
  308. explain(reason, "Cannot starts with '.'");
  309. goto err;
  310. default:
  311. break;
  312. }
  313. ptr = instr;
  314. while(*ptr)
  315. {
  316. switch(*ptr)
  317. {
  318. case '\n':
  319. case ';':
  320. case '#':
  321. explain(reason, "Contains forbidden char");
  322. goto err;
  323. default:
  324. break;
  325. }
  326. if(*ptr < 0x20 || *ptr > 0x7E)
  327. {
  328. explain(reason, "Contains char in unexpected value range");
  329. goto err;
  330. }
  331. ptr++;
  332. }
  333. *reason = NULL;
  334. errno = 0;
  335. return 0;
  336. err:
  337. errno = EINVAL;
  338. return -1;
  339. #undef explain
  340. }
  341. static void _child_cleanup(asmsh_asmc_child_t *child)
  342. {
  343. if(child->pid > 0)
  344. {
  345. kill(child->pid, SIGKILL);
  346. }
  347. if(child->pipe_stdin > 0)
  348. {
  349. close(child->pipe_stdin);
  350. }
  351. if(child->pipe_stderr > 0)
  352. {
  353. close(child->pipe_stderr);
  354. }
  355. child->pid = child->pipe_stdin = child->pipe_stderr = -1;
  356. }
  357. int asmh_asmc_bytecode_from_obj(const char *objfile, asmsh_bytecode_t *bytecode)
  358. {
  359. /*TODO parse ELF header instead of harcoded addr ?
  360. at least check if the object file has the expected
  361. size for the whole file.
  362. Header parsing is better in case the ELF organisation is
  363. platform/version dependent ?
  364. */
  365. // ELF 64 constants
  366. const int bytecode_sz_off = 0x138;
  367. const int bytecode_sz_sz = 4;
  368. const int bytecode_off = 0x40;
  369. int fd, err;
  370. off_t off;
  371. ssize_t rret;
  372. unsigned int bytecode_sz;
  373. if((fd = open(objfile, O_RDONLY)) < 0)
  374. {
  375. perror("Unable to open bytecode's object file");
  376. return -1;
  377. }
  378. if((off = lseek(fd, bytecode_sz_off, SEEK_SET)) != bytecode_sz_off)
  379. {
  380. /// TODO check off
  381. perror("Unable to seek at bytecode size offset");
  382. err = errno;
  383. goto err;
  384. }
  385. if((rret = read(fd, &bytecode_sz, bytecode_sz_sz)) != bytecode_sz_sz)
  386. {
  387. err = errno;
  388. if(rret < 0)
  389. {
  390. perror("Unable to read bytecode size");
  391. }
  392. else
  393. {
  394. dprintf(2, "Unable to read the 4 bytes of the bytecode size\n");
  395. err = ENODATA;
  396. }
  397. goto err;
  398. }
  399. if(bytecode_sz == 0)
  400. {
  401. dprintf(2, "Null bytecode size");
  402. err = ENODATA;
  403. goto err;
  404. }
  405. if(bytecode_sz > sizeof(bytecode->bytes))
  406. {
  407. dprintf(2, "Bytecode size invalid, too many bytes (%d)\n",
  408. bytecode_sz);
  409. err = ENOBUFS;
  410. goto err;
  411. }
  412. if((off = lseek(fd, bytecode_off, SEEK_SET)) != bytecode_off)
  413. {
  414. /// TODO check off
  415. perror("Unable to seek at bytecode offset");
  416. err = errno;
  417. goto err;
  418. }
  419. bytecode->size = bytecode_sz;
  420. if((rret = read(fd, bytecode->bytes, bytecode_sz)) != bytecode_sz)
  421. {
  422. err = errno;
  423. if(rret < 0)
  424. {
  425. perror("Unable to read bytecode");
  426. }
  427. else
  428. {
  429. dprintf(2, "Unable to read the %d bytes of bytecode\n");
  430. err = ENODATA;
  431. }
  432. goto err;
  433. }
  434. close(fd);
  435. return 0;
  436. err:
  437. close(fd);
  438. errno = err;
  439. return -1;
  440. }
  441. int asmsh_asmc_buildarg(const char* progname, const char *result_tpl, \
  442. char *const args_in[],
  443. char ***args_o, char **progname_o, char **respath_o)
  444. {
  445. int argc, tmpfd, err, i;
  446. char *tempname;
  447. char* const *args_ptr;
  448. if((tempname = strdupa(result_tpl)) == NULL)
  449. {
  450. return -1;
  451. }
  452. if((tmpfd = mkstemp(tempname)) == -1)
  453. {
  454. err = errno;
  455. perror("Unable to create temporary result .o file");
  456. errno = err;
  457. return -1;
  458. }
  459. close(tmpfd);
  460. args_ptr = args_in;
  461. argc = 0;
  462. for(i=0;i<2;i++) // here we overflow if args_in was malformed
  463. {
  464. while(*args_ptr) { args_ptr++; argc++; }
  465. args_ptr++;
  466. argc++;
  467. }
  468. if((*args_o = malloc(sizeof(char*)*(argc))) == NULL)
  469. {
  470. err = errno;
  471. perror("unable to allocate compiler args array");
  472. goto err_malloc;
  473. }
  474. if(((*args_o)[0] = strdup(progname)) == NULL)
  475. {
  476. err = errno;
  477. perror("unable to allocate compiler first argument");
  478. goto err_progname;
  479. }
  480. *progname_o = (*args_o)[0];
  481. *respath_o = NULL;
  482. for(i=1; i<argc-1; i++)
  483. {
  484. char *const *tocopy;
  485. if(args_in[i])
  486. {
  487. tocopy = &(args_in[i]);
  488. }
  489. else
  490. {
  491. tocopy = &tempname;
  492. }
  493. (*args_o)[i] = strdup(*tocopy);
  494. if(!(*args_o)[i])
  495. {
  496. perror("Unable to copy compiler args");
  497. goto err_dupargs;
  498. }
  499. if(!args_in[i])
  500. {
  501. *respath_o = (*args_o)[i];
  502. }
  503. }
  504. (*args_o)[argc-1] = NULL;
  505. return 0;
  506. err_dupargs:
  507. i--;
  508. while(i>0)
  509. {
  510. free((*args_o)[i]);
  511. i--;
  512. }
  513. err_progname:
  514. free(args_o);
  515. *args_o = NULL;
  516. err_malloc:
  517. unlink(tempname);
  518. errno = err;
  519. return -1;
  520. }