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

/*
   american fuzzy lop - map display utility
   ----------------------------------------

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

   A very simple tool that runs the targeted binary and displays
   the contents of the trace bitmap in a human-readable form. Useful in
   scripts to eliminate redundant inputs and perform other checks.

   Exit code is 2 if the target program crashes; 1 if it times out or
   there is a problem executing it; or 0 if execution is successful.
*/

#define AFL_MAIN
#include "android-ashmem.h"

#include "config.h"
#include "types.h"
#include "debug.h"
#include "alloc-inl.h"
#include "hash.h"

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <dirent.h>
#include <fcntl.h>

#include <sys/wait.h>
#include <sys/time.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/resource.h>

static s32 child_pid;                 /* 被测试程序的进程 PID          */

static u8* trace_bits;                /* 用于存储插桩信息的共享内存   */

static u8 *out_file,                  /* 输出文件路径                 */
          *doc_path,                  /* 文档目录路径                      */
          *target_path,               /* 被测目标程序的路径             */
          *at_file;                   /* `@@` 的替换字符串，用于输入文件        */

static u32 exec_tmout;                /* 测试程序的执行超时时间 (毫秒)                 */

static u64 mem_limit = MEM_LIMIT;     /* 测试程序的内存限制 (MB)                 */

static s32 shm_id;                    /* 共享内存 (SHM) 的标识符              */

static u8  quiet_mode,                /* 是否隐藏非必要的信息      */
           edges_only,                /* 是否只关心边覆盖率                */
           cmin_mode,                 /* 是否以 `afl-cmin` 模式生成输出 */
           binary_mode,               /* 是否以二进制方式输出插桩信息      */
           keep_cores;                /* A是否允许生成 core dump 文件                  */

static volatile u8
           stop_soon,                 /* 是否按下了 Ctrl-C (中止标志)                   */
           child_timed_out,           /* 子进程是否超时                 */
           child_crashed;             /* 子进程是否崩溃                    */

/* Classify tuple counts. Instead of mapping to individual bits, as in
   afl-fuzz.c, we map to more user-friendly numbers between 1 and 8. */

static const u8 count_class_human[256] = {

  [0]           = 0,
  [1]           = 1,
  [2]           = 2,
  [3]           = 3,
  [4 ... 7]     = 4,
  [8 ... 15]    = 5,
  [16 ... 31]   = 6,
  [32 ... 127]  = 7,
  [128 ... 255] = 8

}; // 映射插桩命中计数到 1-8 的更具可读性的区间。0 代表未命中；1-3 被保留为原值；其余区间依次分段映射，便于统计分析。

static const u8 count_class_binary[256] = {

  [0]           = 0,
  [1]           = 1,
  [2]           = 2,
  [3]           = 4,
  [4 ... 7]     = 8,
  [8 ... 15]    = 16,
  [16 ... 31]   = 32,
  [32 ... 127]  = 64,
  [128 ... 255] = 128

}; // 将插桩计数按指数方式映射到二进制值，便于程序进一步处理。

static void classify_counts(u8* mem, const u8* map) {//对插桩命中计数 (mem) 进行分类处理，基于全局变量 edges_only 的值选择分类模式。
//u8* mem：插桩数据的内存指针。const u8* map：分类映射表

  u32 i = MAP_SIZE;

  if (edges_only) {

    while (i--) {
      if (*mem) *mem = 1;// 只关心边的覆盖情况，命中计数直接归一
      mem++;
    }

  } else {

    while (i--) {
      *mem = map[*mem];// 使用提供的映射表对命中计数进行分类
      mem++;
    }

  }

}


/* 删除共享内存 (atexit 处理程序). */

static void remove_shm(void) {//释放分配的共享内存。

  shmctl(shm_id, IPC_RMID, NULL); //使用 shmctl 函数删除共享内存区域
  //IPC_RMID 标志用于标记共享内存为 "移除"，在无其他进程访问后会自动释放。
  //通过 atexit 注册，在程序退出时自动调用，确保清理资源。

}


