/*
  Copyright 2013 Google LLC 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

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

// 这部分是版权声明和许可证信息，说明这个文件是在Apache License 2.0下发布的。

/*
   american fuzzy lop - wrapper for GCC and clang
   ----------------------------------------------

   Written and maintained by Michal Zalewski <lcamtuf@google.com>

   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>

// 这些是包含的头文件，其中一些是AFL自己的头文件，其他的是C标准库的头文件

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*?            */


// 这些是全局变量声明。`as_path`存储AFL汇编器的路径，`cc_params`存储传递给实际编译器的参数，`cc_par_cnt`是参数计数器，`be_quiet`用于控制
// 是否静默模式，`clang_mode`指示是否以`afl-clang`或`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) {
// 这个函数尝试在AFL_PATH环境变量指定的路径或从argv[0]派生的路径中找到AFL的“假”GNU汇编器。如果找不到，程序将终止。
  u8 *afl_path = getenv("AFL_PATH");
  u8 *slash, *tmp;

  if (afl_path) {

    tmp = alloc_printf("%s/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/afl-as", dir);

    if (!access(tmp, X_OK)) {
      as_path = dir;
      ck_free(tmp);
      return;
    }

    ck_free(tmp);
    ck_free(dir);

  }

  if (!access(AFL_PATH "/as", X_OK)) {
    as_path = AFL_PATH;
    return;
  }

  FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH");
 
}


/* Copy argv to cc_params, making the necessary edits. */

static void edit_params(u32 argc, char** argv) {
  //定义了一个函数edit_params，它接受两个参数：argc是参数的数量，argv是参数的数组。

  u8 fortify_set = 0, asan_set = 0;//声明两个变量fortify_set和asan_set，用于跟踪是否已经设置了FORTIFY_SOURCE和address sanitizer（ASan）标志
  u8 *name;//用于存储程序的名称

#if defined(__FreeBSD__) && defined(__x86_64__)
  u8 m32_set = 0;
#endif

  cc_params = ck_alloc((argc + 128) * sizeof(u8*));//分配内存以存储修改后的参数列表，大小为argc + 128个u8*类型的指针。

  name = strrchr(argv[0], '/');//找到argv[0]（程序的路径）中最后一个'/'字符，这通常用于获取程序的名称。
  if (!name) name = argv[0]; else name++;//如果name为NULL（即argv[0]中没有'/'），则name指向argv[0]的开始。否则，name向前移动一个字符，跳过'/'。

  if (!strncmp(name, "afl-clang", 9)) {

    clang_mode = 1;//检查程序名称是否以"afl-clang"开头，如果是，设置clang_mode标志为1

    setenv(CLANG_ENV_VAR, "1", 1);//设置环境变量CLANG_ENV_VAR为"1"，这可能用于通知其他部分的AFL工具链正在使用Clang。

    if (!strcmp(name, "afl-clang++")) {
      u8* alt_cxx = getenv("AFL_CXX");
      cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++";//如果AFL_CXX设置，将其值作为第一个参数；否则，使用"clang++"。
    } else {
      u8* alt_cc = getenv("AFL_CC");
      cc_params[0] = alt_cc ? alt_cc : (u8*)"clang";//否则尝试获取环境变量AFL_CC的值。
    }

  } 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__
    //在Apple系统上，根据程序名称设置不同的编译器。如果AFL_CXX、AFL_GCJ或AFL_CC环境变量设置，使用它们的值；否则，使用默认的编译器名称
    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]) {
      //输出错误信息，指出在MacOS X上需要设置AFL_CC或AFL_CXX环境变量。
      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
    //对于非Apple系统，根据程序名称设置不同的编译器。如果相应的环境变量设置，使用它们的值；否则，使用默认的编译器名称。
    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)) {//如果当前参数以"-B"开头，输出警告信息，并跳过后续参数（如果当前参数后面紧跟着的是编译器的路径）。

      if (!be_quiet) WARNF("-B is already set, overriding");//如果程序不在静默模式，输出警告信息。

      if (!cur[2] && argc > 1) { argc--; argv++; }//如果-B后面紧跟着的是编译器的路径，跳过这个路径。
      continue;

    }

    if (!strcmp(cur, "-integrated-as")) continue;//如果参数是"-integrated-as"，跳过它。

    if (!strcmp(cur, "-pipe")) continue;//如果参数是"-pipe"，跳过它。

#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;//如果参数是"-fsanitize=address"或"-fsanitize=memory"，设置asan_set标志。

    if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;//如果参数包含"FORTIFY_SOURCE"，设置fortify_set标志。

    cc_params[cc_par_cnt++] = cur;

  }

  cc_params[cc_par_cnt++] = "-B";
  cc_params[cc_par_cnt++] = as_path;
  //向参数列表中添加"-B"和AFL汇编器的路径。


  if (clang_mode)
    cc_params[cc_par_cnt++] = "-no-integrated-as";//如果clang_mode标志设置，向参数列表中添加`"-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) {//检查是否设置了asan_set标志。

    /* Pass this on to afl-as to adjust map density. */

    setenv("AFL_USE_ASAN", "1", 1);//如果设置，设置环境变量AFL_USE_ASAN为"1"

  } else if (getenv("AFL_USE_ASAN")) {//如果asan_set标志未设置，但设置了环境变量AFL_USE_ASAN。

    if (getenv("AFL_USE_MSAN"))
      FATAL("ASAN and MSAN are mutually exclusive");//如果同时设置了AFL_USE_MSAN，输出错误信息并终止程序。

    if (getenv("AFL_HARDEN"))
      FATAL("ASAN and AFL_HARDEN are mutually exclusive");//如果同时设置了AFL_HARDEN，输出错误信息并终止程序。

    cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
    cc_params[cc_par_cnt++] = "-fsanitize=address";//向参数列表中添加"-U_FORTIFY_SOURCE"和"-fsanitize=address"。

  } else if (getenv("AFL_USE_MSAN")) {

    if (getenv("AFL_USE_ASAN"))
      FATAL("ASAN and MSAN are mutually exclusive");//如果同时设置了AFL_USE_ASAN，输出错误信息并终止程序。

    if (getenv("AFL_HARDEN"))
      FATAL("MSAN and AFL_HARDEN are mutually exclusive");//如果同时设置了AFL_HARDEN，输出错误信息并终止程序。

    cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
    cc_params[cc_par_cnt++] = "-fsanitize=memory";//向参数列表中添加"-U_FORTIFY_SOURCE"和"-fsanitize=memory"。


  }

  if (!getenv("AFL_DONT_OPTIMIZE")) {//检查是否设置了环境变量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";//如果不是Clang模式或没有设置m32_set标志，向参数列表中添加"-g"。

#else

      cc_params[cc_par_cnt++] = "-g";

#endif//结束#if defined(__FreeBSD__) && defined(__x86_64__)条件编译块。

    cc_params[cc_par_cnt++] = "-O3";
    cc_params[cc_par_cnt++] = "-funroll-loops";//向参数列表中添加"-O3"和"-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";
    //如果设置，向参数列表中添加一系列"-fno-builtin-*"选项，这些选项禁用编译器的内置函数。
  }

  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",
         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;

}
