/*
  Copyright 2015 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 - test case minimizer
   ----------------------------------------

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

   A simple test case minimizer that takes an input file and tries to remove
   as much data as possible while keeping the binary in a crashing state
   *or* producing consistent instrumentation output (the mode is auto-selected
   based on the initially observed behavior).
*/

#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,                /* 仪器位图的共享内存   */
* mask_bitmap;               /* 跟踪位的掩码（-B选项） */

static u8* in_file,                   /* 最小化工具输入测试用例         */
* out_file,                  /* 最小化工具输出文件             */
* prog_in,                   /* 目标程序输入文件       */
* target_path,               /* 目标二进制文件路径             */
* doc_path;                  /* 文档路径                      */

static u8* in_data;                   /* 用于修剪的输入数据           */

static u32 in_len,                    /* 输入数据长度                 */
orig_cksum,                /* 原始校验和                 */
total_execs,               /* 执行总数             */
missed_hangs,              /* 由于挂起错过的次数               */
missed_crashes,            /* 由于崩溃错过的次数               */
missed_paths,              /* 由于执行路径差异错过的次数     */
exec_tmout = EXEC_TIMEOUT; /* 执行超时（毫秒）                 */

static u64 mem_limit = MEM_LIMIT;     /* 内存限制（MB）                 */

static s32 shm_id,                    /* 共享内存区域的ID              */
dev_null_fd = -1;          /* /dev/null的文件描述符                   */

static u8  crash_mode,                /* 以崩溃为中心的模式？               */
exit_crash,                /* 将非零退出码视为崩溃？     */
edges_only,                /* 忽略命中计数？                */
exact_mode,                /* 要求路径匹配才能视为崩溃？   */
use_stdin = 1;             /* 使用标准输入作为程序输入？      */

static volatile u8
stop_soon,                 /* Ctrl-C被按下？                   */
child_timed_out;           /* 子进程超时？                  */


/* 对元组计数进行分类。这是一个慢速且简单的版本，但在这里足够了。 */

static const u8 count_class_lookup[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 apply_mask(u32* mem, u32* mask) {

    u32 i = (MAP_SIZE >> 2);

    if (!mask) return;

    while (i--) {

        *mem &= ~*mask;
        mem++;
        mask++;

    }

}

/* 检查位图中是否有任何字节被设置。 */

static inline u8 anything_set(void) {

    u32* ptr = (u32*)trace_bits;
    u32  i = (MAP_SIZE >> 2);

    while (i--) if (*(ptr++)) return 1;

    return 0;

}

/* 清除共享内存和临时文件（atexit处理程序）。 */

static void remove_shm(void) {

    if (prog_in) unlink(prog_in); /* 忽略错误 */
    shmctl(shm_id, IPC_RMID, NULL);

}

/* 配置共享内存。 */

static void setup_shm(void) {

    u8* shm_str;

    shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);

    if (shm_id < 0) PFATAL("shmget() failed");

    atexit(remove_shm);

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

}

/* 读取初始文件。 */

static void read_initial_file(void) {

    struct stat st;
    s32 fd = open(in_file, O_RDONLY);

    if (fd < 0) PFATAL("Unable to open '%s'", in_file);

    if (fstat(fd, &st) || !st.st_size)
        FATAL("Zero-sized input file.");

    if (st.st_size >= TMIN_MAX_FILE)
        FATAL("Input file is too large (%u MB max)", TMIN_MAX_FILE / 1024 / 1024);

    in_len = st.st_size;
    in_data = ck_alloc_nozero(in_len);

    ck_read(fd, in_data, in_len, in_file);

    close(fd);

    OKF("Read %u byte%s from '%s'.", in_len, in_len == 1 ? "" : "s", in_file);

}

/* 写入输出文件。 */

static s32 write_to_file(u8* path, u8* mem, u32 len) {

    s32 ret;

    unlink(path); /* 忽略错误 */

    ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600);

    if (ret < 0) PFATAL("Unable to create '%s'", path);

    ck_write(ret, mem, len, path);

    lseek(ret, 0, SEEK_SET);

    return ret;

}

/* 处理超时信号。 */

static void handle_timeout(int sig) {

    child_timed_out = 1;
    if (child_pid > 0) kill(child_pid, SIGKILL);

}

/* 执行目标应用程序。返回0表示更改无效，或1表示应该保留更改。 */