/* 配置共享内存 */

static void setup_shm(void) {// 创建共享内存，分配给 trace_bits 用于记录插桩数据。通过 atexit 注册清理函数，确保资源的释放。

  u8* shm_str;
  /* 创建一个私有的共享内存区域，大小为 MAP_SIZE */
  shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);

  if (shm_id < 0) PFATAL("shmget() failed");/* 创建失败时输出错误并退出 */

  atexit(remove_shm);/* 注册 atexit 处理程序以在程序退出时删除共享内存 */

   /* 将共享内存 ID 转换为字符串并设置环境变量 */
  shm_str = alloc_printf("%d", shm_id);

  setenv(SHM_ENV_VAR, shm_str, 1);

  ck_free(shm_str);/* 释放临时字符串内存 */

  /* 将共享内存附加到当前进程的地址空间 */
  trace_bits = shmat(shm_id, NULL, 0);
  
  if (trace_bits == (void *)-1) PFATAL("shmat() failed"); /* 附加失败时退出 */

}

<<<<<<< HEAD
/* 写入测试结果 */
=======
/* 配置共享内存。 */
>>>>>>> 31b9bf56f4cc249d8b6bb414203bff28d8cf724a

static u32 write_results(void) { //将插桩结果输出到文件，支持二进制和文本模式。

  s32 fd;           /* 文件描述符 */
  u32 i, ret = 0;   /* 计数器和返回值 */

  u8  cco = !!getenv("AFL_CMIN_CRASHES_ONLY"),    /* 仅输出崩溃路径？ */
      caa = !!getenv("AFL_CMIN_ALLOW_ANY");       /* 是否允许所有路径？ */

  /* 根据输出文件路径的类型处理 */
  if (!strncmp(out_file, "/dev/", 5)) {           /* 如果是设备文件路径 */

    fd = open(out_file, O_WRONLY, 0600);
    if (fd < 0) PFATAL("Unable to open '%s'", out_file);

  } else if (!strcmp(out_file, "-")) {            /* 如果是标准输出 */

    fd = dup(1);                                  /* 复制标准输出描述符 */
    if (fd < 0) PFATAL("Unable to open stdout");

  } else {                                        /* 普通文件路径 */

    unlink(out_file); /* 删除已有的同名文件 (忽略错误) */
    fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600);
    if (fd < 0) PFATAL("Unable to create '%s'", out_file);

  }

  /* 如果以二进制模式输出结果 */
  if (binary_mode) {

    for (i = 0; i < MAP_SIZE; i++)
      if (trace_bits[i]) ret++;/* 统计非零插桩计数 */
    
    ck_write(fd, trace_bits, MAP_SIZE, out_file);/* 将插桩数据写入文件 */
    close(fd);

  } else { /* 文本模式输出 */

    FILE* f = fdopen(fd, "w");

    if (!f) PFATAL("fdopen() failed");

    /* 遍历插桩数据，按文本格式输出 */
    for (i = 0; i < MAP_SIZE; i++) {

      if (!trace_bits[i]) continue;/* 跳过空条目 */
      ret++;

      if (cmin_mode) {/* afl-cmin 模式 */

        if (child_timed_out) break; /* 如果子进程超时，停止输出 */
        if (!caa && child_crashed != cco) break;/* 根据环境变量条件过滤 */

        fprintf(f, "%u%u\n", trace_bits[i], i);

      } else fprintf(f, "%06u:%u\n", i, trace_bits[i]);/* 标准格式 */

    }
  
    fclose(f);

  }

  return ret;

}


<<<<<<< HEAD
/*  处理超时信号 */
=======

/* 处理超时信号。 */

>>>>>>> 31b9bf56f4cc249d8b6bb414203bff28d8cf724a

static void handle_timeout(int sig) {

  child_timed_out = 1; /* 设置子进程超时标志 */
  if (child_pid > 0) kill(child_pid, SIGKILL); /* 强制杀死子进程 */

}


