From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by finch.gentoo.org (Postfix) with ESMTPS id 7EB47138806 for ; Mon, 25 Dec 2017 19:47:29 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 8B8B5E0784; Mon, 25 Dec 2017 19:47:27 +0000 (UTC) Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id 4EFBEE0784 for ; Mon, 25 Dec 2017 19:47:25 +0000 (UTC) Received: from oystercatcher.gentoo.org (oystercatcher.gentoo.org [148.251.78.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id C567F33BEF8 for ; Mon, 25 Dec 2017 19:47:24 +0000 (UTC) Received: from localhost.localdomain (localhost [IPv6:::1]) by oystercatcher.gentoo.org (Postfix) with ESMTP id 55EF8A1A0 for ; Mon, 25 Dec 2017 19:47:23 +0000 (UTC) From: "Fabian Groffen" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "Fabian Groffen" Message-ID: <1514126433.554171c6a3d43f87ac771e45be2b5136b18fd2af.grobian@gentoo> Subject: [gentoo-commits] repo/proj/prefix:master commit in: sys-devel/binutils-config/files/ X-VCS-Repository: repo/proj/prefix X-VCS-Files: sys-devel/binutils-config/files/ldwrapper.c X-VCS-Directories: sys-devel/binutils-config/files/ X-VCS-Committer: grobian X-VCS-Committer-Name: Fabian Groffen X-VCS-Revision: 554171c6a3d43f87ac771e45be2b5136b18fd2af X-VCS-Branch: master Date: Mon, 25 Dec 2017 19:47:23 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Archives-Salt: cdeb63a2-7a1a-47af-b0e6-2c5cbd38fede X-Archives-Hash: 4b015a91043e40a3edd845c47ecede75 commit: 554171c6a3d43f87ac771e45be2b5136b18fd2af Author: Michael Weiser weiser dinsnail net> AuthorDate: Sun Dec 24 14:40:33 2017 +0000 Commit: Fabian Groffen gentoo org> CommitDate: Sun Dec 24 14:40:33 2017 +0000 URL: https://gitweb.gentoo.org/repo/proj/prefix.git/commit/?id=554171c6 sys-devel/binutils-config: add cross-support to ldwrapper I've extended ldwrapper: - to detect a cross-ld at runtime from the argv[0] it was called with, - always call ld with the full path in argv[0] so it can find its ldscript directory relative to that - incidentally fix a bug where finding ld via binutils-config could never have worked since previous tries to find it via PATH had reduced PATH to its first component where binutils-config would not usually be sys-devel/binutils-config/files/ldwrapper.c | 392 +++++++++++++++++++--------- 1 file changed, 274 insertions(+), 118 deletions(-) diff --git a/sys-devel/binutils-config/files/ldwrapper.c b/sys-devel/binutils-config/files/ldwrapper.c index 80831a7142..81b78adf20 100644 --- a/sys-devel/binutils-config/files/ldwrapper.c +++ b/sys-devel/binutils-config/files/ldwrapper.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include /** * ldwrapper: Prefix helper to inject -L and -R flags to the invocation @@ -38,9 +40,20 @@ # error CHOST must be defined! #endif +static inline int is_cross(const char *ctarget) { + return strcmp(ctarget, CHOST); +} + +static inline int is_darwin(const char *ctarget) { + return (strstr(ctarget, "-darwin") != NULL); +} -static inline void -find_real_ld(char **ld, char verbose, char *wrapper) +static inline int is_aix(const char *ctarget) { + return (strstr(ctarget, "-aix") != NULL); +} + +static inline char * +find_real_ld(const char verbose, const char *wrapper, const char *ctarget) { FILE *f = NULL; char *ldoveride; @@ -48,9 +61,9 @@ find_real_ld(char **ld, char verbose, char *wrapper) #define ESIZ 1024 /* POSIX_MAX_PATH */ char *ret; struct stat lde; - - /* we may not succeed finding the linker */ - *ld = NULL; + char *config; + const char *config_prefix; + size_t configlen; /* respect the override in environment */ ldoveride = getenv("BINUTILS_CONFIG_LD"); @@ -58,92 +71,164 @@ find_real_ld(char **ld, char verbose, char *wrapper) if (verbose) fprintf(stdout, "%s: using BINUTILS_CONFIG_LD=%s " "from environment\n", wrapper, ldoveride); - *ld = ldoveride; - return; + return ldoveride; } if (verbose) fprintf(stdout, "%s: BINUTILS_CONFIG_LD not found in environment\n", wrapper); - + ret = malloc(sizeof(char) * ESIZ); - if (ret == NULL) - return; - - /* find ld in PATH, allowing easy PATH overrides */ - path = getenv("PATH"); - while (path > (char*)1 && *path != '\0') { - char *q = strchr(path, ':'); - if (q) - *q = '\0'; - if (strstr(path, "/" CHOST "/binutils-bin/") != NULL) { - snprintf(ret, ESIZ, "%s/%s", path, wrapper); - if (stat(ret, &lde) == 0) - *ld = ret; + if (ret == NULL) { + fprintf(stderr, "%s: out of memory allocating string for path to ld\n", + wrapper); + exit(1); + } + + /* find ld in PATH, allowing easy PATH overrides. strdup it because + * modifying it would otherwise corrupt the actual PATH environment + * variable which we might need to be intact later on to call + * binutils-config via popen. */ + path = strdup(getenv("PATH")); + if (path != NULL && *path != '\0') { + char *p; + char *q; + char *match; + const char *match_anchor = "/binutils-bin/"; + size_t matchlen = 1 + strlen(ctarget) + + strlen(match_anchor) + 1; + + match = malloc(sizeof(char) * matchlen); + if (match == NULL) { + fprintf(stderr, "%s: out of memory allocating " + "buffer for path matching\n", + wrapper); + exit(1); + } + + /* construct /CTARGET/binutils-bin/ for matchin against PATH */ + snprintf(match, matchlen, "/%s%s", ctarget, match_anchor); + + for (p = path; (q = strchr(p, ':')) != NULL; p = q + 1) { + if (q) + *q = '\0'; + if (strstr(p, match) != NULL) { + snprintf(ret, ESIZ, "%s/%s", p, wrapper); + if (stat(ret, &lde) == 0) { + free(match); + return ret; + } + } + if (!q) + break; } - if (q) - *q = ':'; /* restore PATH value */ - if (*ld) - return; - path = q + 1; + + free(match); } if (verbose) fprintf(stdout, "%s: linker not found in PATH\n", wrapper); - /* parse EPREFIX/etc/env.d/binutils/config-CHOST to get CURRENT, then - * consider $EPREFIX/usr/CHOST/binutils-bin/CURRENT where we should + /* parse EPREFIX/etc/env.d/binutils/config-CTARGET to get CURRENT, then + * consider $EPREFIX/usr/CTARGET/binutils-bin/CURRENT where we should * be able to find ld */ - ret[0] = '\0'; - if ((f = fopen(EPREFIX "/etc/env.d/binutils/config-" CHOST, "r")) != NULL) { + config_prefix = EPREFIX "/etc/env.d/binutils/config-"; + configlen = strlen(config_prefix) + strlen(ctarget) + 1; + config = malloc(sizeof(char) * configlen); + if (config == NULL) { + fprintf(stderr, "%s: out of memory allocating " + "buffer for configuration file name\n", + wrapper); + exit(1); + } + + snprintf(config, configlen, "%s%s", config_prefix, ctarget); + if ((f = fopen(config, "r")) != NULL) { char p[ESIZ]; + char *q; while (fgets(p, ESIZ, f) != NULL) { - if (strncmp(p, "CURRENT=", strlen("CURRENT=")) == 0) { - char *q = p + strlen(p); - /* strip trailing whitespace (fgets at least includes - * the \n) */ - for (q--; isspace(*q); q--) - *q = '\0'; - ; + if (strncmp(p, "CURRENT=", strlen("CURRENT=")) != 0) + continue; + + q = p + strlen(p); + /* strip trailing whitespace (fgets at least includes + * the \n) */ + for (q--; isspace(*q); q--) + *q = '\0'; + + q = p + strlen("CURRENT="); + if (is_cross(ctarget)) { + snprintf(ret, ESIZ, EPREFIX "/usr/" CHOST "/%s/binutils-bin/%s/%s", + ctarget, q, wrapper); + } else { snprintf(ret, ESIZ, EPREFIX "/usr/" CHOST "/binutils-bin/%s/%s", - p + strlen("CURRENT="), wrapper); - break; + q, wrapper); } + break; } fclose(f); if (stat(ret, &lde) == 0) { - *ld = ret; - return; + free(config); + return ret; } } if (verbose) - fprintf(stdout, "%s: linker not found via " EPREFIX - "/etc/env.d/binutils/config-" CHOST " (ld=%s)\n", - wrapper, ret); - + fprintf(stdout, "%s: linker not found via %s\n", wrapper, config); + free(config); + /* last try, call binutils-config to tell us what the linker is * supposed to be */ - ret[0] = '\0'; - if ((f = popen("binutils-config -c", "r")) != NULL) { + config_prefix = "binutils-config -c "; + configlen = strlen(config_prefix) + strlen(ctarget) + 1; + config = malloc(sizeof(char) * configlen); + if (config == NULL) { + fprintf(stderr, "%s: out of memory allocating " + "buffer for binutils-config command\n", + wrapper); + exit(1); + } + + snprintf(config, configlen, "%s%s", config_prefix, ctarget); + if ((f = popen(config, "r")) != NULL) { char p[ESIZ]; - char *q; - if (fgets(p, ESIZ, f) != NULL) { - q = p; - if (strncmp(q, CHOST "-", strlen(CHOST "-")) == 0) - q += strlen(CHOST "-"); - snprintf(ret, ESIZ, EPREFIX "/usr/" CHOST "/binutils-bin/%s/%s", - q, wrapper); - } else { - *p = '\0'; - } + char *q = fgets(p, ESIZ, f); fclose(f); - if (*p && stat(ret, &lde) == 0) { - *ld = ret; - return; + if (q != NULL) { + size_t ctargetlen = strlen(ctarget); + + /* binutils-config should report CTARGET- */ + if (strncmp(p, ctarget, ctargetlen) == 0 && + strlen(p) > ctargetlen && + p[ctargetlen] == '-') { + /* strip trailing whitespace (fgets at least includes + * the \n) */ + q = p + strlen(p); + for (q--; isspace(*q); q--) + *q = '\0'; + + q = p + ctargetlen + 1; + if (is_cross(ctarget)) { + snprintf(ret, ESIZ, EPREFIX "/usr/" CHOST + "/%s/binutils-bin/%s/%s", + ctarget, q, wrapper); + } else { + snprintf(ret, ESIZ, EPREFIX "/usr/" CHOST + "/binutils-bin/%s/%s", + q, wrapper); + } + + if (stat(ret, &lde) == 0) { + free(config); + return ret; + } + } } } if (verbose) - fprintf(stdout, "%s: linker not found via binutils-config -c (ld=%s)\n", - wrapper, ret); - free(ret); + fprintf(stdout, "%s: linker not found via %s\n", + wrapper, config); + free(config); + + /* we didn't succeed finding the linker */ + return NULL; } int @@ -153,6 +238,7 @@ main(int argc, char *argv[]) int newargc = 0; char **newargv = NULL; char *wrapper = argc > 0 ? argv[0] : "ld-wrapper"; + char *wrapperdir = NULL; char verbose = getenv("BINUTILS_CONFIG_VERBOSE") != NULL; char *builddir = getenv("PORTAGE_BUILDDIR"); size_t builddirlen; @@ -160,13 +246,68 @@ main(int argc, char *argv[]) int i; int j; int k; + glob_t m; + char *ctarget = CHOST; + size_t ctargetlen; - /* cannonicanise wrapper, stripping path and CHOST */ - if ((p = strrchr(wrapper, '/')) != NULL) + /* two ways to determine CTARGET from argv[0]: + * 1. called as -ld (manually) + * 2. called as EPREFIX/usr/libexec/gcc//ld (by gcc's collect2) + * + * TODO: Make argv[0] absolute without resolving symlinks so no. 2 can + * work when added to PATH (which shouldn't happen in the wild, but + * eh!?). */ + if ((p = strrchr(wrapper, '/')) != NULL) { + /* cannonicanise wrapper step 1: strip path */ wrapper = p + 1; - p = CHOST "-"; - if (strncmp(wrapper, p, strlen(p)) == 0) - wrapper += strlen(p); + + /* remember directory to see if it's CTARGET but only + * if parent is /gcc/ */ + *p = '\0'; + if ((p = strrchr(argv[0], '/')) != NULL) { + char *q; + + *p = '\0'; + if ((q = strrchr(argv[0], '/')) != NULL && + strncmp(q + 1, "gcc", strlen("gcc")) == 0) { + wrapperdir = p + 1; + } + } + } + + /* see if we have a known CTARGET prefix */ + i = glob(EPREFIX "/etc/env.d/binutils/config-*", GLOB_NOSORT, NULL, &m); + if (i == GLOB_NOSPACE) { + fprintf(stderr, "%s: out of memory when inspecting " + "binutils configuration\n", wrapper); + exit(1); + } + if (i == 0) { + for (i = 0; i < m.gl_pathc; i++) { + p = strrchr(m.gl_pathv[i], '/'); + if (p == NULL || strncmp(p, "/config-", strlen("/config-")) != 0) + continue; + + /* EPREFIX/etc/env.d/binutils/config-arm-something-or-other + * move here ^ */ + p += strlen("/config-"); + if (strncmp(wrapper, p, strlen(p)) == 0 || + (wrapperdir != NULL && strcmp(wrapperdir, p) == 0)) { + /* this is us! (MEMLEAK) */ + ctarget = strdup(p); + break; + } + } + } + /* ignore GLOB_NOMATCH and (possibly) GLOB_ABORTED */ + globfree(&m); + + /* cannonicanise wrapper step2: strip CTARGET */ + ctargetlen = strlen(ctarget); + if (strncmp(wrapper, ctarget, ctargetlen) == 0 && + wrapper[ctargetlen] == '-') { + wrapper += ctargetlen + 1; + } /* ensure builddir is something useful */ if (builddir != NULL && *builddir != '/') @@ -186,21 +327,28 @@ main(int argc, char *argv[]) } /* account the original arguments */ newargc += argc > 0 ? argc : 1; -#ifdef TARGET_DARWIN - /* add the 2 prefix paths (-L), -search_paths_first and a - * null-terminator */ - newargc += 2 + 1 + 1; -#else - /* add the 4 paths we want (-L + -R) and a null-terminator */ - newargc += 8 + 1; -#endif -#ifdef TARGET_AIX - /* AIX ld accepts -R only with -bsvr4 */ - newargc++; /* -bsvr4 */ -#endif + /* we always add a null-terminator */ + newargc ++; + /* If a package being cross-compiled injects standard directories, it's + * non-cross-compilable on any platform, prefix or no prefix. So no + * need to add PREFIX- or CTARGET-aware libdirs. */ + if (!is_cross(ctarget)) { + if (is_darwin(ctarget)) { + /* add the 2 prefix paths (-L) and -search_paths_first */ + newargc += 2 + 1; + } else { + /* add the 4 paths we want (-L + -R) */ + newargc += 8; + } + + if (is_aix(ctarget)) { + /* AIX ld accepts -R only with -bsvr4 */ + newargc++; /* -bsvr4 */ + } + } /* let's first try to find the real ld */ - find_real_ld(&ld, verbose, wrapper); + ld = find_real_ld(verbose, wrapper, ctarget); if (ld == NULL) { fprintf(stderr, "%s: failed to locate the real ld!\n", wrapper); exit(1); @@ -215,32 +363,38 @@ main(int argc, char *argv[]) /* construct the new argv */ j = 0; - if ((p = strrchr(ld, '/')) != NULL) { - newargv[j++] = p + 1; - } else { - newargv[j++] = ld; + + /* put the full path to ld into the new argv[0] we're calling it with + * because binutils ld finds its ldscripts directory relative to its + * own call path derived from its argv[0] */ + newargv[j++] = ld; + + if (!is_cross(ctarget) && is_darwin(ctarget)) { + /* inject this first to make the intention clear */ + newargv[j++] = "-search_paths_first"; } -#ifdef TARGET_DARWIN - /* inject this first to make the intention clear */ - newargv[j++] = "-search_paths_first"; -#endif + /* position k right after the original arguments */ k = j - 1 + argc; for (i = 1; i < argc; i++, j++) { -#ifdef TARGET_AIX - /* AIX ld has this problem: - * $ /usr/ccs/bin/ld -bsvr4 -bE:xx.exp -bnoentry xx.o - * ld: 0706-005 Cannot find or open file: l - * ld:open(): No such file or directory - * Simplest workaround is to put -bsvr4 last. - */ - if (strcmp(argv[i], "-bsvr4") == 0) { - --j; --k; - continue; + if (is_aix(ctarget)) { + /* AIX ld has this problem: + * $ /usr/ccs/bin/ld -bsvr4 -bE:xx.exp -bnoentry xx.o + * ld: 0706-005 Cannot find or open file: l + * ld:open(): No such file or directory + * Simplest workaround is to put -bsvr4 last. + */ + if (strcmp(argv[i], "-bsvr4") == 0) { + --j; --k; + continue; + } } -#endif + newargv[j] = argv[i]; -#ifndef TARGET_DARWIN + + if (is_cross(ctarget) || is_darwin(ctarget)) + continue; + /* on ELF targets we add runpaths for all found search paths */ if (argv[i][0] == '-' && argv[i][1] == 'L') { char *path; @@ -280,25 +434,27 @@ main(int argc, char *argv[]) snprintf(newargv[k], len, "-R%s", path); k++; } -#endif } /* add the custom paths */ -#ifdef TARGET_DARWIN - newargv[k++] = "-L" EPREFIX "/usr/lib"; - newargv[k++] = "-L" EPREFIX "/lib"; -#else - newargv[k++] = "-L" EPREFIX "/usr/" CHOST "/lib/gcc"; - newargv[k++] = "-R" EPREFIX "/usr/" CHOST "/lib/gcc"; - newargv[k++] = "-L" EPREFIX "/usr/" CHOST "/lib"; - newargv[k++] = "-R" EPREFIX "/usr/" CHOST "/lib"; - newargv[k++] = "-L" EPREFIX "/usr/lib"; - newargv[k++] = "-R" EPREFIX "/usr/lib"; - newargv[k++] = "-L" EPREFIX "/lib"; - newargv[k++] = "-R" EPREFIX "/lib"; -#endif -#ifdef TARGET_AIX - newargv[k++] = "-bsvr4"; /* last one, see above */ -#endif + if (!is_cross(ctarget)) { + if (is_darwin(ctarget)) { + /* FIXME: no support for cross-compiling *to* Darwin */ + newargv[k++] = "-L" EPREFIX "/usr/lib"; + newargv[k++] = "-L" EPREFIX "/lib"; + } else { + newargv[k++] = "-L" EPREFIX "/usr/" CHOST "/lib/gcc"; + newargv[k++] = "-R" EPREFIX "/usr/" CHOST "/lib/gcc"; + newargv[k++] = "-L" EPREFIX "/usr/" CHOST "/lib"; + newargv[k++] = "-R" EPREFIX "/usr/" CHOST "/lib"; + newargv[k++] = "-L" EPREFIX "/usr/lib"; + newargv[k++] = "-R" EPREFIX "/usr/lib"; + newargv[k++] = "-L" EPREFIX "/lib"; + newargv[k++] = "-R" EPREFIX "/lib"; + } + + if (is_aix(ctarget)) + newargv[k++] = "-bsvr4"; /* last one, see above */ + } newargv[k] = NULL; if (verbose) {