static u8 run_target(char** argv, u8* mem, u32 len, u8 first_run) {

    static struct itimerval it;
    int status = 0;

    s32 prog_in_fd;
    u32 cksum;

    memset(trace_bits, 0, MAP_SIZE);
    MEM_BARRIER();

    prog_in_fd = write_to_file(prog_in, mem, len);

    child_pid = fork();

    if (child_pid < 0) PFATAL("fork() failed");

    if (!child_pid) {

        struct rlimit r;

        if (dup2(use_stdin ? prog_in_fd : dev_null_fd, 0) < 0 ||
            dup2(dev_null_fd, 1) < 0 ||
            dup2(dev_null_fd, 2) < 0) {

            *(u32*)trace_bits = EXEC_FAIL_SIG;
            PFATAL("dup2() failed");

        }

        close(dev_null_fd);
        close(prog_in_fd);

        setsid();

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

        }

        r.rlim_max = r.rlim_cur = 0;
        setrlimit(RLIMIT_CORE, &r); /* 忽略错误 */

        execv(target_path, argv);

        *(u32*)trace_bits = EXEC_FAIL_SIG;
        exit(0);
    }

    close(prog_in_fd);

    /* 配置超时，等待子进程，取消超时。 */

    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();

    /* 清理位图，分析退出条件等。 */

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

    classify_counts(trace_bits);
    apply_mask((u32*)trace_bits, (u32*)mask_bitmap);
    total_execs++;

    if (stop_soon) {

        SAYF(cRST cLRD "\n+++ Minimization aborted by user +++\n" cRST);
        close(write_to_file(out_file, in_data, in_len));
        exit(1);
    }

    /* 总是丢弃超时的输入。 */

    if (child_timed_out) {

        missed_hangs++;
        return 0;
    }

    /* 根据当前模式处理崩溃输入。 */

    if (WIFSIGNALED(status) ||
        (WIFEXITED(status) && WEXITSTATUS(status) == MSAN_ERROR) ||
        (WIFEXITED(status) && WEXITSTATUS(status) && exit_crash)) {

        if (first_run) crash_mode = 1;

        if (crash_mode) {

            if (!exact_mode) return 1;

        }
        else {

            missed_crashes++;
            return 0;

        }
    }
    else

        /* 适当处理非崩溃输入。 */

        if (crash_mode) {

            missed_paths++;
            return 0;
        }

    cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);

    if (first_run) orig_cksum = cksum;

    if (orig_cksum == cksum) return 1;

    missed_paths++;
    return 0;

}

/* 找到不小于val的最小2的幂。 */

static u32 next_p2(u32 val) {

    u32 ret = 1;
    while (val > ret) ret <<= 1;
    return ret;

}

/* 实际进行最小化操作！ */