<<<<<<< HEAD
/* 执行目标程序 */

=======
/* 执行目标应用程序。 */
 /* 运行目标程序并等待其结束。 */
  /* 处理超时和信号。 */
>>>>>>> 31b9bf56f4cc249d8b6bb414203bff28d8cf724a
static void run_target(char** argv) {

  static struct itimerval it;/* 定时器结构 */
  int status = 0;

  if (!quiet_mode)
    SAYF("-- Program output begins --\n" cRST);/* 输出调试信息 */

  MEM_BARRIER(); /* 防止编译器重排内存操作 */

  child_pid = fork();/* 创建子进程 */

  if (child_pid < 0) PFATAL("fork() failed");/* 如果 fork 失败，退出程序 */

  if (!child_pid) { /* 子进程逻辑 */

    struct rlimit r;

    if (quiet_mode) { /* 如果静默模式开启，将子进程输出重定向到 `/dev/null` */

      s32 fd = open("/dev/null", O_RDWR);

      if (fd < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0) {
        *(u32*)trace_bits = EXEC_FAIL_SIG; /* 设置错误标志 */
        PFATAL("Descriptor initialization failed");
      }

      close(fd);

    }

    if (mem_limit) {/* 设置子进程内存限制 */

      r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20;

#ifdef RLIMIT_AS

      setrlimit(RLIMIT_AS, &r); /* 设置内存限制 (按地址空间限制)，忽略错误 */

#else

      setrlimit(RLIMIT_DATA, &r); /* 设置内存限制 (按数据段大小限制)，忽略错误 */
#endif /* ^RLIMIT_AS */

    }
// 根据平台使用适当的资源限制类型 (RLIMIT_AS 或 RLIMIT_DATA) 限制子进程的内存使用。
    if (!keep_cores) r.rlim_max = r.rlim_cur = 0; /* 禁止 core dump */
// 控制是否允许子进程生成 core dump 文件。
    else r.rlim_max = r.rlim_cur = RLIM_INFINITY; /* 允许无限 core dump */

    setrlimit(RLIMIT_CORE, &r); /* 设置 core dump 限制，忽略错误 */

    if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0);  // 强制目标程序加载动态库时立即绑定符号，避免延迟加载可能引发的问题。

    setsid(); /* 创建一个新会话，隔离子进程 */

    execv(target_path, argv);/* 替换当前进程镜像为目标程序 */

    *(u32*)trace_bits = EXEC_FAIL_SIG; /* 如果 execv 返回，则标记执行失败 */
    exit(0);/* 终止子进程 */

  }

  /* Configure timeout, wait for child, cancel timeout. */

  if (exec_tmout) {

    child_timed_out = 0;
    it.it_value.tv_sec = (exec_tmout / 1000);
    it.it_value.tv_usec = (exec_tmout % 1000) * 1000;

  }

  setitimer(ITIMER_REAL, &it, NULL);/* 配置超时定时器 */

  if (waitpid(child_pid, &status, 0) <= 0) FATAL("waitpid() failed"); /* 等待子进程结束 */

  child_pid = 0;
  it.it_value.tv_sec = 0;
  it.it_value.tv_usec = 0;
  setitimer(ITIMER_REAL, &it, NULL);

  MEM_BARRIER();

  /* Clean up bitmap, analyze exit condition, etc. */

  if (*(u32*)trace_bits == EXEC_FAIL_SIG)
    FATAL("Unable to execute '%s'", argv[0]);

  classify_counts(trace_bits, binary_mode ?
                  count_class_binary : count_class_human);
/* 将 trace_bits 中的插桩命中数据分类为二进制或人类可读格式 */
  if (!quiet_mode)
    SAYF(cRST "-- Program output ends --\n");

  if (!child_timed_out && !stop_soon && WIFSIGNALED(status))
    child_crashed = 1;

  if (!quiet_mode) {

    if (child_timed_out)
      SAYF(cLRD "\n+++ Program timed off +++\n" cRST);
    else if (stop_soon)
      SAYF(cLRD "\n+++ Program aborted by user +++\n" cRST);
    else if (child_crashed)
      SAYF(cLRD "\n+++ Program killed by signal %u +++\n" cRST, WTERMSIG(status));

  }


}


