text: /*
     american fuzzy lop - wrapper for GCC and clang
     ----------------------------------------------
  
     Written and maintained by Michal Zalewski <lcamtuf@google.com>
  
     Copyright 2013, 2014, 2015 Google Inc. All rights reserved.
  
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at:
  
       http://www.apache.org/licenses/LICENSE-2.0
  
     This program is a drop-in replacement for GCC or clang. The most common way
     of using it is to pass the path to afl-gcc or afl-clang via CC when invoking
     ./configure.
  
     (Of course, use CXX and point it to afl-g++ / afl-clang++ for C++ code.)
  
     The wrapper needs to know the path to afl-as (renamed to 'as'). The default
     is /usr/local/lib/afl/. A convenient way to specify alternative directories
     would be to set AFL_PATH.
  
     If AFL_HARDEN is set, the wrapper will compile the target app with various
     hardening options that may help detect memory management issues more
     reliably. You can also specify AFL_USE_ASAN to enable ASAN.
  
     If you want to call a non-default compiler as a next step of the chain,
     specify its location via AFL_CC or AFL_CXX.
  
   */
  
  #define AFL_MAIN
  
  #include "config.h"
  #include "types.h"
  #include "debug.h"
  #include "alloc-inl.h"
  
  #include <stdio.h>
  #include <unistd.h>
  #include <stdlib.h>
  #include <string.h>
  
  static u8*  as_path;                /* Path to the AFL 'as' wrapper      */
  static u8** cc_params;              /* Parameters passed to the real CC  */
  static u32  cc_par_cnt = 1;         /* Param count, including argv0      */
  static u8   be_quiet,               /* Quiet mode                        */
              clang_mode;             /* Invoked as afl-clang*?            */
  
  
  /* Try to find our "fake" GNU assembler in AFL_PATH or at the location derived
     from argv[0]. If that fails, abort. */
  
  static void find_as(u8* argv0) {
  
    u8 *afl_path = getenv("UNTRACER-AFL_PATH");
    u8 *slash, *tmp;
  
    if (afl_path) {
  
      tmp = alloc_printf("%s/untracer-as", afl_path);
  
      if (!access(tmp, X_OK)) {
        as_path = afl_path;
        ck_free(tmp);
        return;
      }
  
      ck_free(tmp);
  
    }
  
    slash = strrchr(argv0, '/');
  
    if (slash) {
  
      u8 *dir;
  
      *slash = 0;
      dir = ck_strdup(argv0);
      *slash = '/';
  
      tmp = alloc_printf("%s/untracer-as", dir);
  
      if (!access(tmp, X_OK)) {
        as_path = dir;
        ck_free(tmp);
        return;
      }
  
      ck_free(tmp);
      ck_free(dir);
  
    }
  
    if (!access(AFL_PATH "/untracer-as", X_OK)) {
      as_path = AFL_PATH;
      return;
    }
  
    FATAL("Unable to find AFL wrapper binary for 'as'. Please set UNTRACER-AFL_PATH");
   
  }
  
  
  /* Copy argv to cc_params, making the necessary edits. */
  
  static void edit_params(u32 argc, char** argv) {
  
    u8 fortify_set = 0, asan_set = 0;
    u8 *name;
  
  #if defined(__FreeBSD__) && defined(__x86_64__)
    u8 m32_set = 0;
  #endif
  
    cc_params = ck_alloc((argc + 128) * sizeof(u8*));
  
    name = strrchr(argv[0], '/');
    if (!name) name = argv[0]; else name++;
  
    if (!strncmp(name, "afl-clang", 9)) {
  
      clang_mode = 1;
  
      setenv(CLANG_ENV_VAR, "1", 1);
  
      if (!strcmp(name, "afl-clang++")) {
        u8* alt_cxx = getenv("AFL_CXX");
        cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++";
      } else {
        u8* alt_cc = getenv("AFL_CC");
        cc_params[0] = alt_cc ? alt_cc : (u8*)"clang";
      }
  
    } else {
  
      /* With GCJ and Eclipse installed, you can actually compile Java! The
         instrumentation will work (amazingly). Alas, unhandled exceptions do
         not call abort(), so afl-fuzz would need to be modified to equate
         non-zero exit codes with crash conditions when working with Java
         binaries. Meh. */
  
  #ifdef __APPLE__
  
      if (!strcmp(name, "afl-g++")) cc_params[0] = getenv("AFL_CXX");
      else if (!strcmp(name, "afl-gcj")) cc_params[0] = getenv("AFL_GCJ");
      else cc_params[0] = getenv("AFL_CC");
  
      if (!cc_params[0]) {
  
        SAYF("\n" cLRD "[-] " cRST
             "On Apple systems, 'gcc' is usually just a wrapper for clang. Please use the\n"
             "    'afl-clang' utility instead of 'afl-gcc'. If you really have GCC installed,\n"
             "    set AFL_CC or AFL_CXX to specify the correct path to that compiler.\n");
  
        FATAL("AFL_CC or AFL_CXX required on MacOS X");
  
      }
  
  #else
  
      if (!strcmp(name, "afl-g++")) {
        u8* alt_cxx = getenv("AFL_CXX");
        cc_params[0] = alt_cxx ? alt_cxx : (u8*)"g++";
      } else if (!strcmp(name, "afl-gcj")) {
        u8* alt_cc = getenv("AFL_GCJ");
        cc_params[0] = alt_cc ? alt_cc : (u8*)"gcj";
      } else {
        u8* alt_cc = getenv("AFL_CC");
        cc_params[0] = alt_cc ? alt_cc : (u8*)"gcc";
      }
  
  #endif /* __APPLE__ */
  
    }
  
    while (--argc) {
      u8* cur = *(++argv);
  
      if (!strncmp(cur, "-B", 2)) {
  
        if (!be_quiet) WARNF("-B is already set, overriding");
  
        if (!cur[2] && argc > 1) { argc--; argv++; }
        continue;
  
      }
  
      if (!strcmp(cur, "-integrated-as")) continue;
  
      if (!strcmp(cur, "-pipe")) continue;
  
  #if defined(__FreeBSD__) && defined(__x86_64__)
      if (!strcmp(cur, "-m32")) m32_set = 1;
  #endif
  
      if (!strcmp(cur, "-fsanitize=address") ||
          !strcmp(cur, "-fsanitize=memory")) asan_set = 1;
  
      if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
  
      cc_params[cc_par_cnt++] = cur;
  
    }
  
    cc_params[cc_par_cnt++] = "-B";
    cc_params[cc_par_cnt++] = as_path;
  
    if (clang_mode)
      cc_params[cc_par_cnt++] = "-no-integrated-as";
  
    if (getenv("AFL_HARDEN")) {
  
      cc_params[cc_par_cnt++] = "-fstack-protector-all";
  
      if (!fortify_set)
        cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
  
    }
  
    if (asan_set) {
  
      /* Pass this on to afl-as to adjust map density. */
  
      setenv("AFL_USE_ASAN", "1", 1);
  
    } else if (getenv("AFL_USE_ASAN")) {
  
      if (getenv("AFL_USE_MSAN"))
        FATAL("ASAN and MSAN are mutually exclusive");
  
      if (getenv("AFL_HARDEN"))
        FATAL("ASAN and AFL_HARDEN are mutually exclusive");
  
      cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
      cc_params[cc_par_cnt++] = "-fsanitize=address";
  
    } else if (getenv("AFL_USE_MSAN")) {
  
      if (getenv("AFL_USE_ASAN"))
        FATAL("ASAN and MSAN are mutually exclusive");
  
      if (getenv("AFL_HARDEN"))
        FATAL("MSAN and AFL_HARDEN are mutually exclusive");
  
      cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
      cc_params[cc_par_cnt++] = "-fsanitize=memory";
  
  
    }
  
    if (!getenv("AFL_DONT_OPTIMIZE")) {
  
  #if defined(__FreeBSD__) && defined(__x86_64__)
  
      /* On 64-bit FreeBSD systems, clang -g -m32 is broken, but -m32 itself
         works OK. This has nothing to do with us, but let's avoid triggering
         that bug. */
  
      if (!clang_mode || !m32_set)
        cc_params[cc_par_cnt++] = "-g";
  
  #else
  
        cc_params[cc_par_cnt++] = "-g";
  
  #endif
  
      cc_params[cc_par_cnt++] = "-O3";
      cc_params[cc_par_cnt++] = "-funroll-loops";
  
      /* Two indicators that you're building for fuzzing; one of them is
         AFL-specific, the other is shared with libfuzzer. */
  
      cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1";
      cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1";
  
    }
  
    if (getenv("AFL_NO_BUILTIN")) {
  
      cc_params[cc_par_cnt++] = "-fno-builtin-strcmp";
      cc_params[cc_par_cnt++] = "-fno-builtin-strncmp";
      cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp";
      cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp";
      cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
      cc_params[cc_par_cnt++] = "-fno-builtin-strstr";
      cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr";
  
    }
  
    cc_params[cc_par_cnt] = NULL;
  
  }
  
  
  /* Main entry point */
  
  int main(int argc, char** argv) {
  
    if (isatty(2) && !getenv("AFL_QUIET")) {
  
      SAYF(cCYA "afl-cc " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
  
    } else be_quiet = 1;
  
    if (argc < 2) {
  
      SAYF("\n"
           "This is a helper application for afl-fuzz. It serves as a drop-in replacement\n"
           "for gcc or clang, letting you recompile third-party code with the required\n"
           "runtime instrumentation. A common use pattern would be one of the following:\n\n"
  
           "  CC=%s/afl-gcc ./configure\n"
           "  CXX=%s/afl-g++ ./configure\n\n"
  
           "You can specify custom next-stage toolchain via AFL_CC, AFL_CXX, and AFL_AS.\n"
           "Setting AFL_HARDEN enables hardening optimizations in the compiled code.\n\n"
  
           "To enable forkserver-only instrumentation mode, use CFLAG \"-Wa,-F\".\n\n",
           BIN_PATH, BIN_PATH);
  
      exit(1);
  
    }
  
    find_as(argv[0]);
  
    edit_params(argc, argv);
  
    execvp(cc_params[0], (char**)cc_params);
  
    FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
  
    return 0;
  
  }
  