/* $NetBSD: fixup-libtool.c,v 1.8 2021/11/07 12:38:12 christos Exp $ */ /*- * Copyright (c) 2009 Joerg Sonnenberger . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include "common.h" #define LIBPATH_HASH 256 struct unwrap_rule { TAILQ_ENTRY(unwrap_rule) link; char *src; char *dst; size_t src_len; }; TAILQ_HEAD(unwrap_ruleset, unwrap_rule); static struct unwrap_ruleset unwrap_rules = TAILQ_HEAD_INITIALIZER(unwrap_rules); void register_unwrap(const char *rule) { const char *sep; struct unwrap_rule *r; sep = strchr(rule, ':'); if (sep == NULL) goto failure; if (strchr(sep + 1, ':') != NULL) goto failure; r = xmalloc(sizeof(*r)); r->src = xstrndup(rule, sep - rule); if (sep[1]) r->dst = xstrdup(sep + 1); else r->dst = NULL; r->src_len = sep - rule; TAILQ_INSERT_TAIL(&unwrap_rules, r, link); return; failure: errx(255, "Invalid transform rule: %s", rule); } struct processing_option { FILE *output; const char *lafile; struct arglist hashtab[LIBPATH_HASH]; int first; int in_lai; int in_relink; const char *last_opt; size_t last_len; }; static void process_option(struct processing_option *opt, const char *line, size_t len, int in_relink) { struct unwrap_rule *r; struct argument *arg; char *tmp; const char *eol; size_t i, wlen; if (opt->in_lai && opt->last_opt && opt->last_len == len && strncmp(opt->last_opt, line, len) == 0) return; if (len >= 11 && strncmp(line, "-Wl,-rpath,", 11) == 0) { if (in_relink) goto print_option; return; } if (len >= 15 && strncmp(line, "-Wl,-rpath-link,", 15) == 0) { if (in_relink) goto print_option; return; } if (len > 2 && strncmp(line, "-D", 2) == 0) return; /* No preprocessor options */ if (len > 2 && strncmp(line, "-I", 2) == 0) return; /* No preprocessor options */ if (len >= 2 && strncmp(line, "-L", 2) == 0) { if (opt->in_lai) { TAILQ_FOREACH(r, &unwrap_rules, link) { if (len - 2 < r->src_len) continue; if (strncmp(line + 2, r->src, r->src_len)) continue; if (!isabs(line[r->src_len + 2]) && len != r->src_len + 2) continue; line += r->src_len + 2; len -= r->src_len + 2; tmp = xasprintf("-L%s%*.*s", r->dst, (int)len, (int)len, line); process_option(opt, tmp, strlen(tmp), in_relink); free(tmp); return; } if (wrksrc) { wlen = strlen(wrksrc); if (wlen <= len - 2 && strncmp(line + 2, wrksrc, wlen) == 0 && (isabs(line[wlen + 2]) || wlen + 2 == len)) return; } } i = wrapper_hash2(line, len) & (LIBPATH_HASH - 1); TAILQ_FOREACH(arg, opt->hashtab + i, link) { if (strncmp(arg->val, line, len) == 0 && arg->val[len] == '\0') break; } if (arg != NULL) return; arg = argument_new(xstrndup(line, len)); TAILQ_INSERT_TAIL(opt->hashtab + i, arg, link); goto print_option; } opt->last_opt = line; opt->last_len = len; if (len < 3 || strncmp(line + len - 3, ".la", 3)) goto print_option; for (eol = line + len - 3; eol > line; --eol) { if (isabs(*eol)) break; } if (opt->in_lai && isabs(*eol)) { TAILQ_FOREACH(r, &unwrap_rules, link) { if (eol < line + r->src_len) continue; if (strncmp(line, r->src, r->src_len)) continue; if (!isabs(*eol)) continue; line += r->src_len; len -= r->src_len; if (strncmp(eol + 1, "lib", 3) == 0) { tmp = xasprintf("-L%s%*.*s", r->dst, (int)(eol - line), (int)(eol - line), line); process_option(opt, tmp, strlen(tmp), in_relink); free(tmp); eol += 4; len = line + len - eol - 3; fprintf(opt->output, " -l%*.*s", (int)len, (int)len, eol); } else { tmp = xasprintf("%s%*.*s", r->dst, (int)len, (int)len, line); process_option(opt, tmp, strlen(tmp), in_relink); free(tmp); } return; } } if (!opt->in_relink) goto print_option; if (opt->lafile && strncmp(opt->lafile, line, len) == 0 && len == strlen(opt->lafile)) goto print_option; if (!isabs(*line) && line == eol) { process_option(opt, "-L./.libs", 9, in_relink); goto print_option; } if (!isabs(*line)) { tmp = xasprintf("-L%*.*s/.libs", (int)(eol - line), (int)(eol - line), line); process_option(opt, tmp, strlen(tmp), in_relink); free(tmp); goto print_option; } if (wrksrc == NULL) goto print_option; wlen = strlen(wrksrc); if (wlen > len || (wlen != len && !isabs(line[wlen])) || strncmp(wrksrc, line, wlen)) goto print_option; tmp = xasprintf("-L%*.*s/.libs", (int)(eol - line), (int)(eol - line), line); process_option(opt, tmp, strlen(tmp), in_relink); free(tmp); print_option: if (opt->first) opt->first = 0; else putc(' ', opt->output); fwrite(line, len, 1, opt->output); } static void process_variable(FILE *output, const char *lafile, const char *line, int in_lai, int in_relink) { struct processing_option opt; size_t len, len2; struct argument *arg; size_t i; const char *command; for (i = 0; i < LIBPATH_HASH; ++i) TAILQ_INIT(opt.hashtab + i); opt.output = output; opt.lafile = lafile; opt.in_lai = in_lai; opt.in_relink = in_relink; opt.last_opt = NULL; opt.last_len = 0; if (in_relink) { command = strchr(line, ';'); if (command == NULL) errx(255, "Unrecognizable relink format"); ++command; fwrite(line, command - line, 1, output); fprintf(output, " %s", exec_path); /* XXX document this logic */ line = command + 1; len = strspn(line, " \t"); line += len; len2 = len = strcspn(line, " \t"); command = line; if (len == 0) errx(255, "Unrecognizable relink format"); line += len; len = strspn(line, " \t"); line += len; if (len2 != 7 || strncmp(command, "libtool", 7)) { len = strcspn(line, " \t"); if (len == 0) errx(255, "Unrecognizable relink format"); line += len; len = strspn(line, " \t"); line += len; } } for (opt.first = !in_relink; *line ; line += len) { len = strspn(line, " \t"); line += len; len = strcspn(line, " \t"); if (len == 0) break; process_option(&opt, line, len, in_relink); } for (i = 0; i < LIBPATH_HASH; ++i) { while ((arg = TAILQ_FIRST(opt.hashtab + i)) != NULL) argument_unlink(opt.hashtab + i, &arg); } } static void fixup_libtool_la(const char *lafile, int in_lai) { static const char dep_lib[] = "dependency_libs='"; static const char relink_cmd[] = "relink_command=\""; static const char relink_marker_cmd[] = "# buildlink modification\n"; struct stat st; FILE *fp, *output; char *line, *opt_start, *tmp_name; const char *pass_lafile, *cur_option; int in_relink, ignore_relink = 0; char delimiter; size_t len; ssize_t cur; fp = fopen(lafile, "r"); if (fp == NULL) return; if (fstat(fileno(fp), &st)) err(255, "fstat('%s') failed", lafile); if ((st.st_mode & S_IFMT) != S_IFREG) { fclose(fp); return; } tmp_name = concat(lafile, ".tmp"); output = fopen(tmp_name, "w"); if (output == NULL) err(255, "fopen('%s') failed", tmp_name); line = NULL; len = 0; while ((cur = getline(&line, &len, fp)) > 0) { if (strcmp(line, relink_marker_cmd) == 0) { fwrite(line, 1, cur, output); ignore_relink = 1; continue; } if (strncmp(line, relink_cmd, sizeof(relink_cmd) - 1) == 0) { if (ignore_relink) { fwrite(line, 1, cur, output); ignore_relink = 0; continue; } cur_option = relink_cmd; opt_start = line + sizeof(relink_cmd) - 1; pass_lafile = lafile; delimiter='\"'; in_relink = 1; ignore_relink = 0; } else if (strncmp(line, dep_lib, sizeof(dep_lib) - 1) == 0) { cur_option = dep_lib; opt_start = line + sizeof(dep_lib) - 1; pass_lafile = NULL; delimiter='\''; in_relink = 0; ignore_relink = 0; } else { fwrite(line, 1, cur, output); ignore_relink = 0; continue; } if (line[cur - 2] != delimiter || line[cur - 1] != '\n') errx(255, "corrupted libtool archive '%s'", lafile); if (debug) { fputs("# buildlink modification, original:\n# ", output); fwrite(line, 1, cur, output); } else { fputs(relink_marker_cmd, output); } fputs(cur_option, output); line[cur - 2] = '\0'; process_variable(output, pass_lafile, opt_start, in_lai, in_relink); fprintf(output, "%c\n", delimiter); } free(line); if (ferror(fp) || fclose(fp) || ferror(output) || fclose(output) || rename(tmp_name, lafile)) { unlink(tmp_name); err(255, "output processing failed"); } } void fixup_libtool(struct arglist *args) { struct argument *arg, *arg2; const char *slash; char *laifile; size_t len; TAILQ_FOREACH(arg, args, link) { if (strcmp(arg->val, "-o")) continue; arg2 = TAILQ_NEXT(arg, link); if (arg2 == NULL || arg2->val[0] == '-') errx(255, "Missing argument for option -o"); arg = arg2; len = strlen(arg->val); if (len < 3) continue; if (strcmp(arg->val + len - 3, ".la") == 0) break; } if (arg == NULL) return; fixup_libtool_la(arg->val, 0); slash = strrchr(arg->val, '/'); if (slash == NULL) laifile = xasprintf("./.libs/%si", arg->val); else laifile = xasprintf("%*.*s/.libs/%si", (int)(slash - arg->val), (int)(slash - arg->val), arg->val, slash + 1); fixup_libtool_la(laifile, 1); free(laifile); }