/* Handle Ctrl-C and the like. */

static void handle_stop_sig(int sig) {//捕获停止信号 (SIGHUP, SIGINT, SIGTERM) 并设置全局停止标志。终止子进程以确保程序退出。

  stop_soon = 1;/* 设置标志，通知主程序停止 */

  if (child_pid > 0) kill(child_pid, SIGKILL);/* 强制杀死子进程 */

}


/* Do basic preparations - persistent fds, filenames, etc. */

static void set_up_environment(void) {
// 设置环境变量，配置 AddressSanitizer (ASAN) 和 MemorySanitizer (MSAN) 行为：禁用内存泄漏检测 (detect_leaks=0)。禁用符号化输出 (symbolize=0)。配置错误退出行为 (abort_on_error=1)。
  setenv("ASAN_OPTIONS", "abort_on_error=1:"
                         "detect_leaks=0:"
                         "symbolize=0:"
                         "allocator_may_return_null=1", 0);

  setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":"
                         "symbolize=0:"
                         "abort_on_error=1:"
                         "allocator_may_return_null=1:"
                         "msan_track_origins=0", 0);
  // 如果存在 AFL_PRELOAD 环境变量，将其设置为动态库预加载路径。
  if (getenv("AFL_PRELOAD")) {
    setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
    setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
  }

}


/* Setup signal handlers, duh. */

static void setup_signal_handlers(void) {

  struct sigaction sa;

  sa.sa_handler   = NULL;
  sa.sa_flags     = SA_RESTART;
  sa.sa_sigaction = NULL;

  sigemptyset(&sa.sa_mask);

  /* Various ways of saying "stop". */
  // 为停止信号 (SIGHUP, SIGINT, SIGTERM) 注册处理函数 handle_stop_sig。
  sa.sa_handler = handle_stop_sig; /* 设置停止信号的处理函数 */
  sigaction(SIGHUP, &sa, NULL);
  sigaction(SIGINT, &sa, NULL);
  sigaction(SIGTERM, &sa, NULL);

  /* Exec timeout notifications. */

  sa.sa_handler = handle_timeout;/* 设置超时信号的处理函数 */
  sigaction(SIGALRM, &sa, NULL);

}


/* 检测@@在参数中。 */
 /* 检测并替换@@参数。 */
  /* 用于处理传递给目标程序的文件路径参数。 */
static void detect_file_args(char** argv) {

  // 获取当前工作目录，用于构造绝对路径。
  u32 i = 0;
  u8* cwd = getcwd(NULL, 0);/* 获取当前工作目录 */

  if (!cwd) PFATAL("getcwd() failed");

  while (argv[i]) {

    u8* aa_loc = strstr(argv[i], "@@");/* 查找命令行参数中的 @@ */

    if (aa_loc) {

      u8 *aa_subst, *n_arg;

      if (!at_file) FATAL("@@ syntax is not supported by this tool.");

      /* Be sure that we're always using fully-qualified paths. */

      if (at_file[0] == '/') aa_subst = at_file;/* 如果是绝对路径，直接使用 */
      else aa_subst = alloc_printf("%s/%s", cwd, at_file);/* 构造绝对路径 */

      /* Construct a replacement argv value. */

      *aa_loc = 0;
      n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2);/* 替换 @@ */
      argv[i] = n_arg;
      *aa_loc = '@';

      if (at_file[0] != '/') ck_free(aa_subst);

    }

    i++;

  }

  free(cwd); /* not tracked */

}


<<<<<<< HEAD
/* Show banner. */
// 打印工具的名称和版本信息。
=======


