diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..47fce04 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "windows-gcc-x86", + "includePath": [ + "${workspaceFolder}/**" + ], + "compilerPath": "C:/Program Files/MinGW/bin/gcc.exe", + "cStandard": "${default}", + "cppStandard": "${default}", + "intelliSenseMode": "windows-gcc-x86", + "compilerArgs": [ + "" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/docs/2024秋软件工程课程报告一:AFL泛读报告.docx b/docs/2024秋软件工程课程报告:AFL泛读、标注和维护报告文档.docx similarity index 100% rename from docs/2024秋软件工程课程报告一:AFL泛读报告.docx rename to docs/2024秋软件工程课程报告:AFL泛读、标注和维护报告文档.docx diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 4af6518..bce5104 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -54,31 +54,31 @@ #include #include -static s32 child_pid; /* PID of the tested program */ +static s32 child_pid; /* 被测试程序的进程 PID */ -static u8* trace_bits; /* SHM with instrumentation bitmap */ +static u8* trace_bits; /* 用于存储插桩信息的共享内存 */ -static u8 *out_file, /* Trace output file */ - *doc_path, /* Path to docs */ - *target_path, /* Path to target binary */ - *at_file; /* Substitution string for @@ */ +static u8 *out_file, /* 输出文件路径 */ + *doc_path, /* 文档目录路径 */ + *target_path, /* 被测目标程序的路径 */ + *at_file; /* `@@` 的替换字符串,用于输入文件 */ -static u32 exec_tmout; /* Exec timeout (ms) */ +static u32 exec_tmout; /* 测试程序的执行超时时间 (毫秒) */ -static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */ +static u64 mem_limit = MEM_LIMIT; /* 测试程序的内存限制 (MB) */ -static s32 shm_id; /* ID of the SHM region */ +static s32 shm_id; /* 共享内存 (SHM) 的标识符 */ -static u8 quiet_mode, /* Hide non-essential messages? */ - edges_only, /* Ignore hit counts? */ - cmin_mode, /* Generate output in afl-cmin mode? */ - binary_mode, /* Write output as a binary map */ - keep_cores; /* Allow coredumps? */ +static u8 quiet_mode, /* 是否隐藏非必要的信息 */ + edges_only, /* 是否只关心边覆盖率 */ + cmin_mode, /* 是否以 `afl-cmin` 模式生成输出 */ + binary_mode, /* 是否以二进制方式输出插桩信息 */ + keep_cores; /* A是否允许生成 core dump 文件 */ static volatile u8 - stop_soon, /* Ctrl-C pressed? */ - child_timed_out, /* Child timed out? */ - child_crashed; /* Child crashed? */ + 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. */ @@ -95,7 +95,7 @@ static const u8 count_class_human[256] = { [32 ... 127] = 7, [128 ... 255] = 8 -}; +}; // 映射插桩命中计数到 1-8 的更具可读性的区间。0 代表未命中;1-3 被保留为原值;其余区间依次分段映射,便于统计分析。 static const u8 count_class_binary[256] = { @@ -109,23 +109,24 @@ static const u8 count_class_binary[256] = { [32 ... 127] = 64, [128 ... 255] = 128 -}; +}; // 将插桩计数按指数方式映射到二进制值,便于程序进一步处理。 -static void classify_counts(u8* mem, const u8* map) { +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; + if (*mem) *mem = 1;// 只关心边的覆盖情况,命中计数直接归一 mem++; } } else { while (i--) { - *mem = map[*mem]; + *mem = map[*mem];// 使用提供的映射表对命中计数进行分类 mem++; } @@ -134,95 +135,101 @@ static void classify_counts(u8* mem, const u8* map) { } -/* Get rid of shared memory (atexit handler). */ +/* 删除共享内存 (atexit 处理程序). */ -static void remove_shm(void) { +static void remove_shm(void) {//释放分配的共享内存。 - shmctl(shm_id, IPC_RMID, NULL); + shmctl(shm_id, IPC_RMID, NULL); //使用 shmctl 函数删除共享内存区域 + //IPC_RMID 标志用于标记共享内存为 "移除",在无其他进程访问后会自动释放。 + //通过 atexit 注册,在程序退出时自动调用,确保清理资源。 } -/* Configure shared memory. */ +/* 配置共享内存 */ -static void setup_shm(void) { +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"); + if (shm_id < 0) PFATAL("shmget() failed");/* 创建失败时输出错误并退出 */ - atexit(remove_shm); + atexit(remove_shm);/* 注册 atexit 处理程序以在程序退出时删除共享内存 */ + /* 将共享内存 ID 转换为字符串并设置环境变量 */ shm_str = alloc_printf("%d", shm_id); setenv(SHM_ENV_VAR, shm_str, 1); - ck_free(shm_str); + ck_free(shm_str);/* 释放临时字符串内存 */ + /* 将共享内存附加到当前进程的地址空间 */ trace_bits = shmat(shm_id, NULL, 0); - if (trace_bits == (void *)-1) PFATAL("shmat() failed"); + if (trace_bits == (void *)-1) PFATAL("shmat() failed"); /* 附加失败时退出 */ } -/* Write results. */ +/* 写入测试结果 */ -static u32 write_results(void) { +static u32 write_results(void) { //将插桩结果输出到文件,支持二进制和文本模式。 - s32 fd; - u32 i, ret = 0; + s32 fd; /* 文件描述符 */ + u32 i, ret = 0; /* 计数器和返回值 */ - u8 cco = !!getenv("AFL_CMIN_CRASHES_ONLY"), - caa = !!getenv("AFL_CMIN_ALLOW_ANY"); + u8 cco = !!getenv("AFL_CMIN_CRASHES_ONLY"), /* 仅输出崩溃路径? */ + caa = !!getenv("AFL_CMIN_ALLOW_ANY"); /* 是否允许所有路径? */ - if (!strncmp(out_file, "/dev/", 5)) { + /* 根据输出文件路径的类型处理 */ + 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, "-")) { + } else if (!strcmp(out_file, "-")) { /* 如果是标准输出 */ - fd = dup(1); + fd = dup(1); /* 复制标准输出描述符 */ if (fd < 0) PFATAL("Unable to open stdout"); - } else { + } else { /* 普通文件路径 */ - unlink(out_file); /* Ignore errors */ + 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++; + if (trace_bits[i]) ret++;/* 统计非零插桩计数 */ - ck_write(fd, trace_bits, MAP_SIZE, out_file); + ck_write(fd, trace_bits, MAP_SIZE, out_file);/* 将插桩数据写入文件 */ close(fd); - } else { + } else { /* 文本模式输出 */ FILE* f = fdopen(fd, "w"); if (!f) PFATAL("fdopen() failed"); + /* 遍历插桩数据,按文本格式输出 */ for (i = 0; i < MAP_SIZE; i++) { - if (!trace_bits[i]) continue; + if (!trace_bits[i]) continue;/* 跳过空条目 */ ret++; - if (cmin_mode) { + if (cmin_mode) {/* afl-cmin 模式 */ - if (child_timed_out) break; - if (!caa && child_crashed != cco) break; + 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]); + } else fprintf(f, "%06u:%u\n", i, trace_bits[i]);/* 标准格式 */ } @@ -235,42 +242,42 @@ static u32 write_results(void) { } -/* Handle timeout signal. */ +/* 处理超时信号 */ static void handle_timeout(int sig) { - child_timed_out = 1; - if (child_pid > 0) kill(child_pid, SIGKILL); + child_timed_out = 1; /* 设置子进程超时标志 */ + if (child_pid > 0) kill(child_pid, SIGKILL); /* 强制杀死子进程 */ } -/* Execute target application. */ +/* 执行目标程序 */ static void run_target(char** argv) { - static struct itimerval it; + static struct itimerval it;/* 定时器结构 */ int status = 0; if (!quiet_mode) - SAYF("-- Program output begins --\n" cRST); + SAYF("-- Program output begins --\n" cRST);/* 输出调试信息 */ - MEM_BARRIER(); + MEM_BARRIER(); /* 防止编译器重排内存操作 */ - child_pid = fork(); + child_pid = fork();/* 创建子进程 */ - if (child_pid < 0) PFATAL("fork() failed"); + if (child_pid < 0) PFATAL("fork() failed");/* 如果 fork 失败,退出程序 */ - if (!child_pid) { + if (!child_pid) { /* 子进程逻辑 */ struct rlimit r; - if (quiet_mode) { + 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; + *(u32*)trace_bits = EXEC_FAIL_SIG; /* 设置错误标志 */ PFATAL("Descriptor initialization failed"); } @@ -278,35 +285,35 @@ static void run_target(char** argv) { } - if (mem_limit) { + if (mem_limit) {/* 设置子进程内存限制 */ r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20; #ifdef RLIMIT_AS - setrlimit(RLIMIT_AS, &r); /* Ignore errors */ + setrlimit(RLIMIT_AS, &r); /* 设置内存限制 (按地址空间限制),忽略错误 */ #else - setrlimit(RLIMIT_DATA, &r); /* Ignore errors */ - + 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 */ - if (!keep_cores) r.rlim_max = r.rlim_cur = 0; - else r.rlim_max = r.rlim_cur = RLIM_INFINITY; + setrlimit(RLIMIT_CORE, &r); /* 设置 core dump 限制,忽略错误 */ - setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ + if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0); // 强制目标程序加载动态库时立即绑定符号,避免延迟加载可能引发的问题。 - if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0); + setsid(); /* 创建一个新会话,隔离子进程 */ - setsid(); + execv(target_path, argv);/* 替换当前进程镜像为目标程序 */ - execv(target_path, argv); - - *(u32*)trace_bits = EXEC_FAIL_SIG; - exit(0); + *(u32*)trace_bits = EXEC_FAIL_SIG; /* 如果 execv 返回,则标记执行失败 */ + exit(0);/* 终止子进程 */ } @@ -320,9 +327,9 @@ static void run_target(char** argv) { } - setitimer(ITIMER_REAL, &it, NULL); + setitimer(ITIMER_REAL, &it, NULL);/* 配置超时定时器 */ - if (waitpid(child_pid, &status, 0) <= 0) FATAL("waitpid() failed"); + if (waitpid(child_pid, &status, 0) <= 0) FATAL("waitpid() failed"); /* 等待子进程结束 */ child_pid = 0; it.it_value.tv_sec = 0; @@ -338,7 +345,7 @@ static void run_target(char** argv) { classify_counts(trace_bits, binary_mode ? count_class_binary : count_class_human); - +/* 将 trace_bits 中的插桩命中数据分类为二进制或人类可读格式 */ if (!quiet_mode) SAYF(cRST "-- Program output ends --\n"); @@ -362,11 +369,11 @@ static void run_target(char** argv) { /* Handle Ctrl-C and the like. */ -static void handle_stop_sig(int sig) { +static void handle_stop_sig(int sig) {//捕获停止信号 (SIGHUP, SIGINT, SIGTERM) 并设置全局停止标志。终止子进程以确保程序退出。 - stop_soon = 1; + stop_soon = 1;/* 设置标志,通知主程序停止 */ - if (child_pid > 0) kill(child_pid, SIGKILL); + if (child_pid > 0) kill(child_pid, SIGKILL);/* 强制杀死子进程 */ } @@ -374,7 +381,7 @@ static void handle_stop_sig(int sig) { /* 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:" @@ -385,7 +392,7 @@ static void set_up_environment(void) { "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); @@ -407,15 +414,15 @@ static void setup_signal_handlers(void) { sigemptyset(&sa.sa_mask); /* Various ways of saying "stop". */ - - sa.sa_handler = handle_stop_sig; + // 为停止信号 (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; + sa.sa_handler = handle_timeout;/* 设置超时信号的处理函数 */ sigaction(SIGALRM, &sa, NULL); } @@ -425,14 +432,15 @@ static void setup_signal_handlers(void) { static void detect_file_args(char** argv) { + // 获取当前工作目录,用于构造绝对路径。 u32 i = 0; - u8* cwd = getcwd(NULL, 0); + u8* cwd = getcwd(NULL, 0);/* 获取当前工作目录 */ if (!cwd) PFATAL("getcwd() failed"); while (argv[i]) { - u8* aa_loc = strstr(argv[i], "@@"); + u8* aa_loc = strstr(argv[i], "@@");/* 查找命令行参数中的 @@ */ if (aa_loc) { @@ -442,13 +450,13 @@ static void detect_file_args(char** argv) { /* 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); + 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); + n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2);/* 替换 @@ */ argv[i] = n_arg; *aa_loc = '@'; @@ -466,7 +474,7 @@ static void detect_file_args(char** argv) { /* Show banner. */ - +// 打印工具的名称和版本信息。 static void show_banner(void) { SAYF(cCYA "afl-showmap " cBRI VERSION cRST " by \n"); @@ -478,7 +486,7 @@ static void show_banner(void) { static void usage(u8* argv0) { show_banner(); - + // 显示工具的用法说明,包括参数和选项说明。 SAYF("\n%s [ options ] -- /path/to/target_app [ ... ]\n\n" "Required parameters:\n\n" @@ -492,7 +500,7 @@ static void usage(u8* argv0) { " -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" @@ -509,16 +517,16 @@ static void usage(u8* argv0) { /* Find binary. */ - +// 找到目标程序的可执行文件路径 (target_path)。 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); @@ -538,14 +546,14 @@ static void find_binary(u8* fname) { } 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; @@ -553,7 +561,7 @@ static void find_binary(u8* fname) { target_path = 0; } - + // 如果遍历完所有路径后仍未找到目标文件,则程序退出。 if (!target_path) FATAL("Program '%s' not found or not executable", fname); } @@ -562,23 +570,25 @@ static void find_binary(u8* fname) { /* Fix up argv for QEMU. */ - +// 为在 QEMU 模式下执行目标程序,调整参数列表 argv。 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; - /* Workaround for a QEMU stability glitch. */ + /* 设置 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) { @@ -592,7 +602,7 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { return new_argv; } - + // 使用当前程序的路径 own_loc 尝试找到 afl-qemu-trace。 own_copy = ck_strdup(own_loc); rsl = strrchr(own_copy, '/'); @@ -611,14 +621,14 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { } } 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'."); } @@ -627,24 +637,31 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { /* 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'; @@ -681,7 +698,7 @@ int main(int argc, char** argv) { } break; - + // -t:设置超时时间。 case 't': if (timeout_given) FATAL("Multiple -t options not supported"); @@ -696,19 +713,19 @@ int main(int argc, char** argv) { } 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 @@ -717,13 +734,13 @@ int main(int argc, char** argv) { 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"); @@ -731,7 +748,7 @@ int main(int argc, char** argv) { qemu_mode = 1; break; - + // -b:启用二进制输出模式,设置全局变量 binary_mode = 1。 case 'b': /* Secret undocumented mode. Writes output in raw binary format @@ -739,13 +756,13 @@ int main(int argc, char** argv) { 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(); @@ -757,39 +774,40 @@ int main(int argc, char** argv) { } - if (optind == argc || !out_file) usage(argv[0]); + if (optind == argc || !out_file) usage(argv[0]); //如果未指定目标程序路径或输出文件,则显示用法说明并退出。 - setup_shm(); - setup_signal_handlers(); + setup_shm();// 调用 setup_shm 函数,配置用于插桩数据存储的共享内存。 + setup_signal_handlers();// 注册信号处理函数,处理程序停止(SIGINT、SIGTERM)或超时(SIGALRM)等事件。 - set_up_environment(); + set_up_environment();// 配置环境变量,例如 ASAN_OPTIONS 和 MSAN_OPTIONS。 - find_binary(argv[optind]); + find_binary(argv[optind]);//使用 find_binary 函数查找目标程序的可执行文件路径,并存储到全局变量 target_path。 - if (!quiet_mode) { + + if (!quiet_mode) {// 如果未启用静默模式,显示工具信息和目标程序路径。 show_banner(); ACTF("Executing '%s'...\n", target_path); } - detect_file_args(argv + optind); + detect_file_args(argv + optind); //检测命令行参数中的 @@ 并将其替换为指定的文件路径。 - if (qemu_mode) + if (qemu_mode) //如果启用了 QEMU 模式,调用 get_qemu_argv 函数为 QEMU 准备参数列表。 use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind); - else + else // 否则直接使用原始参数。 use_argv = argv + optind; run_target(use_argv); - tcnt = write_results(); + 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); + 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。 }