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