>>>>>>> 31b9bf56f4cc249d8b6bb414203bff28d8cf724a
static void show_banner(void) {

  SAYF(cCYA "afl-showmap " cBRI VERSION cRST " by <lcamtuf@google.com>\n");

}

/* 显示使用提示。 */

static void usage(u8* argv0) {

  show_banner();
  // 显示工具的用法说明，包括参数和选项说明。
  SAYF("\n%s [ options ] -- /path/to/target_app [ ... ]\n\n"

       "Required parameters:\n\n"

       "  -o file       - file to write the trace data to\n\n"

       "Execution control settings:\n\n"

       "  -t msec       - timeout for each run (none)\n"
       "  -m megs       - memory limit for child process (%u MB)\n"
       "  -Q            - use binary-only instrumentation (QEMU mode)\n\n"

       "Other settings:\n\n"
// 描述可用的执行控制选项，包括超时时间和内存限制。
       "  -q            - sink program's output and don't show messages\n"
       "  -e            - show edge coverage only, ignore hit counts\n"
       "  -c            - allow core dumps\n"
       "  -V            - show version number and exit\n\n"

       "This tool displays raw tuple data captured by AFL instrumentation.\n"
       "For additional help, consult %s/README.\n\n" cRST,

       argv0, MEM_LIMIT, doc_path);

  exit(1);

}


<<<<<<< HEAD
/* Find binary. */
// 找到目标程序的可执行文件路径 (target_path)。
=======
/* 查找二进制文件。 */
/* 查找并验证二进制文件。 */
  /* 如果二进制文件不存在或不可执行，则打印错误信息并退出。 */
>>>>>>> 31b9bf56f4cc249d8b6bb414203bff28d8cf724a
static void find_binary(u8* fname) {
// fname：目标程序的文件名。如果 fname 包含斜杠（表明是路径）或没有设置 PATH 环境变量，则直接检查 fname 是否是一个可执行文件。否则，遍历 PATH 环境变量中定义的路径，尝试找到 fname。
  u8* env_path = 0;
  struct stat st;

  if (strchr(fname, '/') || !(env_path = getenv("PATH"))) {
    // 如果 fname 是路径（包含斜杠），直接检查其合法性。
    target_path = ck_strdup(fname);
    // 如果 PATH 环境变量未设置，同样直接检查 fname。
    if (stat(target_path, &st) || !S_ISREG(st.st_mode) ||
        !(st.st_mode & 0111) || st.st_size < 4)
      FATAL("Program '%s' not found or not executable", fname);

  } else {

    while (env_path) {

      u8 *cur_elem, *delim = strchr(env_path, ':');

      if (delim) {

        cur_elem = ck_alloc(delim - env_path + 1);
        memcpy(cur_elem, env_path, delim - env_path);
        delim++;

      } else cur_elem = ck_strdup(env_path);

      env_path = delim;
      // 按冒号分隔符解析 PATH 环境变量，依次检查每个路径是否包含目标文件。
      if (cur_elem[0])
        target_path = alloc_printf("%s/%s", cur_elem, fname);
      else
        target_path = ck_strdup(fname);

      ck_free(cur_elem);
      // 将文件名与路径组合成完整路径。检查文件是否存在、是否是普通文件、是否可执行且文件大小合理。如果找到符合条件的文件路径，则退出循环。
      if (!stat(target_path, &st) && S_ISREG(st.st_mode) &&
          (st.st_mode & 0111) && st.st_size >= 4) break;

      ck_free(target_path);
      target_path = 0;

    }
    // 如果遍历完所有路径后仍未找到目标文件，则程序退出。
    if (!target_path) FATAL("Program '%s' not found or not executable", fname);

  }

}


<<<<<<< HEAD
/* Fix up argv for QEMU. */
// 为在 QEMU 模式下执行目标程序，调整参数列表 argv。
=======

