|  |  |  | @ -54,31 +54,31 @@ | 
			
		
	
		
			
				
					|  |  |  |  | #include <sys/types.h> | 
			
		
	
		
			
				
					|  |  |  |  | #include <sys/resource.h> | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 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,105 @@ 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"); /* 附加失败时退出 */ | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <<<<<<< HEAD | 
			
		
	
		
			
				
					|  |  |  |  | /* 写入测试结果 */ | 
			
		
	
		
			
				
					|  |  |  |  | ======= | 
			
		
	
		
			
				
					|  |  |  |  | /* 配置共享内存。 */ | 
			
		
	
		
			
				
					|  |  |  |  | >>>>>>> 31b9bf56f4cc249d8b6bb414203bff28d8cf724a | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 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,45 +246,54 @@ static u32 write_results(void) { | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <<<<<<< HEAD | 
			
		
	
		
			
				
					|  |  |  |  | /*  处理超时信号 */ | 
			
		
	
		
			
				
					|  |  |  |  | ======= | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | /* 处理超时信号。 */ | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | >>>>>>> 31b9bf56f4cc249d8b6bb414203bff28d8cf724a | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 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); /* 强制杀死子进程 */ | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <<<<<<< HEAD | 
			
		
	
		
			
				
					|  |  |  |  | /* 执行目标程序 */ | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ======= | 
			
		
	
		
			
				
					|  |  |  |  | /* 执行目标应用程序。 */ | 
			
		
	
		
			
				
					|  |  |  |  |  /* 运行目标程序并等待其结束。 */ | 
			
		
	
		
			
				
					|  |  |  |  |   /* 处理超时和信号。 */ | 
			
		
	
		
			
				
					|  |  |  |  | >>>>>>> 31b9bf56f4cc249d8b6bb414203bff28d8cf724a | 
			
		
	
		
			
				
					|  |  |  |  | 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"); | 
			
		
	
		
			
				
					|  |  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -281,35 +301,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); /* Ignore errors */ | 
			
		
	
		
			
				
					|  |  |  |  |     setrlimit(RLIMIT_CORE, &r); /* 设置 core dump 限制,忽略错误 */ | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     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);/* 终止子进程 */ | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -323,9 +343,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; | 
			
		
	
	
		
			
				
					|  |  |  | @ -341,7 +361,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"); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -365,11 +385,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);/* 强制杀死子进程 */ | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -377,7 +397,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:" | 
			
		
	
	
		
			
				
					|  |  |  | @ -388,7 +408,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); | 
			
		
	
	
		
			
				
					|  |  |  | @ -410,15 +430,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); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
	
		
			
				
					|  |  |  | @ -429,14 +449,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) { | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -446,13 +467,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 = '@'; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -469,8 +490,13 @@ static void detect_file_args(char** argv) { | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <<<<<<< HEAD | 
			
		
	
		
			
				
					|  |  |  |  | /* Show banner. */ | 
			
		
	
		
			
				
					|  |  |  |  | // 打印工具的名称和版本信息。
 | 
			
		
	
		
			
				
					|  |  |  |  | ======= | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | >>>>>>> 31b9bf56f4cc249d8b6bb414203bff28d8cf724a | 
			
		
	
		
			
				
					|  |  |  |  | static void show_banner(void) { | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   SAYF(cCYA "afl-showmap " cBRI VERSION cRST " by <lcamtuf@google.com>\n"); | 
			
		
	
	
		
			
				
					|  |  |  | @ -482,7 +508,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" | 
			
		
	
	
		
			
				
					|  |  |  | @ -496,7 +522,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" | 
			
		
	
	
		
			
				
					|  |  |  | @ -512,18 +538,23 @@ static void usage(u8* argv0) { | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <<<<<<< 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); | 
			
		
	
	
		
			
				
					|  |  |  | @ -543,14 +574,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; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -558,7 +589,7 @@ static void find_binary(u8* fname) { | 
			
		
	
		
			
				
					|  |  |  |  |       target_path = 0; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // 如果遍历完所有路径后仍未找到目标文件,则程序退出。
 | 
			
		
	
		
			
				
					|  |  |  |  |     if (!target_path) FATAL("Program '%s' not found or not executable", fname); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
	
		
			
				
					|  |  |  | @ -566,26 +597,33 @@ static void find_binary(u8* 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; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   /* 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) { | 
			
		
	
	
		
			
				
					|  |  |  | @ -599,7 +637,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, '/'); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -618,14 +656,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'."); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
	
		
			
				
					|  |  |  | @ -635,24 +673,31 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { | 
			
		
	
		
			
				
					|  |  |  |  | /*解析命令行参数并执行目标程序 */ | 
			
		
	
		
			
				
					|  |  |  |  | /*根据参数执行不同的操作 */ | 
			
		
	
		
			
				
					|  |  |  |  | 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'; | 
			
		
	
	
		
			
				
					|  |  |  | @ -689,7 +734,7 @@ int main(int argc, char** argv) { | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         break; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       // -t:设置超时时间。
 | 
			
		
	
		
			
				
					|  |  |  |  |       case 't': | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (timeout_given) FATAL("Multiple -t options not supported"); | 
			
		
	
	
		
			
				
					|  |  |  | @ -704,19 +749,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
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -725,13 +770,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"); | 
			
		
	
	
		
			
				
					|  |  |  | @ -739,7 +784,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
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -747,13 +792,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(); | 
			
		
	
	
		
			
				
					|  |  |  | @ -765,39 +810,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。
 | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | 
 |