static void minimize(char** argv) {

    static u32 alpha_map[256];

    u8* tmp_buf = ck_alloc_nozero(in_len);
    u32 orig_len = in_len, stage_o_len;

    u32 del_len, set_len, del_pos, set_pos, i, alpha_size, cur_pass = 0;
    u32 syms_removed, alpha_del0 = 0, alpha_del1, alpha_del2, alpha_d_total = 0;
    u8  changed_any, prev_del;

    /***********************

    BLOCK NORMALIZATION * ***********************/
    set_len = next_p2(in_len / TMIN_SET_STEPS);
    set_pos = 0;

    if (set_len < TMIN_SET_MIN_SIZE) set_len = TMIN_SET_MIN_SIZE;

    ACTF(cBRI "Stage #0: " cRST "One-time block normalization...");

    while (set_pos < in_len) {

        u8  res;
        u32 use_len = MIN(set_len, in_len - set_pos);

        for (i = 0; i < use_len; i++)
            if (in_data[set_pos + i] != '0') break;

        if (i != use_len) {

            memcpy(tmp_buf, in_data, in_len);
            memset(tmp_buf + set_pos, '0', use_len);

            res = run_target(argv, tmp_buf, in_len, 0);

            if (res) {

                memset(in_data + set_pos, '0', use_len);
                changed_any = 1;
                alpha_del0 += use_len;

            }

        }

        set_pos += set_len;
    }

    alpha_d_total += alpha_del0;

    OKF("Block normalization complete, %u byte%s replaced.", alpha_del0,
        alpha_del0 == 1 ? "" : "s");

next_pass:

    ACTF(cYEL "--- " cBRI "Pass #%u " cYEL "---", ++cur_pass);
    changed_any = 0;

    /******************

    BLOCK DELETION * ******************/
    del_len = next_p2(in_len / TRIM_START_STEPS);
    stage_o_len = in_len;

    ACTF(cBRI "Stage #1: " cRST "Removing blocks of data...");

next_del_blksize:

    if (!del_len) del_len = 1;
    del_pos = 0;
    prev_del = 1;

    SAYF(cGRA "    Block length = %u, remaining size = %u\n" cRST,
        del_len, in_len);

    while (del_pos < in_len) {

        u8  res;
        s32 tail_len;

        tail_len = in_len - del_pos - del_len;
        if (tail_len < 0) tail_len = 0;

        /* 如果我们已经处理了至少一个完整的块（最初，prev_del == 1），
          并且我们没有删除前一个块，并且我们不在缓冲区的末端（tail_len > 0），
          并且当前块与前一个块相同...跳过这一步作为无操作。 */

        if (!prev_del && tail_len && !memcmp(in_data + del_pos - del_len,
            in_data + del_pos, del_len)) {

            del_pos += del_len;
            continue;

        }

        prev_del = 0;

        /* 头部 */
        memcpy(tmp_buf, in_data, del_pos);

        /* 尾部 */
        memcpy(tmp_buf + del_pos, in_data + del_pos + del_len, tail_len);

        res = run_target(argv, tmp_buf, del_pos + tail_len, 0);

        if (res) {

            memcpy(in_data, tmp_buf, del_pos + tail_len);
            prev_del = 1;
            in_len = del_pos + tail_len;

            changed_any = 1;

        }
        else del_pos += del_len;
    }

    if (del_len > 1 && in_len >= 1) {

        del_len /= 2;
        goto next_del_blksize;
    }

    OKF("Block removal complete, %u bytes deleted.", stage_o_len - in_len);

    if (!in_len && changed_any)
        WARNF(cLRD "Down to zero bytes - check the command line and mem limit!" cRST);

    if (cur_pass > 1 && !changed_any) goto finalize_all;

    /* 初始化变量，用于记录不同字符的数量以及被删除的字符数 */
    alpha_size = 0;
    alpha_del1 = 0;
    syms_removed = 0;

    /* 初始化一个大小为256的数组，用于记录每个字符出现的次数 */
    memset(alpha_map, 0, 256 * sizeof(u32));

    /* 遍历输入数据，统计每个字符出现的次数 */
    for (i = 0; i < in_len; i++) {
        if (!alpha_map[in_data[i]]) alpha_size++;  // 如果字符是第一次出现，alpha_size加1
        alpha_map[in_data[i]]++;  // 记录字符出现次数
    }

    /* 显示当前阶段信息，正在最小化符号（字符） */
    ACTF(cBRI "Stage #2: " cRST "Minimizing symbols (%u code point%s)...",
        alpha_size, alpha_size == 1 ? "" : "s");

    /* 遍历所有可能的字符 */
    for (i = 0; i < 256; i++) {

        u32 r;
        u8 res;

        /* 如果字符是'0'或者没有出现过，则跳过 */
        if (i == '0' || !alpha_map[i]) continue;

        /* 复制当前的输入数据到临时缓冲区 */
        memcpy(tmp_buf, in_data, in_len);

        /* 将当前字符替换为'0' */
        for (r = 0; r < in_len; r++)
            if (tmp_buf[r] == i) tmp_buf[r] = '0';

        /* 运行目标程序，检查替换后的数据是否仍然有效 */
        res = run_target(argv, tmp_buf, in_len, 0);

        /* 如果替换后的数据仍然有效，则更新输入数据 */
        if (res) {
            memcpy(in_data, tmp_buf, in_len);
            syms_removed++;  // 增加被删除的字符数
            alpha_del1 += alpha_map[i];  // 增加被删除的字符所占的字节数
            changed_any = 1;  // 标记发生了变化
        }
    }

    /* 更新总共被删除的字符所占的字节数 */
    alpha_d_total += alpha_del1;

    /* 显示符号最小化完成的信息 */
    OKF("Symbol minimization finished, %u symbol%s (%u byte%s) replaced.",
        syms_removed, syms_removed == 1 ? "" : "s",
        alpha_del1, alpha_del1 == 1 ? "" : "s");

    /************************* CHARACTER MINIMIZATION *************************/
    /* 初始化变量，用于记录被删除的字符数 */
    alpha_del2 = 0;

    /* 显示当前阶段信息，正在最小化字符 */
    ACTF(cBRI "Stage #3: " cRST "Character minimization...");

    /* 遍历输入数据，尝试将每个字符替换为'0' */
    while (set_pos < in_len) {
        u8  res;
        u32 use_len = MIN(set_len, in_len - set_pos);

        /* 检查当前块内是否所有字符都是'0' */
        for (i = 0; i < use_len; i++)
            if (in_data[set_pos + i] != '0') break;

        /* 如果当前块内不是所有字符都是'0'，则尝试将当前块替换为'0' */
        if (i != use_len) {
            memcpy(tmp_buf, in_data, in_len);
            memset(tmp_buf + set_pos, '0', use_len);

            res = run_target(argv, tmp_buf, in_len, 0);

            /* 如果替换后的数据仍然有效，则更新输入数据 */
            if (res) {
                memset(in_data + set_pos, '0', use_len);
                changed_any = 1;  // 标记发生了变化
                alpha_del0 += use_len;  // 增加被删除的字节数
            }
        }

        set_pos += set_len;  // 移动到下一个块
    }

    /* 更新总共被删除的字节数 */
    alpha_d_total += alpha_del0;

    /* 显示块归一化完成的信息 */
    OKF("Block normalization complete, %u byte%s replaced.", alpha_del0,
        alpha_del0 == 1 ? "" : "s");

    /* 标记下一个最小化过程的开始 */
next_pass:
    ACTF(cYEL "--- " cBRI "Pass #%u " cYEL "---", ++cur_pass);
    changed_any = 0;  // 重置变化标记

    /******************
   * BLOCK DELETION *
   ******************/

   /* 计算要删除的块的初始长度，这是通过输入长度除以TRIM_START_STEPS并向上取到最近的2的幂得到的 */
    del_len = next_p2(in_len / TRIM_START_STEPS);
    /* 保存原始输入长度，用于后续的比较 */
    stage_o_len = in_len;

    /* 显示当前阶段信息，正在删除数据块 */
    ACTF(cBRI "Stage #1: " cRST "Removing blocks of data...");

next_del_blksize:
    /* 如果del_len为0，则设置为1，确保至少尝试删除单个字节 */
    if (!del_len) del_len = 1;
    /* 初始化删除位置和上一次删除标志 */
    del_pos = 0;
    prev_del = 1;

    /* 显示当前块的长度和剩余大小 */
    SAYF(cGRA "    Block length = %u, remaining size = %u\n" cRST,
        del_len, in_len);

    /* 遍历输入数据，尝试删除每个块 */
    while (del_pos < in_len) {
        u8  res;
        s32 tail_len;

        /* 计算当前块之后的剩余长度 */
        tail_len = in_len - del_pos - del_len;
        /* 如果剩余长度为负，则设置为0 */
        if (tail_len < 0) tail_len = 0;

        /*
          如果已经处理了一个完整的块（最初，prev_del == 1），
          并且没有删除前一个块，并且不在缓冲区的末端（tail_len > 0），
          并且当前块与前一个块相同...则跳过这一步作为无操作。
        */
        if (!prev_del && tail_len && !memcmp(in_data + del_pos - del_len,
            in_data + del_pos, del_len)) {
            del_pos += del_len;
            continue;
        }

        /* 重置上一次删除标志 */
        prev_del = 0;

        /* 头部：复制到删除位置之前的输入数据 */
        memcpy(tmp_buf, in_data, del_pos);

        /* 尾部：复制删除位置之后的输入数据 */
        memcpy(tmp_buf + del_pos, in_data + del_pos + del_len, tail_len);

        /* 运行目标程序，检查删除当前块后的数据是否仍然有效 */
        res = run_target(argv, tmp_buf, del_pos + tail_len, 0);

        /* 如果删除当前块后数据仍然有效，则更新输入数据 */
        if (res) {
            memcpy(in_data, tmp_buf, del_pos + tail_len);
            prev_del = 1;
            in_len = del_pos + tail_len; /* 更新输入长度 */

            changed_any = 1; /* 标记发生了变化 */
        }
        else {
            del_pos += del_len; /* 移动到下一个块 */
        }
    }

    /* 如果当前块长度大于1并且输入长度大于0，则减小块长度并重新尝试 */
    if (del_len > 1 && in_len >= 1) {
        del_len /= 2;
        goto next_del_blksize;
    }

    /* 显示块删除完成的信息 */
    OKF("Block removal complete, %u bytes deleted.", stage_o_len - in_len);

    /* 如果输入长度变为0并且发生了变化，则警告用户 */
    if (!in_len && changed_any)
        WARNF(cLRD "Down to zero bytes - check the command line and mem limit!" cRST);

    /* 如果已经是第二次以上遍历并且没有发生变化，则跳转到最终化处理 */
    if (cur_pass > 1 && !changed_any) goto finalize_all;

    /*************************
   * ALPHABET MINIMIZATION *
   *************************/

   /* 初始化变量，用于记录不同字符的数量以及被删除的字符数 */
    alpha_size = 0;
    alpha_del1 = 0;
    syms_removed = 0;

    /* 初始化一个大小为256的数组，用于记录每个字符出现的次数 */
    memset(alpha_map, 0, 256 * sizeof(u32));

    /* 遍历输入数据，统计每个字符出现的次数 */
    for (i = 0; i < in_len; i++) {
        if (!alpha_map[in_data[i]]) alpha_size++;  // 如果字符是第一次出现，alpha_size加1
        alpha_map[in_data[i]]++;  // 记录字符出现次数
    }

    /* 显示当前阶段信息，正在最小化符号（字符） */
    ACTF(cBRI "Stage #2: " cRST "Minimizing symbols (%u code point%s)...",
        alpha_size, alpha_size == 1 ? "" : "s");

    /* 遍历所有可能的字符 */
    for (i = 0; i < 256; i++) {

        u32 r;
        u8 res;

        /* 如果字符是'0'或者没有出现过，则跳过 */
        if (i == '0' || !alpha_map[i]) continue;

        /* 复制当前的输入数据到临时缓冲区 */
        memcpy(tmp_buf, in_data, in_len);

        /* 将当前字符替换为'0' */
        for (r = 0; r < in_len; r++)
            if (tmp_buf[r] == i) tmp_buf[r] = '0';

        /* 运行目标程序，检查替换后的数据是否仍然有效 */
        res = run_target(argv, tmp_buf, in_len, 0);

        /* 如果替换后的数据仍然有效，则更新输入数据 */
        if (res) {
            memcpy(in_data, tmp_buf, in_len);
            syms_removed++;  // 增加被删除的字符数
            alpha_del1 += alpha_map[i];  // 增加被删除的字符所占的字节数
            changed_any = 1;  // 标记发生了变化
        }
    }

    /* 更新总共被删除的字符所占的字节数 */
    alpha_d_total += alpha_del1;

    /* 显示符号最小化完成的信息 */
    OKF("Symbol minimization finished, %u symbol%s (%u byte%s) replaced.",
        syms_removed, syms_removed == 1 ? "" : "s",
        alpha_del1, alpha_del1 == 1 ? "" : "s");

    /**************************
     * CHARACTER MINIMIZATION *
     **************************/

     /* 初始化变量，用于记录被删除的字符数 */
    alpha_del2 = 0;

    /* 显示当前阶段信息，正在最小化字符 */
    ACTF(cBRI "Stage #3: " cRST "Character minimization...");

    /* 复制当前的输入数据到临时缓冲区 */
    memcpy(tmp_buf, in_data, in_len);

    /* 遍历输入数据，尝试将每个字符替换为'0' */
    for (i = 0; i < in_len; i++) {

        u8 res, orig = tmp_buf[i];

        /* 如果字符已经是'0'，则跳过 */
        if (orig == '0') continue;
        /* 将当前字符替换为'0' */
        tmp_buf[i] = '0';

        /* 运行目标程序，检查替换后的数据是否仍然有效 */
        res = run_target(argv, tmp_buf, in_len, 0);

        /* 如果替换后的数据仍然有效，则更新输入数据 */
        if (res) {
            in_data[i] = '0';  // 更新输入数据
            alpha_del2++;  // 增加被删除的字符数
            changed_any = 1;  // 标记发生了变化
        }
        else {
            tmp_buf[i] = orig;  // 恢复原始数据，以便下一次尝试
        }
    }

    /* 更新总共被删除的字符所占的字节数 */
    alpha_d_total += alpha_del2;

    /* 显示字符最小化完成的信息 */
    OKF("Character minimization done, %u byte%s replaced.",
        alpha_del2, alpha_del2 == 1 ? "" : "s");

    /* 如果发生了变化，则重新开始新一轮的最小化过程 */
    if (changed_any) goto next_pass;

finalize_all:
    /* 显示最小化过程的总结信息 */
    SAYF("\n"
        cGRA "     File size reduced by : " cRST "%0.02f%% (to %u byte%s)\n"
        cGRA "    Characters simplified : " cRST "%0.02f%%\n"
        cGRA "     Number of execs done : " cRST "%u\n"
        cGRA "          Fruitless execs : " cRST "path=%u crash=%u hang=%s%u\n\n",
        100 - ((double)in_len) * 100 / orig_len, in_len, in_len == 1 ? "" : "s",
        ((double)(alpha_d_total)) * 100 / (in_len ? in_len : 1),
        total_execs, missed_paths, missed_crashes, missed_hangs ? cLRD : "",
        missed_hangs);

    /* 如果执行次数超过50次且无结果的执行次数超过总执行次数的10%，则警告用户 */
    if (total_execs > 50 && missed_hangs * 10 > total_execs)
        WARNF(cLRD "Frequent timeouts - results may be skewed." cRST);



    /* 处理Ctrl-C等中断信号 */
    static void handle_stop_sig(int sig) {
        stop_soon = 1;  // 设置停止标志，表示即将停止执行
        if (child_pid > 0) kill(child_pid, SIGKILL);  // 如果子进程存在，发送SIGKILL信号强制终止
    }

    /* 进行基本的准备工作 - 持久的文件描述符、文件名等 */
    static void set_up_environment(void) {
        u8* x;

        // 打开/dev/null，用于重定向不需要的输出
        dev_null_fd = open("/dev/null", O_RDWR);
        if (dev_null_fd < 0) PFATAL("Unable to open /dev/null");

        // 如果prog_in未设置，即未指定目标程序输入文件，则使用当前目录或TMPDIR环境变量指定的目录
        if (!prog_in) {
            u8* use_dir = ".";
            if (access(use_dir, R_OK | W_OK | X_OK)) {
                use_dir = getenv("TMPDIR");
                if (!use_dir) use_dir = "/tmp";
            }
            prog_in = alloc_printf("%s/.afl-tmin-temp-%u", use_dir, getpid());  // 生成临时文件名
        }

        // 设置ASAN_OPTIONS环境变量的默认值，确保在地址_SANITIZER启用时，程序在错误发生时终止，不进行符号化
        x = getenv("ASAN_OPTIONS");
        if (x) {
            if (!strstr(x, "abort_on_error=1"))
                FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!");
            if (!strstr(x, "symbolize=0"))
                FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!");
        }
        setenv("ASAN_OPTIONS", "abort_on_error=1:detect_leaks=0:symbolize=0:allocator_may_return_null=1", 0);

        // 设置MSAN_OPTIONS环境变量的默认值，确保在内存_SANITIZER启用时，程序在错误发生时终止，不进行符号化
        x = getenv("MSAN_OPTIONS");
        if (x) {
            if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR)))
                FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY(MSAN_ERROR) " - please fix!");
            if (!strstr(x, "symbolize=0"))
                FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!");
        }
        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环境变量，则将其值设置到LD_PRELOAD和DYLD_INSERT_LIBRARIES环境变量中
        if (getenv("AFL_PRELOAD")) {
            setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
            setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
        }
    }

    /* 设置信号处理程序 */
    static void setup_signal_handlers(void) {
        struct sigaction sa;

        // 初始化sigaction结构体
        sa.sa_handler = NULL;
        sa.sa_flags = SA_RESTART;
        sa.sa_sigaction = NULL;
        sigemptyset(&sa.sa_mask);

        // 设置多种停止信号的处理程序为handle_stop_sig
        sa.sa_handler = handle_stop_sig;
        sigaction(SIGHUP, &sa, NULL);
        sigaction(SIGINT, &sa, NULL);
        sigaction(SIGTERM, &sa, NULL);

        // 设置执行超时信号的处理程序为handle_timeout
        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;

                // 如果prog_in是绝对路径，则直接使用，否则使用当前工作目录拼接
                if (prog_in[0] == '/') aa_subst = prog_in;
                else aa_subst = alloc_printf("%s/%s", cwd, prog_in);

                // 构造替换后的参数值
                *aa_loc = 0;
                n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2);
                argv[i] = n_arg;
                *aa_loc = '@';

                if (prog_in[0] != '/') ck_free(aa_subst);  // 如果prog_in不是绝对路径，则释放临时字符串
            }

            i++;
        }

        free(cwd);  // 释放当前工作目录字符串
    }