/*修正argv以用于QEMU*/
/* 为QEMU模式修正参数*/
/* 返回新的参数数组*/
>>>>>>> 31b9bf56f4cc249d8b6bb414203bff28d8cf724a
static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
  // own_loc：当前程序的路径，用于寻找 afl-qemu-trace。argv：原始参数列表。argc：参数个数。
  char** new_argv = ck_alloc(sizeof(char*) * (argc + 4));
  u8 *tmp, *cp, *rsl, *own_copy;

  /* 设置 QEMU 的环境变量 QEMU_LOG，禁用链式日志。 */

  setenv("QEMU_LOG", "nochain", 1);

  memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc);
  // 为新参数列表分配空间。
  // 将原始参数 argv 复制到新参数列表的合适位置。
  // 添加必要的 QEMU 参数 -- 和 target_path。
  new_argv[2] = target_path;
  new_argv[1] = "--";

  /* Now we need to actually find qemu for argv[0]. */
  // 检查 AFL_PATH 环境变量是否指向 afl-qemu-trace，并验证其是否可执行。
  tmp = getenv("AFL_PATH");

  if (tmp) {

    cp = alloc_printf("%s/afl-qemu-trace", tmp);

    if (access(cp, X_OK))
      FATAL("Unable to find '%s'", tmp);

    target_path = new_argv[0] = cp;
    return new_argv;

  }
  // 使用当前程序的路径 own_loc 尝试找到 afl-qemu-trace。
  own_copy = ck_strdup(own_loc);
  rsl = strrchr(own_copy, '/');

  if (rsl) {

    *rsl = 0;

    cp = alloc_printf("%s/afl-qemu-trace", own_copy);
    ck_free(own_copy);

    if (!access(cp, X_OK)) {

      target_path = new_argv[0] = cp;
      return new_argv;

    }

  } else ck_free(own_copy);
  // 检查预定义路径 BIN_PATH 下是否存在 afl-qemu-trace。
  if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) {

    target_path = new_argv[0] = BIN_PATH "/afl-qemu-trace";
    return new_argv;

  }
  // 如果未找到 afl-qemu-trace，程序退出并报错。
  FATAL("Unable to find 'afl-qemu-trace'.");

}


