commit: 554171c6a3d43f87ac771e45be2b5136b18fd2af
Author: Michael Weiser <michael <AT> weiser <DOT> dinsnail <DOT> net>
AuthorDate: Sun Dec 24 14:40:33 2017 +0000
Commit: Fabian Groffen <grobian <AT> gentoo <DOT> 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 <ctype.h>
#include <sys/stat.h>
#include <errno.h>
+#include <glob.h>
+#include <stdarg.h>
/**
* 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-<version> */
+ 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 <CTARGET>-ld (manually)
+ * 2. called as EPREFIX/usr/libexec/gcc/<CTARGET>/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) {