123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681 |
- /* Copyright Yann Weber <asmsh@yannweb.net>
- This file is part of asmsh.
-
- asmsh is free software: you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation, either version 3 of the License, or any later version.
-
- asmsh is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- details.
-
- You should have received a copy of the GNU General Public License along
- with asmsh. If not, see <https://www.gnu.org/licenses/>.
- */
- #include "compile.h"
-
- static void _child_cleanup(asmsh_asmc_child_t *child);
-
- asmsh_asmc_ctx_t* asmsh_asmc_ctx_default()
- {
- char * const args[] = ASMSH_COMPILE_ARGS;
- return asmsh_asmc_ctx(ASMSH_COMPILE_AS, ASMSH_COMPILE_OBJ, args, 1, NULL);
- }
-
-
- asmsh_asmc_ctx_t* asmsh_asmc_ctx(const char *progname, \
- const char *result_path, char* const args[], int pre_spawn,
- struct timeval *ctimeout)
- {
-
- const struct timeval default_timeout = {.tv_sec = 1, .tv_usec = 250000};
- asmsh_asmc_ctx_t *res;
-
- int errno_bck; // errno will take this value in err labels
-
- if((res = malloc(sizeof(*res))) == NULL)
- {
- errno_bck = errno;
- goto err_alloc_res;
- }
- bzero(res, sizeof(*res));
-
- if(asmsh_asmc_buildarg(progname, result_path, args,
- &res->args, &res->progname, &res->respath) < 0)
- {
- errno_bck = errno;
- goto err_buildarg;
- }
-
- res->child.pid = -1;
- res->pre_spawn = pre_spawn?1:0;
- if(res->pre_spawn)
- {
- asmsh_asmc_spawn(res);
- }
-
- memcpy(&(res->ctimeout), ctimeout?ctimeout:&default_timeout,
- sizeof(struct timeval));
-
- return res;
-
- err_buildarg:
- free(res);
- err_alloc_res:
- errno = errno_bck;
- return NULL;
- }
-
-
- void asmsh_asmc_ctx_free(asmsh_asmc_ctx_t *ctx)
- {
- pid_t cpid;
- int wstatus;
- char **ptr;
-
- if(ctx->child.pid > 0)
- {
- cpid = waitpid(ctx->child.pid, &wstatus, WNOHANG);
- if(cpid != -1 && cpid != 0)
- {
- if(WCOREDUMP(wstatus))
- {
- dprintf(2, "Child %d segfault\n", cpid);
- ctx->child.pid = 0;
- }
- else if(WIFEXITED(wstatus))
- {
- if(WEXITSTATUS(wstatus))
- {
- dprintf(2, "Child exited with status %d\n",
- WEXITSTATUS(wstatus));
- }
- ctx->child.pid = 0;
- }
- }
- _child_cleanup(&ctx->child);
- }
- if(ctx->respath)
- {
- unlink(ctx->respath);
- }
-
- for(ptr = ctx->args; *ptr; ptr++) { free(*ptr); }
- free(ctx->args);
- free(ctx);
- }
-
-
- int asmsh_asmc_compile(asmsh_asmc_ctx_t *ctx, const char *instr, asmsh_bytecode_t *res)
- {
- char *reason = NULL;
- pid_t wpid;
- int wstatus;
-
- if(asmsh_asmc_syntax(instr, &reason) == -1)
- {
- dprintf(2, "Syntax error %s : '%s'\n",
- reason, instr);
- errno = EINVAL;
- return -1;
- }
-
- if(ctx->child.pid <= 0 && asmsh_asmc_spawn(ctx) == -1)
- {
- return -1;
- }
-
- wpid = waitpid(ctx->child.pid, &wstatus, WNOHANG);
- if(wpid == -1)
- {
- perror("Unable to check child state");
- ctx->child.pid = -1;
- _child_cleanup(&ctx->child);
- return -1;
- }
- else if (wpid)
- {
- if(WCOREDUMP(wstatus))
- {
- dprintf(2, "Child %d segfault\n", ctx->child.pid);
- }
- else if (WIFEXITED(wstatus))
- {
- dprintf(2, "Child %d exited with status %d\n",
- ctx->child.pid,
- WEXITSTATUS(wstatus));
- }
- else
- {
- dprintf(2, "Child %d in unknown status %d\n",
- ctx->child.pid, wstatus);
- _child_cleanup(&ctx->child);
- }
- return -1;
- }
-
-
- if(!ctx->pre_spawn && asmsh_asmc_spawn(ctx) == -1)
- {
- return -1;
- }
-
-
- return asmsh_asmc_compile_unsafe(ctx, instr, res);
- }
-
- int asmsh_asmc_compile_unsafe(asmsh_asmc_ctx_t *ctx, const char *_instr,
- asmsh_bytecode_t *res)
- {
- int ret, err;
- size_t instr_sz;
- pid_t wpid;
- int wstatus, sigfd;
- sigset_t sigfdmask, oldmask;
- fd_set sigset;
- struct timeval stimeout;
-
- sigemptyset(&sigfdmask);
- sigaddset(&sigfdmask, SIGCHLD);
- sigfillset(&sigfdmask);
-
- instr_sz = strlen(_instr);
- char instr[instr_sz+1];
- memcpy(instr, _instr, instr_sz);
- instr[instr_sz] = '\n';
- instr[instr_sz+1] = '\0';
- instr_sz++;
-
- /// TODO : early (context ?) sigprocmask ?
- if((sigprocmask(SIG_BLOCK, &sigfdmask, &oldmask) == -1))
- {
- err=errno;
- perror("Unable to sigmask");
- goto err_early;
- }
- if((sigfd = signalfd(-1, &sigfdmask, SFD_NONBLOCK)) == -1)
- {
- err=errno;
- perror("Unable to sigfd");
- goto err_sigfd;
- }
-
-
- ret = write(ctx->child.pipe_stdin, instr, instr_sz);
- if(ret < 0)
- {
- err=errno;
- perror("Unable to send instruction to child process");
- goto err;
- }
- else if(ret < instr_sz)
- {
- err=errno;
- asmsh_log_error("Unable to write the whole instruction in one write, bailout !\n");
- close(ctx->child.pipe_stdin);
- goto err;
- }
- close(ctx->child.pipe_stdin);
-
-
- stimeout = ctx->ctimeout;
- do
- {
- FD_ZERO(&sigset);
- FD_SET(sigfd, &sigset);
- if(select(sigfd+1, &sigset, NULL, NULL, &stimeout) == 0)
- {
- //compilation timeout t_t
- asmsh_log_error("Compilation timeout");
- goto err_stderr;
- }
-
- wpid = waitpid(ctx->child.pid, &wstatus, 0);
- }while(!wpid);
-
- if(wpid == -1)
- {
- perror("Unable to wait for as to exit");
- goto err_stderr;
- }
- if(!WIFEXITED(wstatus))
- {
- if(WIFSTOPPED(wstatus))
- {
- asmsh_log_error("as stopped...");
- goto err_stderr;
- }
- asmsh_log_error("as didn't exited");
- goto err_stderr;
- }
- ctx->child.pid = -1;
-
- if(WCOREDUMP(wstatus))
- {
- asmsh_log_error("as segfault will compiling");
- goto err_stderr;
- }
- if(WEXITSTATUS(wstatus))
- {
- asmsh_log_error("GNU as did not exited with 0 status");
- goto err_stderr;
- }
-
- if((sigprocmask(SIG_BLOCK, &oldmask, NULL) == -1))
- {
- err=errno;
- perror("Unable to restore sigmask");
- goto err;
- }
-
- close(ctx->child.pipe_stdout[0]);
- close(ctx->child.pipe_stdout[1]);
- ctx->child.pid = 0;
- if(ctx->pre_spawn && asmsh_asmc_spawn(ctx) == -1)
- {
- err=errno;
- perror("Unable to pre-spawn");
- goto err;
- }
-
- return asmh_asmc_bytecode_from_obj(ctx->respath, res);
-
-
- err_stderr:
- // TODO read stderr & log it
- for(int ip=0; ip<2; ip++)
- {
- int wanted, readed, inbuf;
- char buf[4096], *curbuf;
- curbuf=buf;
- inbuf=0;
- do
- {
- wanted = sizeof(buf)-(1+inbuf);
- readed = read(ctx->child.pipe_stdout[ip], curbuf, wanted);
- if(readed < 0)
- {
- perror("Unable to read ccas stderr");
- }
- else if (readed > 0)
- {
- curbuf[readed] = '\0';
- //inbuf += readed;
- curbuf=buf;
- for(char *tbuf=buf; *tbuf; tbuf++)
- {
- if(*tbuf=='\n')
- {
- *tbuf='\0';
- asmsh_log_error("%s", curbuf);
- *tbuf='\n';
- curbuf=tbuf+1;
- }
- }
- inbuf = strlen(curbuf);
- if(inbuf)
- {
- memmove(buf, curbuf, inbuf);
- buf[inbuf] = '\0';
- curbuf=&buf[inbuf];
- }
- else
- {
- curbuf=buf;
- *buf='\0';
- }
- }
- }while(readed == wanted);
- }
- err=EINVAL;
- err:
- close(ctx->child.pipe_stdout[0]);
- close(ctx->child.pipe_stdout[1]);
- close(sigfd);
- err_sigfd:
- sigprocmask(SIG_BLOCK, &oldmask, NULL);
- err_early:
- _child_cleanup(&ctx->child);
- errno=err;
- return -1;
- }
-
- int asmsh_asmc_spawn(asmsh_asmc_ctx_t *ctx)
- {
- int fpid;
- int pipes[3][2];
- int err;
- int i;
-
- if(ctx->child.pid > 0)
- {
- errno = EUCLEAN;
- return -1;
- }
-
- for(i=0; i<3; i++)
- {
- if(pipe(pipes[i]) == -1)
- {
- err=errno;
- goto err_pipe;
- }
- }
- fpid = fork();
- if(fpid == -1)
- {
- err = errno;
- goto err_fork;
- }
- else if (fpid == 0)
- {
- asmsh_asmc_child(pipes, ctx->progname, ctx->args);
- }
-
- close(pipes[0][0]);
- close(pipes[1][1]);
-
- ctx->child.pid = fpid;
- ctx->child.pipe_stdin = pipes[0][1];
- ctx->child.pipe_stdout[0] = pipes[1][0];
- ctx->child.pipe_stdout[1] = pipes[2][0];
-
- return 0;
-
- err_fork:
- i=1;
- err_pipe:
- for(; i>=0; i--)
- {
- close(pipes[i][0]);
- close(pipes[i][1]);
- }
- errno = err;
- return -1;
- }
-
-
- void asmsh_asmc_child(const int std_pipes[3][2], const char *progname, char* const args[])
- {
- close(std_pipes[0][1]);
- if(dup2(std_pipes[0][0], 0) < 0)
- {
- perror("Unable to pipe stdin");
- exit(1);
- }
- for(int i=1; i<3; i++) // piping stdout & stderr
- {
- close(std_pipes[i][0]);
- if(dup2(std_pipes[i][1], i) < 0)
- {
- perror("Unable to pipe std fd");
- exit(1);
- }
- }
- execvp(progname, args);
- perror("execvp failed");
- close(std_pipes[0][0]);
- exit(2);
- }
-
-
- int asmsh_asmc_syntax(const char* instr, char **reason)
- {
-
- #define explain(ptr, reason){\
- if(ptr) {\
- *ptr = strdup(reason);\
- if(!*ptr) { \
- int e = errno;\
- perror("Unable to strdup to explain syntax error");\
- errno=e;return -1;}}}
-
- const unsigned char *ptr;
-
- switch(*instr)
- {
- case '.':
- explain(reason, "Cannot starts with '.'");
- goto err;
- default:
- break;
- }
-
- ptr = (const unsigned char*)instr;
- while(*ptr)
- {
- switch(*ptr)
- {
- case '\n':
- case ';':
- case '#':
- explain(reason, "Contains forbidden char");
- goto err;
- default:
- break;
- }
- if(*ptr < 0x20 || *ptr > 0x7E)
- {
- explain(reason, "Contains char in unexpected value range");
- goto err;
- }
- ptr++;
- }
-
- *reason = NULL;
- errno = 0;
- return 0;
- err:
- errno = EINVAL;
- return -1;
- #undef explain
- }
-
- static void _child_cleanup(asmsh_asmc_child_t *child)
- {
- if(child->pid > 0)
- {
- kill(child->pid, SIGKILL);
- }
- if(child->pipe_stdin > 0)
- {
- close(child->pipe_stdin);
- }
- if(child->pipe_stdout[0] > 0)
- {
- close(child->pipe_stdout[0]);
- }
- if(child->pipe_stdout[1] > 0)
- {
- close(child->pipe_stdout[1]);
- }
- child->pid = -1;
- child->pipe_stdin = child->pipe_stdout[0] = child->pipe_stdout[1] = -1;
- }
-
-
- int asmh_asmc_bytecode_from_obj(const char *objfile, asmsh_bytecode_t *bytecode)
- {
- /*TODO parse ELF header instead of harcoded addr ?
- at least check if the object file has the expected
- size for the whole file.
- Header parsing is better in case the ELF organisation is
- platform/version dependent ?
- */
- // ELF 64 constants
- const int bytecode_sz_off = 0x138;
- const int bytecode_sz_sz = 4;
- const int bytecode_off = 0x40;
-
- int fd, err;
- off_t off;
- ssize_t rret;
- unsigned int bytecode_sz;
-
- if((fd = open(objfile, O_RDONLY)) < 0)
- {
- perror("Unable to open bytecode's object file");
- return -1;
- }
-
- if((off = lseek(fd, bytecode_sz_off, SEEK_SET)) != bytecode_sz_off)
- {
- /// TODO check off
- perror("Unable to seek at bytecode size offset");
- err = errno;
- goto err;
- }
-
- if((rret = read(fd, &bytecode_sz, bytecode_sz_sz)) != bytecode_sz_sz)
- {
- err = errno;
- if(rret < 0)
- {
- perror("Unable to read bytecode size");
- }
- else
- {
- dprintf(2, "Unable to read the 4 bytes of the bytecode size %ld read instead\n", rret);
- err = ENODATA;
- }
- goto err;
- }
-
- if(bytecode_sz == 0)
- {
- asmsh_log_error("Null bytecode size");
- err = ENODATA;
- goto err;
- }
- if(bytecode_sz > sizeof(bytecode->bytes))
- {
- dprintf(2, "Bytecode size invalid, too many bytes (%d)\n",
- bytecode_sz);
- err = ENOBUFS;
- goto err;
- }
- if((off = lseek(fd, bytecode_off, SEEK_SET)) != bytecode_off)
- {
- /// TODO check off
- perror("Unable to seek at bytecode offset");
- err = errno;
- goto err;
- }
-
- bytecode->size = bytecode_sz;
- if((rret = read(fd, bytecode->bytes, bytecode_sz)) != bytecode_sz)
- {
- err = errno;
- if(rret < 0)
- {
- perror("Unable to read bytecode");
- }
- else
- {
- dprintf(2, "Unable to read the %d bytes of bytecode\n",
- bytecode_sz);
- err = ENODATA;
- }
- goto err;
- }
-
- close(fd);
- return 0;
-
- err:
- close(fd);
- errno = err;
- return -1;
- }
-
-
- int asmsh_asmc_buildarg(const char* progname, const char *result_tpl, \
- char *const args_in[],
- char ***args_o, char **progname_o, char **respath_o)
- {
- int argc, tmpfd, err, i;
- char *tempname;
- char* const *args_ptr;
-
- if((tempname = strdupa(result_tpl)) == NULL)
- {
- return -1;
- }
- if((tmpfd = mkstemp(tempname)) == -1)
- {
- err = errno;
- perror("Unable to create temporary result .o file");
- errno = err;
- return -1;
- }
- close(tmpfd);
-
- args_ptr = args_in;
- argc = 0;
- for(i=0;i<2;i++) // here we overflow if args_in was malformed
- {
- while(*args_ptr) { args_ptr++; argc++; }
- args_ptr++;
- argc++;
- }
- if((*args_o = malloc(sizeof(char*)*(argc))) == NULL)
- {
- err = errno;
- perror("unable to allocate compiler args array");
- goto err_malloc;
- }
-
- if(((*args_o)[0] = strdup(progname)) == NULL)
- {
- err = errno;
- perror("unable to allocate compiler first argument");
- goto err_progname;
- }
- *progname_o = (*args_o)[0];
- *respath_o = NULL;
-
- for(i=1; i<argc-1; i++)
- {
- char *const *tocopy;
- if(args_in[i])
- {
- tocopy = &(args_in[i]);
- }
- else
- {
- tocopy = &tempname;
- }
- (*args_o)[i] = strdup(*tocopy);
- if(!(*args_o)[i])
- {
- err=errno;
- perror("Unable to copy compiler args");
- goto err_dupargs;
- }
- if(!args_in[i])
- {
- *respath_o = (*args_o)[i];
- }
- }
- (*args_o)[argc-1] = NULL;
-
- return 0;
-
- err_dupargs:
- i--;
- while(i>0)
- {
- free((*args_o)[i]);
- i--;
- }
-
- err_progname:
- free(args_o);
- *args_o = NULL;
- err_malloc:
- unlink(tempname);
- errno = err;
- return -1;
- }
|