/[gentoo-alt]/trunk/toolchain-prefix-wrapper/ld/ldwrapper.c
Gentoo

Contents of /trunk/toolchain-prefix-wrapper/ld/ldwrapper.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1591 - (show annotations) (download) (as text)
Fri Nov 2 10:58:02 2007 UTC (6 years, 11 months ago) by haubi
File MIME type: text/x-csrc
File size: 13432 byte(s)
initial import of toolchain-prefix-wrapper

1 /*
2 * Copyright 1999-2007 Gentoo Foundation
3 * Distributed under the terms of the GNU General Public License v2
4 * Authors: Fabian Groffen <grobian@gentoo.org>
5 * Michael Haubenwallner <haubi@gentoo.org>
6 * based on the work of gcc wrapper done by:
7 * Martin Schlemmer <azarah@gentoo.org>
8 * Mike Frysinger <vapier@gentoo.org>
9 */
10
11 #include <config.h>
12 #include <stringutil.h>
13
14 #include <darwinplugin.h>
15 #include <aixplugin.h>
16 #include <hpuxplugin.h>
17 #include <gnuplugin.h>
18
19 #define _REENTRANT
20 #if !defined(_GNU_SOURCE)
21 #define _GNU_SOURCE
22 #endif
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/param.h>
29 #include <unistd.h>
30 #include <sys/wait.h>
31 #include <libgen.h>
32 #include <string.h>
33 #include <stdarg.h>
34 #include <errno.h>
35
36 #define BINUTILS_CONFIG GENTOO_PORTAGE_EPREFIX "/usr/bin/binutils-config"
37 #define ENVD_BASE_BINUTILS GENTOO_PORTAGE_EPREFIX "/etc/env.d/05binutils"
38 #define ENVD_BASE_GCC GENTOO_PORTAGE_EPREFIX "/etc/env.d/05gcc"
39
40 struct wrapper_data {
41 char name[MAXPATHLEN + 1];
42 char fullname[MAXPATHLEN + 1];
43 char bin[MAXPATHLEN + 1];
44 char ldpath[MAXPATHLEN + 1];
45 char tmp[MAXPATHLEN + 1];
46 char *path;
47 };
48
49 static const char *wrapper_strerror(int err, struct wrapper_data *data)
50 {
51 /* this app doesn't use threads and strerror
52 * is more portable than strerror_r */
53 strncpy(data->tmp, strerror(err), sizeof(data->tmp));
54 return data->tmp;
55 }
56
57 static void wrapper_exit(char *msg, ...)
58 {
59 va_list args;
60 fprintf(stderr, "binutils-config error: ");
61 va_start(args, msg);
62 vfprintf(stderr, msg, args);
63 va_end(args);
64 exit(1);
65 }
66
67 /* check_for_binutils checks in path for the file we are seeking
68 * it returns 1 if found (with data->bin setup), 0 if not and
69 * negative on error
70 */
71 static int check_for_binutils(char *path, struct wrapper_data *data)
72 {
73 struct stat sbuf;
74 int result;
75 char str[MAXPATHLEN + 1];
76 size_t len = strlen(path) + strlen(data->name) + 2;
77
78 snprintf(str, len, "%s/%s", path, data->name);
79
80 /* Stat possible file to check that
81 * 1) it exist and is a regular file, and
82 * 2) it is not the wrapper itself, and
83 * 3) it is in a /binutils-bin/ directory tree
84 */
85 result = stat(str, &sbuf);
86 if ((result == 0) && \
87 ((sbuf.st_mode & S_IFREG) || (sbuf.st_mode & S_IFLNK)) && \
88 (strcmp(str, data->fullname) != 0) && \
89 (strstr(str, "/binutils-bin/") != 0)) {
90
91 strncpy(data->bin, str, MAXPATHLEN);
92 data->bin[MAXPATHLEN] = 0;
93 result = 1;
94 } else
95 result = 0;
96
97 return result;
98 }
99
100 static int find_binutils_in_path(struct wrapper_data *data)
101 {
102 char *token = NULL, *state;
103 char str[MAXPATHLEN + 1];
104
105 if (data->path == NULL) return 0;
106
107 /* Make a copy since strtok_r will modify path */
108 snprintf(str, MAXPATHLEN + 1, "%s", data->path);
109
110 token = strtok_r(str, ":", &state);
111
112 /* Find the first file with suitable name in PATH. The idea here is
113 * that we do not want to bind ourselfs to something static like the
114 * default profile, or some odd environment variable, but want to be
115 * able to build something with a non default binutils by just tweaking
116 * the PATH ... */
117 while ((token != NULL) && strlen(token)) {
118 if (check_for_binutils(token, data))
119 return 1;
120 token = strtok_r(NULL, ":", &state);
121 }
122
123 return 0;
124 }
125
126 /* find_binutils_in_envd parses /etc/env.d/05binutils, and tries to
127 * extract PATH, which is set to the current profile's bin
128 * directory ...
129 */
130 static int find_binutils_in_envd(struct wrapper_data *data, int cross_compile)
131 {
132 FILE *envfile = NULL;
133 char *token = NULL, *state;
134 char str[MAXPATHLEN + 1];
135 char *strp = str;
136 char envd_file[MAXPATHLEN + 1];
137
138 if (!cross_compile) {
139 snprintf(envd_file, MAXPATHLEN, "%s", ENVD_BASE_BINUTILS);
140 } else {
141 char *ctarget, *end = strrchr(data->name, '-');
142 if (end == NULL)
143 return 0;
144 ctarget = strdup(data->name);
145 ctarget[end - data->name] = '\0';
146 snprintf(envd_file, MAXPATHLEN, "%s-%s", ENVD_BASE_BINUTILS, ctarget);
147 free(ctarget);
148 }
149 envfile = fopen(envd_file, "r");
150 if (envfile == NULL)
151 return 0;
152
153 while (0 != fgets(strp, MAXPATHLEN, envfile)) {
154 /* Keep reading ENVD_FILE until we get a line that
155 * starts with 'PATH='
156 */
157 if (((strp) && (strlen(strp) > strlen("PATH=")) &&
158 !strncmp("PATH=", strp, strlen("PATH=")))) {
159
160 token = strtok_r(strp, "=", &state);
161 if ((token != NULL) && strlen(token))
162 /* The second token should be the value of PATH .. */
163 token = strtok_r(NULL, "=", &state);
164 else
165 goto bail;
166
167 if ((token != NULL) && strlen(token)) {
168 strp = token;
169 /* A bash variable may be unquoted, quoted with " or
170 * quoted with ', so extract the value without those ..
171 */
172 token = strtok(strp, "\n\"\'");
173
174 while (token != NULL) {
175 if (check_for_binutils(token, data)) {
176 fclose(envfile);
177 return 1;
178 }
179
180 token = strtok(NULL, "\n\"\'");
181 }
182 }
183 }
184 strp = str;
185 }
186
187 bail:
188 fclose(envfile);
189 return (cross_compile ? 0 : find_binutils_in_envd(data, 1));
190 }
191
192 /* find_ldpath_in_envd parses the given file, and tries to
193 * extract LDPATH, of which only the first path is returned
194 */
195 static int find_ldpath_in_envd(struct wrapper_data *data, char* file, int cross_compile)
196 {
197 FILE *envfile = NULL;
198 char *token = NULL, *state;
199 char str[MAXPATHLEN + 1];
200 char *strp = str;
201 char envd_file[MAXPATHLEN + 1];
202
203 if (!cross_compile) {
204 snprintf(envd_file, MAXPATHLEN, "%s", file);
205 } else {
206 char *ctarget, *end = strrchr(data->name, '-');
207 if (end == NULL)
208 return 0;
209 ctarget = strdup(data->name);
210 ctarget[end - data->name] = '\0';
211 snprintf(envd_file, MAXPATHLEN, "%s-%s", file, ctarget);
212 free(ctarget);
213 }
214 envfile = fopen(envd_file, "r");
215 if (envfile == NULL)
216 return 0;
217
218 while (0 != fgets(strp, MAXPATHLEN, envfile)) {
219 /* Keep reading ENVD_FILE until we get a line that
220 * starts with 'LDPATH='
221 */
222 if (((strp) && (strlen(strp) > strlen("LDPATH=")) &&
223 !strncmp("LDPATH=", strp, strlen("LDPATH=")))) {
224
225 token = strtok_r(strp, "=", &state);
226 if ((token != NULL) && strlen(token))
227 /* The second token should be the value of LDPATH .. */
228 token = strtok_r(NULL, "=", &state);
229 else
230 goto bail;
231
232 if ((token != NULL) && strlen(token)) {
233 strp = token;
234 /* A bash variable may be unquoted, quoted with " or
235 * quoted with ', so extract the value without those ..
236 */
237 token = strtok(strp, "\n\"\'");
238
239 if (token != NULL) {
240 /* only take the first path in the string */
241 if ((strp = strchr(token, ':')) != NULL)
242 *strp = '\0';
243 strncpy(data->ldpath, token, MAXPATHLEN);
244 fclose(envfile);
245 return 1;
246 }
247 }
248 }
249 strp = str;
250 }
251
252 bail:
253 fclose(envfile);
254 return (cross_compile ? 0 : find_ldpath_in_envd(data, file, 1));
255 }
256
257 static void find_wrapper_binutils(struct wrapper_data *data)
258 {
259 FILE *inpipe = NULL;
260 char str[MAXPATHLEN + 1];
261
262 if (find_binutils_in_path(data))
263 return;
264
265 if (find_binutils_in_envd(data, 0))
266 return;
267
268 /* Only our wrapper is in PATH, so
269 get the CC path using binutils-config and
270 execute the real binary in there... */
271 inpipe = popen(BINUTILS_CONFIG " --get-bin-path", "r");
272 if (inpipe == NULL)
273 wrapper_exit(
274 "Could not open pipe: %s\n",
275 wrapper_strerror(errno, data));
276
277 if (fgets(str, MAXPATHLEN, inpipe) == 0)
278 wrapper_exit(
279 "Could not get linker binary path: %s\n",
280 wrapper_strerror(errno, data));
281
282 strncpy(data->bin, str, sizeof(data->bin) - 1);
283 data->bin[strlen(data->bin) - 1] = '/';
284 strncat(data->bin, data->name, sizeof(data->bin) - 1);
285 data->bin[MAXPATHLEN] = 0;
286
287 pclose(inpipe);
288 }
289
290 static StringList* filterSysLibpath(StringList *argList, StringList *userLibpathList, StringList *sysLibpathList)
291 {
292 StringList *newArgList = NULL;
293 int argc;
294 String *argString;
295 char const *argBuffer;
296 int argLength;
297
298 /* keep argv[0] */
299 newArgList = StringListCreate(argList, 0, 1);
300 if (newArgList == NULL) return NULL;
301
302 for(argc = 1; argc < StringListGetSize(argList); argc++) {
303 argString = StringListGetString(argList, argc);
304 argBuffer = StringGetBuffer(argString);
305 argLength = StringGetLength(argString);
306
307 if (strncmp(argBuffer, "-L", 2) == 0) {
308 argBuffer += 2;
309 argLength -= 2;
310
311 if (*argBuffer == 0) {
312 if (argc+1 == StringListGetSize(argList)) {
313 /* no more arguments */
314 continue;
315 }
316 argString = StringListGetString(argList, ++argc);
317 argBuffer = StringGetBuffer(argString);
318 argLength = StringGetLength(argString);
319 }
320
321 if (StringListContains(sysLibpathList, argBuffer, argLength)) {
322 /* do not pass sys libpath early */
323 continue;
324 }
325
326 /* add to user libpath list */
327 if (StringListAppendConcat(userLibpathList, argBuffer, argLength, NULL) < 0)
328 break;
329
330 /* keep user libpath on commandline */
331 if (StringListAppendConcat(newArgList, "-L", 2, argBuffer, argLength, NULL) < 0)
332 break;
333
334 /* end -L handling */
335 continue;
336 }
337
338 /* keep other arguments on commandline */
339 if (StringListAppendString(newArgList, argString) < 0)
340 break;
341 }
342 if (argc < StringListGetSize(argList) && newArgList) {
343 /* error */
344 newArgList = StringListDestroy(newArgList);
345 }
346
347 return newArgList;
348 }
349
350 static StringList* callPlugin(StringList*(*plugin)(StringList*, StringList*, StringList*)
351 , StringList *argList, StringList *userLibpathList, StringList *sysLibpathList)
352 {
353 StringList *newArgList;
354
355 if (plugin == NULL) {
356 return argList;
357 }
358
359 newArgList = plugin(argList, userLibpathList, sysLibpathList);
360 if (newArgList == NULL) {
361 return NULL;
362 }
363
364 if (newArgList != argList) {
365 StringListDestroy(argList);
366 }
367 return newArgList;
368 }
369
370 int main(int argc, char *argv[])
371 {
372 StringList *argList = NULL;
373 StringList *userLibpathList = NULL;
374 StringList *sysLibpathList = NULL;
375 String *tmpString = NULL;
376 struct wrapper_data data;
377 size_t size;
378 int i;
379 char **newargv = argv;
380 struct stat sbuf;
381
382 memset(&data, 0, sizeof(data));
383
384 if (getenv("PATH")) {
385 data.path = strdup(getenv("PATH"));
386 if (data.path == NULL)
387 wrapper_exit("%s wrapper: %s\n", argv[0], strerror(errno));
388 }
389
390 /* What should we find?
391 * If this is a ${CHOST}-ld{,64} thing, strip the ${CHOST}- */
392 strcpy(data.name, basename(argv[0]));
393 size = strlen(data.name);
394 #ifdef __MACH__
395 /* only Apple/OSX/Darwin has an ld64 as far as I know */
396 if (size > 4) {
397 if (strcmp(&data.name[size - 5], "-ld64") == 0) {
398 strcpy(data.name, "ld64");
399 size = 0; /* trick the if below in thinking it won't work */
400 }
401 }
402 #endif
403 if (size > 2) {
404 if (strcmp(&data.name[size - 3], "-ld") == 0)
405 strcpy(data.name, "ld");
406 }
407
408 /* What is the full name of our wrapper? */
409 size = sizeof(data.fullname);
410 i = snprintf(data.fullname, size, GENTOO_PORTAGE_EPREFIX "/usr/bin/%s", data.name);
411 if ((i == -1) || (i > (int)size))
412 wrapper_exit("invalid wrapper name: \"%s\"\n", data.name);
413
414 find_wrapper_binutils(&data);
415
416 if (data.path)
417 free(data.path);
418 data.path = NULL;
419
420 argList = StringListFromArgv(argc, argv);
421 if (argList == NULL) {
422 wrapper_exit("cannot create arg list: %s\n", strerror(errno));
423 }
424
425 sysLibpathList = StringListCreate(NULL, 0, 0);
426 if (sysLibpathList == NULL) {
427 wrapper_exit("cannot create sys libpath list: %s\n", strerror(errno));
428 }
429
430 userLibpathList = StringListCreate(NULL, 0, 0);
431 if (userLibpathList == NULL) {
432 wrapper_exit("cannot create user libpath list: %s\n", strerror(errno));
433 }
434
435 /* Get the include path for the compiler and linker */
436 if (find_ldpath_in_envd(&data, ENVD_BASE_GCC, 0) == 0) {
437 fprintf(stderr, "binutils-config: warning: no GCC found on your system!\n");
438 } else {
439 if (StringListAppendConcat(sysLibpathList, data.ldpath, strlen(data.ldpath), NULL) < 0) {
440 wrapper_exit("cannot append \"%s\" to sys libpath list: %s\n", data.ldpath, strerror(errno));
441 }
442 }
443 if (find_ldpath_in_envd(&data, ENVD_BASE_BINUTILS, 0) != 0) {
444 if (StringListAppendConcat(sysLibpathList, data.ldpath, strlen(data.ldpath), NULL) < 0) {
445 wrapper_exit("cannot append \"%s\" to sys libpath list: %s\n", data.ldpath, strerror(errno));
446 }
447 } else {
448 fprintf(stderr, "binutils-config: warning: linker not found!\n");
449 }
450
451 #define ADDLIBDIR(X) \
452 tmpString = StringCreateConcat(GENTOO_PORTAGE_EPREFIX, strlen(GENTOO_PORTAGE_EPREFIX), X, strlen(X), NULL); \
453 if (tmpString == NULL) \
454 wrapper_exit("cannot create temporary libpath string: %s\n", strerror(errno)); \
455 i = stat(StringGetBuffer(tmpString), &sbuf); \
456 if ((i == 0) && (sbuf.st_mode & S_IFDIR)) { \
457 if (StringListAppendString(sysLibpathList, tmpString) < 0) { \
458 wrapper_exit("cannot extend sys libpath list: %s\n", strerror(errno)); \
459 } \
460 }
461
462 ADDLIBDIR("/usr/lib64");
463 ADDLIBDIR("/usr/lib");
464 ADDLIBDIR("/lib64");
465 ADDLIBDIR("/lib");
466
467 /* filter out sys libpath's passed with "-L" */
468 argList = callPlugin(filterSysLibpath, argList, userLibpathList, sysLibpathList);
469 if (argList == NULL) {
470 wrapper_exit("cannot filter sys libpath's from arguments: %s\n", strerror(errno));
471 }
472
473 #if defined(PLUGIN)
474 #define str(x) #x
475 {
476 extern StringList* PLUGIN(StringList*, StringList*, StringList*);
477 argList = callPlugin(PLUGIN, argList, userLibpathList, sysLibpathList);
478 if (argList == NULL) {
479 wrapper_exit("%s failed: %s\n", str(x), strerror(errno));
480 }
481 }
482 #endif
483
484 newargv = StringListToArgv(argList);
485 if (newargv == NULL) {
486 wrapper_exit("cannot create argument array: %s\n", strerror(errno));
487 }
488
489 /* Ok, lets do it one more time ... */
490 if (execv(data.bin, newargv) < 0)
491 wrapper_exit("Could not run/locate \"%s\" (%s)\n", data.name, data.bin);
492
493 return 0;
494 }

Properties

Name Value
svn:executable

  ViewVC Help
Powered by ViewVC 1.1.20