/* Display usage hints. */

static void usage(u8* argv0) {

  SAYF("\n%s [ options ] -- /path/to/target_app [ ... ]\n\n"

       "Required parameters:\n\n"

       "  -i file       - input test case to be shrunk by the tool\n"
       "  -o file       - final output location for the minimized data\n\n"

       "Execution control settings:\n\n"

       "  -f file       - input file read by the tested program (stdin)\n"
       "  -t msec       - timeout for each run (%u ms)\n"
       "  -m megs       - memory limit for child process (%u MB)\n"
       "  -Q            - use binary-only instrumentation (QEMU mode)\n\n"

       "Minimization settings:\n\n"

       "  -e            - solve for edge coverage only, ignore hit counts\n"
       "  -x            - treat non-zero exit codes as crashes\n\n"

       "Other stuff:\n\n"

       "  -V            - show version number and exit\n\n"

       "For additional tips, please consult %s/README.\n\n",

       argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path);

  exit(1);

}


/* 查找二进制文件 */
static void find_binary(u8* fname) {
    u8* env_path = 0;
    struct stat st;

    /* 如果文件名中包含 '/' 或者环境变量 PATH 未设置，则假定 fname 为二进制文件的绝对路径 */
    if (strchr(fname, '/') || !(env_path = getenv("PATH"))) {
        target_path = ck_strdup(fname);  // 复制文件名

        /* 检查目标路径是否存在，是否为常规文件，是否有执行权限，以及文件大小是否至少为4字节 */
        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 {
        /* 遍历环境变量 PATH 中的每个目录，查找 fname 指定的二进制文件 */
        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;

            /* 构造完整的文件路径 */
            if (cur_elem[0])
                target_path = alloc_printf("%s/%s", cur_elem, fname);
            else
                target_path = ck_strdup(fname);

            ck_free(cur_elem);

            /* 检查构造的路径是否存在，是否为常规文件，是否有执行权限，以及文件大小是否至少为4字节 */
            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);
    }
}

