/* Check that the linker is not instructed to link *crt*.o files from the * wrong directory. Most of the time this is a configuration issue, e.g. the * wrong value for the -B compiler flag was specified, and now the compiler * has instructed the linker to look for the *crt*.o files a system-default * (e.g. /usr/lib/...) directory. */ static void check_ld_crtoarg(char **argv) { /* If a relevant *crt*.o file is missing from xstaticcld, then * gcc generates e.g. * /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../i386-linux-gnu/crtn.o , and * pts-static-clang generates crtn.o . We detect and fail on both. */ char had_error = 0; char **argi, *supported_dir; const char *arg, *basename, *argend, *supbasename; size_t supdirlen; for (supbasename = argv[0] + strlen(argv[0]); supbasename != argv[0] && supbasename[-1] != '/'; --supbasename) {} supdirlen = supbasename - argv[0]; for (argi = argv + 1; (arg = *argi); ++argi) { if (0 == strcmp(arg, "-o") && argi[1]) { ++argi; continue; } if (arg[0] == '-') continue; argend = arg + strlen(arg); for (basename = argend; basename != arg && basename[-1] != '/'; --basename) {} if (basename[0] == '\0' || argend - arg < 2 || argend[-1] != 'o' || argend[-2] != '.' || ((0 != strncmp(basename, "crt", 3) || (0 != strcmp(basename, "crt0.o") && 0 != strcmp(basename, "crt1.o") && 0 != strcmp(basename, "crt2.o") && 0 != strcmp(basename, "crti.o") && 0 != strcmp(basename, "crtn.o") && 0 != strcmp(basename, "crtbegin.o") && 0 != strcmp(basename, "crtbeginS.o") && 0 != strcmp(basename, "crtbeginT.o") && 0 != strcmp(basename, "crtend.o") && 0 != strcmp(basename, "crtendS.o") && 0 != strcmp(basename, "crtfastmath.o"))) && (0 != strcmp(basename + 1, "crt1.o") || (0 != strcmp(basename, "Scrt1.o") && 0 != strcmp(basename, "Mcrt1.o") && 0 != strcmp(basename, "gcrt1.o"))))) continue; if (0 == strncmp(arg, argv[0], supdirlen)) continue; if (supdirlen == 0) { supported_dir = "."; } else { supported_dir = malloc(supdirlen); memcpy(supported_dir, argv[0], supdirlen - 1); supported_dir[supdirlen - 1] = '\0'; } fdprint(2, strdupcat( "xstatic-ld: error: *crt*.o file linked from unsupported location " "(supported is ", strdupcat(supported_dir, "): ", arg), "\n")); had_error = 1; } if (had_error) exit(122); }
int put_keyring(const char *keyring_filename) { int res; char *tmp_keyring_filename; tmp_keyring_filename = strdupcat(keyring_filename, ".new"); res = write_keyring(tmp_keyring_filename); if (res < 0) { fprintf(stderr, "Unable to write the keyring file %s (%s).\n", tmp_keyring_filename, strerror(errno)); free(tmp_keyring_filename); return -1; } res = rename(tmp_keyring_filename, keyring_filename); if (res < 0) { fprintf(stderr, "Unable to replace keyring file %s with %s(%s).\n", keyring_filename, tmp_keyring_filename, strerror(errno)); } free(tmp_keyring_filename); return res; }
/* Return NULL if not found. Return cmd if it contains a slash. */ static char *find_on_path(const char *cmd) { const char *path; char *pathname; const char *p, *q; size_t scmd, s; struct stat st; if (strstr(cmd, "/")) { if (0 == stat(cmd, &st) && !S_ISDIR(st.st_mode)) { return strdupcat(cmd, "", ""); } } else { path = getenv("PATH"); if (!path || !path[0]) path = "/bin:/usr/bin"; scmd = strlen(cmd); for (p = path; *p != '\0'; p = q + 1) { for (q = p; *q != '\0' && *q != ':'; ++q) {} s = q - p; pathname = malloc(s + 2 + scmd); memcpy(pathname, p, s); pathname[s] = '/'; strcpy(pathname + s + 1, cmd); if (0 == stat(pathname, &st) && !S_ISDIR(st.st_mode)) return pathname; free(pathname); /* TODO(pts): Realloc. */ if (*q == '\0') break; } } return NULL; }
static void check_xflags(char **argv) { char **argi, *arg; for (argi = argv + 1; (arg = *argi); ++argi) { /* TODO(pts): Copy some of these flags to clang_trampoline.cc */ if ((arg[0] == '-' && arg[1] == 'B') || is_argprefix(arg, "--sysroot") || is_argprefix(arg, "--gcc-toolchain") || /* TODO(pts): Accept a few targets (and don't pass the flag). */ 0 == strcmp(arg, "-target") || /* Clang-specific. */ 0 == strcmp(arg, "-m64") || 0 == strcmp(arg, "-sysld") || 0 == strcmp(arg, "--sysld") || 0 == strcmp(arg, "-p") || /* Needs Mcrt1.o (| gcrt1.o), clang crt1.o */ 0 == strcmp(arg, "-pg") || /* Needs gcrt1.o, clang uses crt1.o */ 0 == strcmp(arg, "-pie") || 0 == strcmp(arg, "-fpic") || 0 == strcmp(arg, "-fPIC") || 0 == strcmp(arg, "-fpie") || 0 == strcmp(arg, "-fPIE") || /* This would link Scrt1.o etc. */ 0 == strcmp(arg, "-shared") || 0 == strcmp(arg, "-shared-libgcc")) { fdprint(2, strdupcat( "xstatic: error: flag not supported: ", arg, "\n")); exit(122); } } }
static bool get_logfile(char ** logfile) { char * home; if (!(home = getenv("HOME"))) return (print_perror(false, "getenv")); if (!(*logfile = strdupcat(home, LOGFILE))) return (print_perror(false, "malloc")); return (true); }
static char *get_up_dir_alloc(const char *dir) { char *dirup = NULL, *p; if (0 == strcmp(dir, "..") || (dirup = readlink_alloc(dir))) { free(dirup); return strdupcat(dir, "/..", ""); } dirup = strdupcat(dir, ".", ""); for (p = dirup + strlen(dirup) - 1; p != dirup && p[-1] != '/'; --p) {} if (p == dirup) { *p++ = '.'; if (0 == strcmp(dir, ".")) *p++ = '.'; *p = '\0'; } else if (dirup[0] == '/' && dirup[1] == '\0') { fdprint(2, strdupcat("xstatic: error: no parent dir for: ", dir, "\n")); exit(122); } else { p[-1] = '\0'; /* Remove basename of dirup. */ } return dirup; }
static void check_bflags(char **argv) { char **argi, *arg; for (argi = argv + 1; (arg = *argi); ++argi) { if ((arg[0] == '-' && arg[1] == 'B') || is_argprefix(arg, "--sysroot") || is_argprefix(arg, "--gcc-toolchain")) { fdprint(2, strdupcat( "error: flag not supported in this ldmode: ", arg, "\n")); exit(122); } } }
/* Combines two pathnames to a newly allocated string. The . and .. components * in the beginning of path2 are processed specially (and correctly). * * base must end with a '/'. */ static char *combine_paths_dotdot_alloc(const char *base, const char *path2) { char *path1; if (path2[0] == '/') return strdupcat(path2, "", ""); path1 = malloc(strlen(base) + strlen(path2) + 4); strcpy(path1, base); while (path2[0] == '.') { if (path2[1] == '/') { path2 += 2; for (; path2[0] == '/'; ++path2) {} continue; } else if (path2[1] == '.' && path2[2] == '/' && path1[0] != '\0') { /* TODO(pts): Also special-case path2 == "..". */ char *p, *path3; struct stat sta, stb; char c; size_t spath1; path2 += 3; for (; path2[0] == '/'; ++path2) {} do_path1_again: spath1 = strlen(path1); /* TODO(pts): Add loop limit for infinite symlinks. */ while ((path3 = readlink_slash_alloc(path1))) { char *path4; size_t spath3; /* TODO(pts): Handle ../ in the beginning of path3. */ spath3 = strlen(path3); if (path3[0] == '/') { spath1 = 0; /* Truncate on absolute symlink. */ } else { for (p = path1 + spath1; p != path1 && p[-1] == '/'; --p) {} for (; p != path1 && p[-1] != '/'; --p) {} spath1 = p - path1; *p = '\0'; } path4 = malloc(spath1 + spath3 + strlen(path2) + 5); memcpy(path4, path1, spath1); free(path1); path1 = path4; memcpy(p = path4 + spath1, path3, spath3); free(path3); p += spath3; if (spath3 != 0 && p[-1] != '/') *p++ = '/'; *p = '\0'; spath1 = p - path1; } strcpy(path1 + spath1, "../"); if (0 != lstat(path1, &sta)) { dotdot_dumb: strcpy(path1 + spath1 + 3, path2); return path1; } p = path1 + spath1; for (; p != path1 && p[-1] == '/'; --p) {} if (p == path1) goto dotdot_dumb; /* Absolute /. */ for (; p != path1 && p[-1] != '/'; --p) {} if (p[0] == '.' && p[1] == '/') { if (p == path1) goto dotdot_dumb; *p = '\0'; goto do_path1_again; } else if (p[0] == '.' && p[1] == '.' && p[2] == '/') { goto dotdot_dumb; } if (p == path1) { if (0 != lstat(".", &stb) || sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino) goto dotdot_dumb; } else { c = p[0]; p[0] = '\0'; // path1 now ends with '/', it's OK. if (0 != lstat(path1, &stb) || sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino) { p[0] = c; goto dotdot_dumb; } } } else { break; } } strcat(path1, path2); return path1; }
int main(int argc, char **argv) { char *prog; char *dir, *dirup; char *p; char **args, **argp; char is_verbose = 0; char **argi, *arg; struct stat st; dir = find_on_path(argv[0]); if (!dir) { fdprint(2, "xstatic: error: could not find myself on $PATH\n"); return 122; } dir = readlink_alloc_all(dir); for (p = dir + strlen(dir); p != dir && p[-1] != '/'; --p) {} if (p == dir) { strcpy(dir, "."); } else { for (; p != dir && p[-1] == '/'; --p) {} p[p == dir] = '\0'; /* Remove basename of dir. */ } dirup = get_up_dir_alloc(dir); if (detect_linker(argv)) { /* clang trampoline runs as as ld. */ char is_static = 0; char **argli; /* Where to add the -L flags. */ /* All we do is exec()ing ld.bin with the following dropped from argv: * * -L/usr/lib... * -L/lib... * --hash-style=both (because not supported by old ld) * --build-id (because not supported by old ld) * -z relro (because it increases the binary size and it's useless for static) */ check_ld_crtoarg(argv); argp = args = malloc(sizeof(*args) * (argc + 5)); *argp++ = *argv; for (argi = argv + 1; (arg = *argi); ++argi) { if (0 == strcmp(arg, "--do-xstaticldv")) { is_verbose = 1; } else if (0 == strcmp(arg, "-static")) { is_static = 1; } } if (!is_static) { fdprint(2, "xstatic: error: missing -static\n"); exit(1); } *argp++ = "-nostdlib"; /* No system directories to find .a files. */ /* Find argli: in front of the last -L (which will be removed), but no * later than just before the first -l. */ argli = NULL; for (argi = argv + 1; (arg = *argi); ++argi) { if (arg[0] == '-') { if (arg[1] == 'L') { /* "-L". */ argli = argi; } else if (arg[1] == 'l') { /* "-l". */ if (!argli) argli = argi; break; } } } if (!argli) argli = argv + 1; for (argi = argv + 1; (arg = *argi); ++argi) { if (argi == argli) { char **argj; /* Add the user-specified link dirs first (just like non-xstatic). */ for (argj = argv + 1; *argj; ++argj) { if (0 == strncmp(*argj, "-=L", 3)) { *argp++ = strdupcat("-L", "", *argj + 3); } } /* We put xstaticcld with libgcc.a first, because clang puts * /usr/lib/gcc/i486-linux-gnu/4.4 with libgcc.a before /usr/lib with * libc.a . * * We add these -L flags even if compiler -nostdlib was specified, * because non-xstatic compilers do the same. */ *argp++ = strdupcat("-L", dirup, "/xstaticcld"); *argp++ = strdupcat("-L", dirup, "/xstaticusr/lib"); argli = NULL; /* Don't insert the -L flags again. */ } if (0 == strcmp(arg, "-z") && argi[1] && 0 == strcmp(argi[1], "relro")) { /* Would increase size. */ ++argi; /* Omit both arguments: -z relro */ } else if (arg[0] == '-' && arg[1] == 'L') { /* "-L". */ if (arg[2] == '\0' && argi[1]) ++argi; /* Omit -L... containing the system link dirs, the user-specified link * dir flags were passed as -=L... */ } else if (0 == strncmp(arg, "-=L", 3)) { /* Omit -=L here, gets added at position argli. */ } else if ( 0 == strcmp(arg, "-nostdlib") || 0 == strcmp(arg, "--do-xstaticcld") || 0 == strcmp(arg, "--do-xstaticldv") || 0 == strncmp(arg, "--hash-style=", 13) || /* Would increase size. */ 0 == strcmp(arg, "--build-id") || 0 == strncmp(arg, "--sysroot=", 10)) { /* Omit this argument. */ } else { *argp++ = *argi; } } *argp = NULL; prog = strdupcat(argv[0], ".bin", ""); /* "ld.bin". */ args[0] = prog; if (is_verbose) { fdprint(2, escape_argv("xstatic-ld: info: running ld:\n", args, "\n")); } execv(prog, args); fdprint(2, strdupcat("xstatic-ld: error: exec failed: ", prog, "\n")); return 120; } if (!argv[1] || 0 == strcmp(argv[1], "--help")) { fdprint(1, strdupcat( "Usage: ", argv[0], " <gcc|g++|clang|clang++> [<compiler-arg>...]\n" "Invokes the C/C++ compiler with -static against the xstatic uClibc.\n" )); return 1; } if (argv[1][0] == '-') { fdprint(2, "xstatic: error: please specify gcc|clang prog in 1st arg\n"); return 1; } check_bflags(argv); check_xflags(argv); p = strdupcat(dirup, "/xstaticcld/ld.bin", ""); if (0 != stat(p, &st) || !S_ISREG(st.st_mode)) { file_missing: fdprint(2, strdupcat( "xstatic: error: file missing, please install: ", p, "\n")); return 123; } p = strdupcat(dirup, "/xstaticusr/lib/libc.a", ""); if (0 != stat(p, &st) || !S_ISREG(st.st_mode)) goto file_missing; p = strdupcat(dirup, "/xstaticusr/include/stdio.h", ""); if (0 != stat(p, &st) || !S_ISREG(st.st_mode)) goto file_missing; p = strdupcat(dirup, "/xstaticusr/lib/crt1.o", ""); if (0 != stat(p, &st) || !S_ISREG(st.st_mode)) goto file_missing; p = strdupcat(dirup, "/xstaticcld/crtbeginT.o", ""); if (0 != stat(p, &st) || !S_ISREG(st.st_mode)) goto file_missing; prog = find_on_path(argv[1]); if (!prog) { fdprint(2, strdupcat( "xstatic: error: compiler not found: ", argv[1], "\n")); return 119; } argv[1] = argv[0]; ++argv; --argc; argp = args = malloc(sizeof(*args) * (argc + 20)); *argp++ = prog; /* Set destination argv[0]. */ /* No need to check for -m64, check_bflags has already done that. */ if (!argv[1] || (!argv[2] && (0 == strcmp(argv[1], "-v") || 0 == strcmp(argv[1], "-m32"))) || (argv[2] && !argv[3] && 0 == strcmp(argv[1], "-m32") && 0 == strcmp(argv[2], "-v"))) { /* This changes the `Target: ...' of Clang to i386, but of GCC. */ if (!argv[1] || (!argv[2] && 0 != strcmp(argv[1], "-m32"))) { *argp++ = "-m32"; } /* Don't add any flags, because the user wants some version info, and with * `-Wl,... -v' gcc and clang won't display version info. */ memcpy(argp, argv + 1, argc * sizeof(*argp)); } else { char need_linker; char has_nostdinc, has_nostdincxx; lang_t lang; need_linker = detect_need_linker(argv); detect_nostdinc(argv, &has_nostdinc, &has_nostdincxx); detect_lang(prog, argv, &lang); /* When adding more arguments here, increase the args malloc count. */ /* We don't need get_autodetect_archflag(argv), we always send "-m32". */ *argp++ = "-m32"; *argp++ = "-static"; /* The linker would be ../xstaticcld/ld, which is also a trampoline binary * of ours. */ *argp++ = strdupcat("-B", dirup, "/xstaticcld"); /* This puts xstaticusr/include to the top of the include path, and keeps * the gcc or clang headers below that. Specifying --nostdinc (when * lang.is_compiling) would remove these compiler-specific headers (e.g. * stdarg.h), which we don't want removed, because libc headers depend * on them. * * TODO(pts): Make /usr/local/bin/clang also work. */ *argp++ = strdupcat("--sysroot=", dirup, lang.is_clang && is_dirprefix(prog, "/usr/bin") ? "/xstaticclangempty" : "/xstaticempty"); if (lang.is_compiling) { /* * Without this we get the following error compiling binutils 2.20.1: * chew.c:(.text+0x233f): undefined reference to `__stack_chk_fail' * We can't implement this in a compatible way, glibc gcc generates %gs:20, * uClibc-0.9.33 has symbol __stack_chk_guard. */ *argp++ = "-fno-stack-protector"; if (lang.is_cxx) { /* Glitch: It's not possible to disable the gcc warning ``cc1: warning: * command line option "-nostdinc++" is valid for C++/ObjC++ but not * for C'', there is no such clang warning. Example invocation (with * both .c and .cc files): ``xstatic gcc -c -W -Wall -O2 co.c * hello.cc''. */ *argp++ = "-nostdinc++"; if (!has_nostdincxx) { /* Clang has -cxx-isystem, which is a no-op when compiling C code, * but it adds the directory after the regular -isystem. But we need * the C++ headers in front of the C headers (because of conflicting * files, e.g. complex.h, tgmath.h, fenv.h). * * So we don't use -cxx-isystem, but we use -isystem uniformly for * Clang and GCC. * * A small glitch: if there are both C and C++ source files * specified (because C source files mustn't have C++ headers on * their include path), and a non-C++ compiler is used (e.g. * ``xstatic gcc -c -W -Wall -O2 helloco.c hello.cc''), then * `#include <vector>' in the .c file would find the C++ vector * header, and fail with a confusing error message when parsing it. */ *argp++ = "-isystem"; *argp++ = strdupcat(dirup, "/xstaticusr/c++include", ""); /* Regular g++ libstdc++ has non-backward and then backward. */ *argp++ = "-isystem"; *argp++ = strdupcat(dirup, "/xstaticusr/c++include/backward", ""); } } if (!has_nostdinc) { *argp++ = "-isystem"; *argp++ = strdupcat(dirup, "/xstaticusr/include", ""); } } for (argi = argv + 1; (arg = *argi); ++argi) { if (0 == strcmp(arg, "-v")) { is_verbose = 1; *argp++ = arg; } else if (0 == strcmp(arg, "-static") || 0 == strcmp(arg, "-xstatic") || 0 == strcmp(arg, "--xstatic") || 0 == strcmp(arg, "-nostdinc") || 0 == strcmp(arg, "-nostdinc++") || 0 == strcmp(arg, "-m32")) { } else if (arg[0] == '-' && arg[1] == 'L') { /* "-L". */ /* Convert -L to -=L in the linker command-line on which our ld wrapper * code can trigger. */ if (arg[2] == '\0' && argi[1]) { *argp++ = strdupcat("-Wl,-=L", "", *++argi); } else { *argp++ = strdupcat("-Wl,-=L", "", arg + 2); } } else { *argp++ = arg; } } if (need_linker) { *argp++ = "-Wl,--do-xstaticcld"; if (is_verbose) *argp++ = "-Wl,--do-xstaticldv"; } *argp++ = NULL; } if (is_verbose) { fdprint(2, escape_argv("xstatic: info: running compiler:\n", args, "\n")); } execv(prog, args); fdprint(2, strdupcat("xstatic: error: exec failed: ", prog, "\n")); return 120; }
/* Return values: * 1 = continue reading packets * 0 = stop reading packets, cos we're done * -1 = stop reading packets, we've got an error */ static int per_packet(libtrace_packet_t *packet) { double ts = trace_get_seconds(packet); if (trace_get_link_type(packet) == ~0U) { fprintf(stderr, "Halted due to being unable to determine linktype - input trace may be corrupt.\n"); return -1; } if (snaplen>0) { trace_set_capture_length(packet,snaplen); } if (ts <starttime) { return 1; } if ( ts > endtime) { //printf( "%f\t%f\n", ts, endtime); return 0; } if (firsttime==0) { time_t now = trace_get_seconds(packet); if (starttime != 0.0) { firsttime=now-((now - (int)starttime) % interval); } else { firsttime=now; } } if (output && trace_get_seconds(packet)>firsttime+interval) { trace_destroy_output(output); output=NULL; firsttime+=interval; } if (output && pktcount%count==0) { trace_destroy_output(output); output=NULL; } pktcount++; totbytes+=trace_get_capture_length(packet); if (output && totbytes-totbyteslast>=bytes) { trace_destroy_output(output); output=NULL; totbyteslast=totbytes; } if (!output) { char *buffer; bool need_ext=false; if (maxfiles <= filescreated) { return 0; } buffer=strdup(output_base); if (interval!=UINT64_MAX && maxfiles>1) { buffer=strdupcat(buffer,"-"); buffer=strdupcati(buffer,(uint64_t)firsttime); need_ext=true; } if (count!=UINT64_MAX && maxfiles>1) { buffer=strdupcat(buffer,"-"); buffer=strdupcati(buffer,(uint64_t)pktcount); need_ext=true; } if (bytes!=UINT64_MAX && maxfiles>1) { static int filenum=0; buffer=strdupcat(buffer,"-"); buffer=strdupcati(buffer,(uint64_t)++filenum); need_ext=true; } if (need_ext) { if (compress_level!=0) buffer=strdupcat(buffer,".gz"); } if (verbose>1) { fprintf(stderr,"%s:",buffer); if (count!=UINT64_MAX) fprintf(stderr," count=%" PRIu64,pktcount); if (bytes!=UINT64_MAX) fprintf(stderr," bytes=%" PRIu64,bytes); if (interval!=UINT64_MAX) { time_t filetime = firsttime; fprintf(stderr," time=%s",ctime(&filetime)); } else { fprintf(stderr,"\n"); } } output=trace_create_output(buffer); if (trace_is_err_output(output)) { trace_perror_output(output,"%s",buffer); free(buffer); return -1; } if (compress_level!=-1) { if (trace_config_output(output, TRACE_OPTION_OUTPUT_COMPRESS, &compress_level)==-1) { trace_perror_output(output,"Unable to set compression level"); } } if (trace_config_output(output, TRACE_OPTION_OUTPUT_COMPRESSTYPE, &compress_type) == -1) { trace_perror_output(output, "Unable to set compression type"); } trace_start_output(output); if (trace_is_err_output(output)) { trace_perror_output(output,"%s",buffer); free(buffer); return -1; } free(buffer); filescreated ++; } /* Some traces we have are padded (usually with 0x00), so * lets sort that out now and truncate them properly */ if (trace_get_capture_length(packet) > trace_get_wire_length(packet)) { trace_set_capture_length(packet,trace_get_wire_length(packet)); } if (trace_write_packet(output,packet)==-1) { trace_perror_output(output,"write_packet"); return -1; } return 1; }
static char *strdupcati(char *str,uint64_t i) { char buffer[64]; snprintf(buffer,sizeof(buffer),"%" PRIu64,i); return strdupcat(str,buffer); }