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 13KB

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