/* 为QEMU设置argv */
static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
    char** new_argv = ck_alloc(sizeof(char*) * (argc + 4));  // 分配新的argv数组
    u8* tmp, * cp, * rsl, * own_copy;

    /* 为QEMU设置环境变量，避免稳定性问题 */
    setenv("QEMU_LOG", "nochain", 1);

    /* 复制原始argv的内容到新argv */
    memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc);

    /* 查找afl-qemu-trace程序的路径 */
    new_argv[2] = target_path;
    new_argv[1] = "--";

    tmp = getenv("AFL_PATH");

    if (tmp) {
        /* 如果设置了AFL_PATH环境变量，则使用该路径下的afl-qemu-trace */
        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_copy = ck_strdup(own_loc);
    rsl = strrchr(own_copy, '/');

    if (rsl) {
        /* 构造afl-qemu-trace的路径 */
        *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'.");
}

/* 从文件读取掩码位图，用于-B选项 */
static void read_bitmap(u8* fname) {
    s32 fd = open(fname, O_RDONLY);  // 以只读方式打开文件

    if (fd < 0) PFATAL("Unable to open '%s'", fname);  // 如果打开文件失败，则报错

    ck_read(fd, mask_bitmap, MAP_SIZE, fname);  // 从文件中读取掩码位图

    close(fd);  // 关闭文件描述符
}



/* 主入口点 */

int main(int argc, char** argv) {
    s32 opt;
    u8  mem_limit_given = 0, timeout_given = 0, qemu_mode = 0;
    char** use_argv;

    // 根据DOC_PATH文件是否存在，设置文档路径
    doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;

    // 打印欢迎信息，显示版本号和作者邮箱
    SAYF(cCYA "afl-tmin " cBRI VERSION cRST " by <lcamtuf@google.com>\n");

    // 使用getopt函数解析命令行参数
    while ((opt = getopt(argc, argv, "+i:o:f:m:t:B:xeQV")) > 0) {
        switch (opt) {
        case 'i':
            // 如果已经设置了输入文件，则报错
            if (in_file) FATAL("Multiple -i options not supported");
            in_file = optarg; // 设置输入文件路径
            break;

        case 'o':
            // 如果已经设置了输出文件，则报错
            if (out_file) FATAL("Multiple -o options not supported");
            out_file = optarg; // 设置输出文件路径
            break;

        case 'f':
            // 如果已经设置了程序输入文件，则报错
            if (prog_in) FATAL("Multiple -f options not supported");
            use_stdin = 0; // 不使用标准输入
            prog_in = optarg; // 设置程序输入文件路径
            break;

        case 'e':
            // 如果已经设置了边缘覆盖模式，则报错
            if (edges_only) FATAL("Multiple -e options not supported");
            edges_only = 1; // 设置为边缘覆盖模式
            break;

        case 'x':
            // 如果已经设置了非零退出代码视为崩溃，则报错
            if (exit_crash) FATAL("Multiple -x options not supported");
            exit_crash = 1; // 设置非零退出代码视为崩溃
            break;

        case 'm':
            // 如果已经设置了内存限制，则报错
            if (mem_limit_given) FATAL("Multiple -m options not supported");
            mem_limit_given = 1; // 标记内存限制已设置

            // 解析内存限制参数，支持K、M、G和T后缀
            if (!strcmp(optarg, "none")) {
                mem_limit = 0; // 如果参数为"none"，则不限制内存
                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; // T后缀，转换为MB
            case 'G': mem_limit *= 1024; break; // G后缀，转换为MB
            case 'k': mem_limit /= 1024; break; // k后缀，转换为MB
            case 'M': break; // M后缀，无需转换
            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"); // 32位系统内存限制过高
            break;

        case 't':
            // 如果已经设置了超时时间，则报错
            if (timeout_given) FATAL("Multiple -t options not supported");
            timeout_given = 1; // 标记超时时间已设置

            exec_tmout = atoi(optarg); // 设置执行超时时间

            if (exec_tmout < 10 || optarg[0] == '-')
                FATAL("Dangerously low value of -t"); // 超时时间过低或格式错误
            break;

        case 'Q':
            // 如果已经设置了QEMU模式，则报错
            if (qemu_mode) FATAL("Multiple -Q options not supported");
            if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; // 如果未设置内存限制，则使用默认值

            qemu_mode = 1; // 启用QEMU模式
            break;
        }
    }

    // ...（后续代码省略）
}

      case 'B': /* load bitmap */

        /* This is a secret undocumented option! It is speculated to be useful
           if you have a baseline "boring" input file and another "interesting"
           file you want to minimize.

           You can dump a binary bitmap for the boring file using
           afl-showmap -b, and then load it into afl-tmin via -B. The minimizer
           will then minimize to preserve only the edges that are unique to
           the interesting input file, but ignoring everything from the
           original map.

           The option may be extended and made more official if it proves
           to be useful. */

        if (mask_bitmap) FATAL("Multiple -B options not supported");
        mask_bitmap = ck_alloc(MAP_SIZE);
        read_bitmap(optarg);
        break;

      case 'V': /* Show version number */

        /* Version number has been printed already, just quit. */
        exit(0);

      default:

        usage(argv[0]);

    }

    // 检查是否有足够的命令行参数，以及输入和输出文件是否已指定，如果没有，则显示用法信息
    if (optind == argc || !in_file || !out_file) usage(argv[0]);

    // 设置共享内存
    setup_shm();
    // 设置信号处理程序
    setup_signal_handlers();
    // 设置环境变量和文件描述符
    set_up_environment();

    // 查找目标二进制文件的路径
    find_binary(argv[optind]);
    // 检测并替换参数中的@@为实际文件路径
    detect_file_args(argv + optind);

    // 如果启用了QEMU模式，则获取QEMU模式下的参数
    if (qemu_mode)
        use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
    else
        use_argv = argv + optind; // 否则使用原始参数

    // 根据环境变量AFL_TMIN_EXACT设置精确模式
    exact_mode = !!getenv("AFL_TMIN_EXACT");

    // 打印换行符
    SAYF("\n");

    // 读取初始文件
    read_initial_file();

    // 执行干运行（不实际执行目标程序，仅检查设置）
    ACTF("Performing dry run (mem limit = %llu MB, timeout = %u ms%s)...",
        mem_limit, exec_tmout, edges_only ? ", edges only" : "");

    // 运行目标程序
    run_target(use_argv, in_data, in_len, 1);

    // 如果子进程超时，则报错
    if (child_timed_out)
        FATAL("Target binary times out (adjusting -t may help).");

    // 如果程序正常终止，则在"instrumented"模式下进行最小化
    if (!crash_mode) {
        OKF("Program terminates normally, minimizing in "
            cCYA "instrumented" cRST " mode.");

        // 如果没有检测到任何仪器数据，则报错
        if (!anything_set()) FATAL("No instrumentation detected.");
    }
    else {
        // 如果程序以信号退出，则在"crash"模式下进行最小化
        OKF("Program exits with a signal, minimizing in " cMGN "%scrash" cRST
            " mode.", exact_mode ? "EXACT " : "");
    }

    // 执行最小化操作
    minimize(use_argv);

    // 将最小化后的结果写入输出文件
    ACTF("Writing output to '%s'...", out_file);

    // 删除临时文件
    unlink(prog_in);
    prog_in = NULL;

    // 将最小化后的数据写入输出文件，并关闭文件描述符
    close(write_to_file(out_file, in_data, in_len));

    // 打印完成信息
    OKF("We're done here. Have a nice day!\n");

    // 正常退出程序
    exit(0);

