xref: /aosp_15_r20/external/AFLplusplus/src/afl-ld-lto.c (revision 08b48e0b10e97b33e7b60c5b6e2243bd915777f2)
1 /*
2   american fuzzy lop++ - wrapper for llvm 11+ lld
3   -----------------------------------------------
4 
5   Written by Marc Heuse <[email protected]> for AFL++
6 
7   Maintained by Marc Heuse <[email protected]>,
8                 Heiko Eißfeldt <[email protected]>
9                 Andrea Fioraldi <[email protected]>
10                 Dominik Maier <[email protected]>
11 
12   Copyright 2019-2024 AFLplusplus Project. All rights reserved.
13 
14   Licensed under the Apache License, Version 2.0 (the "License");
15   you may not use this file except in compliance with the License.
16   You may obtain a copy of the License at:
17 
18     https://www.apache.org/licenses/LICENSE-2.0
19 
20   The sole purpose of this wrapper is to preprocess clang LTO files when
21   linking with lld and performing the instrumentation on the whole program.
22 
23 */
24 
25 #define AFL_MAIN
26 #ifndef _GNU_SOURCE
27   #define _GNU_SOURCE
28 #endif
29 
30 #include "config.h"
31 #include "types.h"
32 #include "debug.h"
33 #include "alloc-inl.h"
34 
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40 #include <ctype.h>
41 #include <fcntl.h>
42 #include <limits.h>
43 
44 #include <sys/stat.h>
45 #include <sys/types.h>
46 #include <sys/wait.h>
47 #include <sys/time.h>
48 
49 #include <dirent.h>
50 
51 #ifdef __APPLE__
52   #include <sys/syslimits.h>
53 #endif
54 
55 #define MAX_PARAM_COUNT 4096
56 
57 static u8 **ld_params;              /* Parameters passed to the real 'ld'   */
58 
59 static u8 *afl_path = AFL_PATH;
60 static u8 *real_ld = AFL_REAL_LD;
61 
62 static u8 be_quiet,                 /* Quiet mode (no stderr output)        */
63     debug,                          /* AFL_DEBUG                            */
64     passthrough,                    /* AFL_LD_PASSTHROUGH - no link+optimize*/
65     just_version;                   /* Just show version?                   */
66 
67 static u32 ld_param_cnt = 1;        /* Number of params to 'ld'             */
68 
69 /* Examine and modify parameters to pass to 'ld', 'llvm-link' and 'llmv-ar'.
70    Note that the file name is always the last parameter passed by GCC,
71    so we exploit this property to keep the code "simple". */
edit_params(int argc,char ** argv)72 static void edit_params(int argc, char **argv) {
73 
74   u32 i, gold_pos = 0, gold_present = 0, rt_present = 0, rt_lto_present = 0,
75          inst_present = 0;
76   char *ptr;
77 
78   ld_params = ck_alloc(4096 * sizeof(u8 *));
79 
80   ld_params[0] = (u8 *)real_ld;
81 
82   if (!passthrough) {
83 
84     for (i = 1; i < (u32)argc; i++) {
85 
86       if (strstr(argv[i], "/afl-llvm-rt-lto.o") != NULL) rt_lto_present = 1;
87       if (strstr(argv[i], "/afl-compiler-rt.o") != NULL) rt_present = 1;
88       if (strstr(argv[i], "/afl-llvm-lto-instr") != NULL) inst_present = 1;
89 
90     }
91 
92     for (i = 1; i < (u32)argc && !gold_pos; i++) {
93 
94       if (strcmp(argv[i], "-plugin") == 0) {
95 
96         if (strncmp(argv[i], "-plugin=", strlen("-plugin=")) == 0) {
97 
98           if (strcasestr(argv[i], "LLVMgold.so") != NULL)
99             gold_present = gold_pos = i + 1;
100 
101         } else if (i < (u32)argc &&
102 
103                    strcasestr(argv[i + 1], "LLVMgold.so") != NULL) {
104 
105           gold_present = gold_pos = i + 2;
106 
107         }
108 
109       }
110 
111     }
112 
113     if (!gold_pos) {
114 
115       for (i = 1; i + 1 < (u32)argc && !gold_pos; i++) {
116 
117         if (argv[i][0] != '-') {
118 
119           if (argv[i - 1][0] == '-') {
120 
121             switch (argv[i - 1][1]) {
122 
123               case 'b':
124                 break;
125               case 'd':
126                 break;
127               case 'e':
128                 break;
129               case 'F':
130                 break;
131               case 'f':
132                 break;
133               case 'I':
134                 break;
135               case 'l':
136                 break;
137               case 'L':
138                 break;
139               case 'm':
140                 break;
141               case 'o':
142                 break;
143               case 'O':
144                 break;
145               case 'p':
146                 if (index(argv[i - 1], '=') == NULL) gold_pos = i;
147                 break;
148               case 'R':
149                 break;
150               case 'T':
151                 break;
152               case 'u':
153                 break;
154               case 'y':
155                 break;
156               case 'z':
157                 break;
158               case '-': {
159 
160                 if (strcmp(argv[i - 1], "--oformat") == 0) break;
161                 if (strcmp(argv[i - 1], "--output") == 0) break;
162                 if (strncmp(argv[i - 1], "--opt-remarks-", 14) == 0) break;
163                 gold_pos = i;
164                 break;
165 
166               }
167 
168               default:
169                 gold_pos = i;
170 
171             }
172 
173           } else
174 
175             gold_pos = i;
176 
177         }
178 
179       }
180 
181     }
182 
183     if (!gold_pos) gold_pos = 1;
184 
185   }
186 
187   if (getenv("AFL_LLVM_INSTRIM") ||
188       ((ptr = getenv("AFL_LLVM_INSTRUMENT")) &&
189        (strcasestr(ptr, "CFG") == 0 || strcasestr(ptr, "INSTRIM") == 0)))
190     FATAL(
191         "InsTrim was removed because it is not effective. Use a modern LLVM "
192         "and PCGUARD (which is the default in afl-cc).\n");
193 
194   if (debug)
195     DEBUGF(
196         "passthrough=%s, gold_pos=%u, gold_present=%s "
197         "inst_present=%s rt_present=%s rt_lto_present=%s\n",
198         passthrough ? "true" : "false", gold_pos,
199         gold_present ? "true" : "false", inst_present ? "true" : "false",
200         rt_present ? "true" : "false", rt_lto_present ? "true" : "false");
201 
202   for (i = 1; i < (u32)argc; i++) {
203 
204     if (ld_param_cnt >= MAX_PARAM_COUNT)
205       FATAL(
206           "Too many command line parameters because of unpacking .a archives, "
207           "this would need to be done by hand ... sorry! :-(");
208 
209     if (strcmp(argv[i], "--afl") == 0) {
210 
211       if (!be_quiet) OKF("AFL++ test command line flag detected, exiting.");
212       exit(0);
213 
214     }
215 
216     if (i == gold_pos && !passthrough) {
217 
218       ld_params[ld_param_cnt++] = alloc_printf("-L%s/../lib", LLVM_BINDIR);
219 
220       if (!gold_present) {
221 
222         ld_params[ld_param_cnt++] = "-plugin";
223         ld_params[ld_param_cnt++] =
224             alloc_printf("%s/../lib/LLVMgold.so", LLVM_BINDIR);
225 
226       }
227 
228       ld_params[ld_param_cnt++] = "--allow-multiple-definition";
229 
230       if (!inst_present) {
231 
232         ld_params[ld_param_cnt++] = alloc_printf(
233             "-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", afl_path);
234 
235       }
236 
237       if (!rt_present)
238         ld_params[ld_param_cnt++] =
239             alloc_printf("%s/afl-compiler-rt.o", afl_path);
240       if (!rt_lto_present)
241         ld_params[ld_param_cnt++] =
242             alloc_printf("%s/afl-llvm-rt-lto.o", afl_path);
243 
244     }
245 
246     ld_params[ld_param_cnt++] = argv[i];
247 
248   }
249 
250   ld_params[ld_param_cnt] = NULL;
251 
252 }
253 
254 /* Main entry point */
255 
main(int argc,char ** argv)256 int main(int argc, char **argv) {
257 
258   s32  pid, i, status;
259   char thecwd[PATH_MAX];
260 
261   if (getenv("AFL_LD_CALLER") != NULL) {
262 
263     FATAL("ld loop detected! Set AFL_REAL_LD!\n");
264 
265   }
266 
267   if (isatty(2) && !getenv("AFL_QUIET") && !getenv("AFL_DEBUG")) {
268 
269     SAYF(cCYA "afl-ld-to" VERSION cRST
270               " by Marc \"vanHauser\" Heuse <[email protected]>\n");
271 
272   } else
273 
274     be_quiet = 1;
275 
276   if (getenv("AFL_DEBUG") != NULL) debug = 1;
277   if (getenv("AFL_PATH") != NULL) afl_path = getenv("AFL_PATH");
278   if (getenv("AFL_LD_PASSTHROUGH") != NULL) passthrough = 1;
279   if (getenv("AFL_REAL_LD") != NULL) real_ld = getenv("AFL_REAL_LD");
280 
281   if (!afl_path || !*afl_path) afl_path = AFL_PATH;
282 
283   setenv("AFL_LD_CALLER", "1", 1);
284 
285   if (debug) {
286 
287     if (getcwd(thecwd, sizeof(thecwd)) != 0) strcpy(thecwd, ".");
288 
289     DEBUGF("cd \"%s\";", thecwd);
290     for (i = 0; i < argc; i++)
291       SAYF(" \"%s\"", argv[i]);
292     SAYF("\n");
293 
294   }
295 
296   if (argc < 2) {
297 
298     SAYF(
299         "\n"
300         "This is a helper application for afl-clang-lto.\n"
301         "It is a wrapper around llvm's 'lld' in case afl-clang-lto cannot be "
302         "used.\n"
303         "Note that the target still has to be compiled with -flto=full!\n"
304         "You probably don't want to run this program directly but rather pass "
305         "it as LD\nparameter to e.g. configure scripts.\n\n"
306 
307         "Environment variables:\n"
308         "  AFL_LD_PASSTHROUGH   do not link+optimize == no instrumentation\n"
309         "  AFL_REAL_LD          point to the real llvm 11 lld if necessary\n"
310 
311         "\nafl-ld-to was compiled with the fixed real 'ld' of %s and the "
312         "binary path of %s\n\n",
313         real_ld, LLVM_BINDIR);
314 
315     exit(1);
316 
317   }
318 
319   edit_params(argc, argv);  // here most of the magic happens :-)
320 
321   if (debug) {
322 
323     DEBUGF("cd \"%s\";", thecwd);
324     for (i = 0; i < (s32)ld_param_cnt; i++)
325       SAYF(" \"%s\"", ld_params[i]);
326     SAYF("\n");
327 
328   }
329 
330   if (!(pid = fork())) {
331 
332     if (strlen(real_ld) > 1) execvp(real_ld, (char **)ld_params);
333     execvp("ld", (char **)ld_params);  // fallback
334     FATAL("Oops, failed to execute 'ld' - check your PATH");
335 
336   }
337 
338   if (pid < 0) PFATAL("fork() failed");
339 
340   if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed");
341   if (debug) DEBUGF("linker result: %d\n", status);
342 
343   if (!just_version) {
344 
345     if (status == 0) {
346 
347       if (!be_quiet) OKF("Linker was successful");
348 
349     } else {
350 
351       SAYF(cLRD "[-] " cRST
352                 "Linker failed, please investigate and send a bug report. Most "
353                 "likely an 'ld' option is incompatible with %s.\n",
354            AFL_CLANG_FLTO);
355 
356     }
357 
358   }
359 
360   exit(WEXITSTATUS(status));
361 
362 }
363 
364