/* * Copyright (c) 2015 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #define foreach_libtool_mode _ (compile) _ (link) _ (install) typedef enum { #define _(m) MODE_##m, foreach_libtool_mode #undef _ } lt_mode_t; typedef enum { LITERAL, OUTPUT_EXE, OUTPUT_LO, OUTPUT_LA, LT_LIB, NON_LT_LIB, IGNORE, } lt_edit_type_t; typedef struct { lt_edit_type_t type; u8 * data; } lt_edit_t; typedef struct { u8 * path; } lt_lib_path_t; typedef struct { lt_mode_t mode; int link_static; int silent; lt_edit_type_t output_edit_type; u8 * output_file; lt_edit_t * edits; lt_lib_path_t * lib_path; uword * rpath_hash; u8 * tag; } lt_main_t; static lt_lib_path_t * search_lib_path (lt_main_t * lm, char * fmt, ...) { va_list va; static u8 * file_name, * path_name; lt_lib_path_t * p = 0; if (file_name) _vec_len (file_name) = 0; va_start (va, fmt); file_name = va_format (file_name, fmt, &va); va_end (va); path_name = 0; vec_foreach (p, lm->lib_path) { struct stat st; if (path_name) _vec_len (path_name) = 0; path_name = format (path_name, "%s/%v%c", p->path, file_name, 0); if (stat ((char *) path_name, &st) >= 0) return p; } return 0; } static u8 * format_libtool_mode (u8 * s, va_list * args) { int m = va_arg (*args, int); char * t; switch (m) { #define _(f) case MODE_##f: t = #f; break; foreach_libtool_mode; #undef _ default: t = 0; } if (t) vec_add (s, t, strlen (t)); else s = format (s, "unknown 0x%x", m); return s; } static uword unformat_libtool_mode (unformat_input_t * input, va_list * args) { int * result = va_arg (*args, int *); #define _(m) if (unformat (input, #m)) { *result = MODE_##m; return 1; } foreach_libtool_mode; #undef _ return 0; } static uword unformat_basename (unformat_input_t * input, va_list * args) { u8 ** result = va_arg (*args, u8 **); u8 * suffix = va_arg (*args, u8 *); u8 * current_suffix = suffix; uword c; while ((c = unformat_get_input (input)) != UNFORMAT_END_OF_INPUT) { switch (c) { case 0: case ' ': case '\t': case '\n': case '\r': goto fail; } vec_add1 (*result, c); if (c == *current_suffix) current_suffix++; else current_suffix = suffix; if (*current_suffix == 0) { _vec_len (*result) -= current_suffix - suffix; return 1; } } fail: vec_free (*result); return 0; } static void edit (lt_main_t * lm, lt_edit_type_t type, char * fmt, ...) { va_list va; lt_edit_t * e; u8 * s; va_start (va, fmt); s = va_format (0, fmt, &va); va_end (va); vec_add2 (lm->edits, e, 1); e->type = type; e->data = s; } static u8 * format_argv (u8 * s, va_list * args) { u8 ** a = va_arg (*args, u8 **); uword i; for (i = 0; i < vec_len (a) - 1; i++) { if (i > 0) vec_add1 (s, ' '); vec_add (s, a[i], vec_len (a[i]) - 1); } return s; } static u8 * format_dirname (u8 * s, va_list * args) { u8 * f = va_arg (*args, u8 *); u8 * t; for (t = vec_end (f) - 1; t >= f; t--) { if (t[0] == '/') break; } if (t[0] == '/') vec_add (s, f, t - f); else vec_add1 (s, '.'); return s; } static u8 * format_basename (u8 * s, va_list * args) { u8 * f = va_arg (*args, u8 *); u8 * t; for (t = vec_end (f) - 1; t >= f; t--) { if (t[0] == '/') break; } if (t[0] == '/') vec_add (s, t + 1, vec_end (f) - (t + 1)); else vec_add (s, f, vec_len (f)); return s; } static void my_system (char * fmt, ...) { va_list va; u8 * s; va_start (va, fmt); s = va_format (0, fmt, &va); va_end (va); vec_add1 (s, 0); /* null terminate */ if (system ((char *) s) != 0) clib_error ("%s", s); vec_free (s); } static u8 * my_cmd (char * fmt, ...) { va_list va; u8 * s; FILE * result; int c; va_start (va, fmt); s = va_format (0, fmt, &va); va_end (va); vec_add1 (s, 0); /* null terminate */ result = popen ((char *) s, "r"); if (! result) clib_error ("%s", s); _vec_len (s) = 0; while ((c = fgetc (result)) != EOF) vec_add1 (s, c); pclose (result); return s; } static void make_file_with_contents (lt_main_t * lm, u8 * contents, char * fmt, ...) { va_list va; u8 * s; FILE * f; va_start (va, fmt); s = va_format (0, fmt, &va); va_end (va); vec_add1 (s, 0); /* null terminate */ f = fopen ((char *) s, "w"); if (! f) clib_error ("fopen %s", s); if (1 != fwrite (contents, vec_len (contents), 1, f)) clib_error ("fwrite"); fclose (f); } static u8 ** add_argv (u8 ** argv, char * fmt, ...) { va_list va; u8 * s; va_start (va, fmt); s = va_format (0, fmt, &va); va_end (va); vec_add1 (s, 0); /* null terminate */ vec_add1 (argv, s); return argv; } #define GEN_ARGV_PIC (1 << 0) #define GEN_ARGV_PUNT (1 << 1) static u8 ** gen_argv (lt_main_t * lm, uword flags) { u8 ** r = 0; uword * path_used_bitmap = 0; lt_edit_t * e; int is_punt; is_punt = (flags & GEN_ARGV_PUNT) != 0; if (is_punt) { /* No supported so punt back to shell based libtool. */ r = add_argv (r, "/bin/sh"); r = add_argv (r, "./libtool"); r = add_argv (r, "--mode=%U", format_libtool_mode, lm->mode); } if (lm->mode == MODE_compile) ASSERT (lm->output_edit_type != OUTPUT_LA); vec_foreach (e, lm->edits) { switch (e->type) { case LITERAL: r = add_argv (r, "%v", e->data); break; case OUTPUT_EXE: if (! is_punt) my_system ("mkdir -p %U/.libs", format_dirname, e->data); r = add_argv (r, "-o"); r = add_argv (r, "%s%v", is_punt ? "" : ".libs/", e->data); break; case OUTPUT_LO: if (flags & GEN_ARGV_PIC) { r = add_argv (r, "-fPIC"); r = add_argv (r, "-DPIC"); } r = add_argv (r, "-o"); if (is_punt) r = add_argv (r, "-o %v.lo", e->data); else if (flags & GEN_ARGV_PIC) { my_system ("mkdir -p %U/.libs", format_dirname, e->data); r = add_argv (r, "%U/.libs/%U.o", format_dirname, e->data, format_basename, e->data); } else { my_system ("mkdir -p %U", format_dirname, e->data); r = add_argv (r, "%v.o", e->data); } break; case OUTPUT_LA: if (is_punt) r = add_argv (r, "-o %v.la", e->data); else abort (); break; case LT_LIB: if (is_punt) r = add_argv (r, "%v.la", e->data); else if (lm->mode == MODE_link) { u8 * pwd = get_current_dir_name (); u8 * libdir = my_cmd (". %s/%v.la && echo -n ${libdir}", pwd, e->data); if (! hash_get_mem (lm->rpath_hash, libdir)) { r = add_argv (r, "-Wl,-rpath"); r = add_argv (r, "-Wl,%v", libdir); hash_set_mem (lm->rpath_hash, libdir, 0); } r = add_argv (r, "%U/.libs/%U.so", format_dirname, e->data, format_basename, e->data); } else r = add_argv (r, "%v.la", e->data); break; case NON_LT_LIB: if (lm->mode == MODE_link && ! is_punt) { lt_lib_path_t * p = search_lib_path (lm, "lib%v.so", e->data); if (p) { path_used_bitmap = clib_bitmap_ori (path_used_bitmap, p - lm->lib_path); r = add_argv (r, "%s/lib%v.so", p->path, e->data); } else r = add_argv (r, "-l%v", e->data); } else r = add_argv (r, "-l%v", e->data); break; default: ASSERT (0); } } { uword i; clib_bitmap_foreach (i, path_used_bitmap, ({ lt_lib_path_t * p = vec_elt_at_index (lm->lib_path, i); r = add_argv (r, "-Wl,-rpath"); r = add_argv (r, "-Wl,%s", p->path); })); clib_bitmap_free (path_used_bitmap); } vec_add1 (r, 0); return r; } static void do_command (lt_main_t * lm, u8 ** argv) { u8 * cmd = format (0, "%U%c", format_argv, argv, 0); if (! lm->silent) fformat (stderr, "lt: %s\n", cmd); if (system ((char *) cmd)) exit (1); vec_free (cmd); } static int lt_main (unformat_input_t * input) { lt_main_t _lm = {0}, * lm = &_lm; clib_error_t * error = 0; u8 * s; lm->rpath_hash = hash_create_vec (0, sizeof (u8), sizeof (uword)); while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { s = 0; if (s) _vec_len (s) = 0; if (unformat (input, "-o %s", &s)) { u8 * dot = vec_end (s) - 4; int is_la = 0, is_lo = 0; is_lo = vec_len (s) >= 4 && ! strcmp ((char *) dot, ".lo"); is_la = vec_len (s) >= 4 && ! strcmp ((char *) dot, ".la"); if (is_lo || is_la) { dot[0] = 0; lm->output_edit_type = is_lo ? OUTPUT_LO : OUTPUT_LA; } else lm->output_edit_type = OUTPUT_EXE; edit (lm, lm->output_edit_type, "%s", s); lm->output_file = format (0, "%s", s); } else if (unformat (input, "-L%s", &s)) { lt_lib_path_t * p; vec_add2 (lm->lib_path, p, 1); p->path = s; edit (lm, LITERAL, "-L%s", s); } else if (unformat (input, "%U", unformat_basename, &s, ".la")) edit (lm, LT_LIB, "%v", s); else if (unformat (input, "-l%s", &s)) edit (lm, NON_LT_LIB, "%s", s); else if (unformat (input, "--mode=%U", unformat_libtool_mode, &lm->mode)) ; else if (unformat (input, "--tag=%s", &lm->tag)) ; else if (unformat (input, "-static")) { lm->link_static = 1; edit (lm, LITERAL, "%s", "-static"); } else if (unformat (input, "%s", &s)) edit (lm, LITERAL, "%s", s); else { error = clib_error_create ("parse error `%U'", format_unformat_error, input); goto done; } } { u8 ** argv; if (! (lm->mode == MODE_compile || (lm->mode == MODE_link && lm->output_edit_type == OUTPUT_EXE && ! lm->link_static))) { argv = gen_argv (lm, GEN_ARGV_PUNT); do_command (lm, argv); } else if (lm->mode == MODE_compile) { argv = gen_argv (lm, GEN_ARGV_PIC); do_command (lm, argv); argv = gen_argv (lm, 0); do_command (lm, argv); } else { argv = gen_argv (lm, 0); do_command (lm, argv); } if (lm->mode == MODE_compile) { u8 * s = 0; u8 * f = lm->output_file; /* Need this or .lo files are rejected. */ s = format (s, "# Generated by libtool (Eliot lt 0.0)\n"); s = format (s, "pic_object='.libs/%U.o'\n", format_basename, f); s = format (s, "non_pic_object='%U.o'\n", format_basename, f); make_file_with_contents (lm, s, "%v.lo", f); vec_free (s); } else if (lm->mode == MODE_link) { u8 * s = 0; u8 * f = lm->output_file; s = format (s, "%s", "# Generated by libtool (Eliot lt) 2.4\n" "# %%%MAGIC variable%%%\n" "generated_by_libtool_version=2.4\n"); make_file_with_contents (lm, s, "%v", f); vec_free (s); } { int status; while (1) { if (waitpid (-1, &status, 0) < 0 && errno == ECHILD) break; } exit (0); } } done: if (s) vec_free (s); if (error) { clib_error_report (error); return 1; } return 0; } int main (int argc, char * argv[]) { unformat_input_t i; unformat_init_command_line (&i, argv); exit (lt_main (&i)); return 0; }