/* Main entry point */
/*解析命令行参数并执行目标程序 */
/*根据参数执行不同的操作 */
int main(int argc, char** argv) {
  //opt：用于存储当前解析的命令行选项。
  //mem_limit_given：标志是否设置了内存限制选项。
  //timeout_given：标志是否设置了超时选项。
  //qemu_mode：标志是否启用了 QEMU 模式。
  //tcnt：存储记录的插桩数据元组计数。
  //use_argv：用于执行目标程序的参数列表。

  s32 opt;
  u8  mem_limit_given = 0, timeout_given = 0, qemu_mode = 0;
  u32 tcnt;
  char** use_argv;
  // 检查 DOC_PATH 是否存在，如果不存在则回退到默认路径 "docs"。
  doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
  // 使用 getopt 解析命令行选项，支持的选项包括：

  while ((opt = getopt(argc,argv,"+o:m:t:A:eqZQbcV")) > 0)

    switch (opt) {
      // -o：指定输出文件。
      case 'o':

        if (out_file) FATAL("Multiple -o options not supported");
        out_file = optarg;
        break;
      // -m：设置内存限制。
      case 'm': {

          u8 suffix = 'M';

          if (mem_limit_given) FATAL("Multiple -m options not supported");
          mem_limit_given = 1;

          if (!strcmp(optarg, "none")) {

            mem_limit = 0;
            break;

          }

          if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 ||
              optarg[0] == '-') FATAL("Bad syntax used for -m");

          switch (suffix) {

            case 'T': mem_limit *= 1024 * 1024; break;
            case 'G': mem_limit *= 1024; break;
            case 'k': mem_limit /= 1024; break;
            case 'M': break;

            default:  FATAL("Unsupported suffix or bad syntax for -m");

          }

          if (mem_limit < 5) FATAL("Dangerously low value of -m");

          if (sizeof(rlim_t) == 4 && mem_limit > 2000)
            FATAL("Value of -m out of range on 32-bit systems");

        }

        break;
      // -t：设置超时时间。
      case 't':

        if (timeout_given) FATAL("Multiple -t options not supported");
        timeout_given = 1;

        if (strcmp(optarg, "none")) {
          exec_tmout = atoi(optarg);

          if (exec_tmout < 20 || optarg[0] == '-')
            FATAL("Dangerously low value of -t");

        }

        break;
      // -e：只关注边覆盖率。
      case 'e':

        if (edges_only) FATAL("Multiple -e options not supported");
        edges_only = 1;
        break;
      // -q：静默模式，不显示多余信息。
      case 'q':

        if (quiet_mode) FATAL("Multiple -q options not supported");
        quiet_mode = 1;
        break;
      // -Z：启用 afl-cmin 模式，内部使用，设置 cmin_mode 和 quiet_mode。
      case 'Z':

        /* This is an undocumented option to write data in the syntax expected
           by afl-cmin. Nobody else should have any use for this. */

        cmin_mode  = 1;
        quiet_mode = 1;
        break;
      // -A：设置 @@ 替换文件路径。
      case 'A':

        /* Another afl-cmin specific feature. */
        at_file = optarg;
        break;
      // -Q：启用 QEMU 模式，如果未指定内存限制，使用默认值 MEM_LIMIT_QEMU。
      case 'Q':

        if (qemu_mode) FATAL("Multiple -Q options not supported");
        if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU;

        qemu_mode = 1;
        break;
      // -b：启用二进制输出模式，设置全局变量 binary_mode = 1。
      case 'b':

        /* Secret undocumented mode. Writes output in raw binary format
           similar to that dumped by afl-fuzz in <out_dir/queue/fuzz_bitmap. */

        binary_mode = 1;
        break;
      // -c：允许生成核心转储文件，设置全局变量 keep_cores = 1。
      case 'c':

        if (keep_cores) FATAL("Multiple -c options not supported");
        keep_cores = 1;
        break;
      // -V：显示版本号并退出程序。
      case 'V':

        show_banner();
        exit(0);

      default:

        usage(argv[0]);

    }

  if (optind == argc || !out_file) usage(argv[0]); //如果未指定目标程序路径或输出文件，则显示用法说明并退出。

  setup_shm();// 调用 setup_shm 函数，配置用于插桩数据存储的共享内存。
  setup_signal_handlers();// 注册信号处理函数，处理程序停止（SIGINT、SIGTERM）或超时（SIGALRM）等事件。

  set_up_environment();// 配置环境变量，例如 ASAN_OPTIONS 和 MSAN_OPTIONS。

  find_binary(argv[optind]);//使用 find_binary 函数查找目标程序的可执行文件路径，并存储到全局变量 target_path。


  if (!quiet_mode) {// 如果未启用静默模式，显示工具信息和目标程序路径。
    show_banner();
    ACTF("Executing '%s'...\n", target_path);
  }

  detect_file_args(argv + optind); //检测命令行参数中的 @@ 并将其替换为指定的文件路径。

  if (qemu_mode) //如果启用了 QEMU 模式，调用 get_qemu_argv 函数为 QEMU 准备参数列表。
    use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
  else // 否则直接使用原始参数。
    use_argv = argv + optind;

  run_target(use_argv);

  tcnt = write_results(); // 调用 write_results 函数，将插桩数据写入指定的输出文件。

  if (!quiet_mode) {

    if (!tcnt) FATAL("No instrumentation detected" cRST); //如果没有捕获插桩数据，显示错误并退出。
    OKF("Captured %u tuples in '%s'." cRST, tcnt, out_file); // 否则显示捕获的元组数量和输出文件路径。

  }

  exit(child_crashed * 2 + child_timed_out);
  // 根据子进程状态退出.如果子进程崩溃：返回 2。如果子进程超时：返回 1。正常退出：返回 0。
}

