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

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