main^2
em0 8 months ago
parent 8f017a1f4c
commit 7f8464c6db

@ -47,79 +47,73 @@
#include <stdlib.h>
#include <string.h>
static u8 *as_path; /*AFL“as”包装器的路径*/
static u8 **cc_params; /*传递给真实CC的参数*/
static u32 cc_par_cnt = 1; /*参数计数包括argv0*/
static u8 be_quiet, /*静音模式*/
clang_mode; /*被称为afl clang模式**/
static u8 *as_path; /* AFL“as”包装器的路径 */
static u8 **cc_params; /* 传递给真实C编译器的参数 */
static u32 cc_par_cnt = 1; /* 参数计数包括argv0 */
static u8 be_quiet, /* 静音模式 */
clang_mode; /* 被称为afl clang模式 */
/* 尝试在 AFL_PATH 或从 argv[0] 派生的位置找到我们的“假”GNU 汇编器。
*/
static void find_as(u8 *argv0)
{
u8 *afl_path = getenv("AFL_PATH");
u8 *afl_path = getenv("AFL_PATH"); // 获取环境变量AFL_PATH的值
u8 *slash, *tmp;
if (afl_path)
if (afl_path) // 如果AFL_PATH环境变量存在
{
tmp = alloc_printf("%s/as", afl_path); // 构造路径字符串
tmp = alloc_printf("%s/as", afl_path);
if (!access(tmp, X_OK))
if (!access(tmp, X_OK)) // 检查路径是否存在且可执行
{
as_path = afl_path;
ck_free(tmp);
as_path = afl_path; // 设置as_path为找到的路径
ck_free(tmp); // 释放临时字符串
return;
}
ck_free(tmp);
ck_free(tmp); // 如果不可执行,释放临时字符串
}
slash = strrchr(argv0, '/');
slash = strrchr(argv0, '/'); // 在argv0中查找最后一个'/',以获取目录部分
if (slash)
if (slash) // 如果找到了'/'说明argv0是一个路径
{
u8 *dir;
*slash = 0;
dir = ck_strdup(argv0);
*slash = '/';
*slash = 0; // 暂时将'/'替换为'\0',以便截取目录部分
dir = ck_strdup(argv0); // 复制目录部分
*slash = '/'; // 恢复'/'恢复argv0的完整路径
tmp = alloc_printf("%s/afl-as", dir);
tmp = alloc_printf("%s/afl-as", dir); // 构造路径字符串
if (!access(tmp, X_OK))
if (!access(tmp, X_OK)) // 检查路径是否存在且可执行
{
as_path = dir;
ck_free(tmp);
as_path = dir; // 设置as_path为找到的路径
ck_free(tmp); // 释放临时字符串
return;
}
ck_free(tmp);
ck_free(dir);
ck_free(tmp); // 如果不可执行,释放临时字符串
ck_free(dir); // 释放目录字符串
}
if (!access(AFL_PATH "/as", X_OK))
if (!access(AFL_PATH "/as", X_OK)) // 检查默认路径AFL_PATH/as是否存在且可执行
{
as_path = AFL_PATH;
as_path = AFL_PATH; // 设置as_path为默认路径
return;
}
FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH");
FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH"); // 如果找不到,输出错误信息并中止
}
/*将argv复制到cc_params进行必要的编辑*/
/* 将argv复制到cc_params进行必要的编辑 */
static void edit_params(u32 argc, char **argv)
{
u8 fortify_set = 0, asan_set = 0;
u8 fortify_set = 0, asan_set = 0; // 标志变量用于检测是否设置了fortify和asan
u8 *name;
#if defined(__FreeBSD__) && defined(__x86_64__)
u8 m32_set = 0;
u8 m32_set = 0; // FreeBSD x86_64环境下检测是否设置了m32
#endif
/********************************************************************************
@ -127,77 +121,74 @@ static void edit_params(u32 argc, char **argv)
* 使 afl-clang使 C C++
********************************************************************************/
cc_params = ck_alloc((argc + 128) * sizeof(u8 *));
name = strrchr(argv[0], '/');
if (!name)
name = argv[0];
else
name++;
if (!strncmp(name, "afl-clang", 9))
{
clang_mode = 1;
setenv(CLANG_ENV_VAR, "1", 1);
if (!strcmp(name, "afl-clang++"))
{
u8 *alt_cxx = getenv("AFL_CXX");
cc_params[0] = alt_cxx ? alt_cxx : (u8 *)"clang++";
}
else
{
u8 *alt_cc = getenv("AFL_CC");
cc_params[0] = alt_cc ? alt_cc : (u8 *)"clang";
}
}
cc_params = ck_alloc((argc + 128) * sizeof(u8 *)); // 分配足够大的空间来存储编译器参数
name = strrchr(argv[0], '/'); // 获取可执行文件名
if (!name)
name = argv[0]; // 如果没有找到'/'则argv0就是可执行文件名
else
{
name++; // 否则name指向可执行文件名的第一个字符
/*安装了GCJ和Eclipse后您实际上可以编译Java这个
abortafl-fuzz使
使Java退
*/
if (!strncmp(name, "afl-clang", 9)) // 如果可执行文件名以afl-clang开头
{
clang_mode = 1; // 设置clang_mode标志为1表示使用clang模式
#ifdef __APPLE__
setenv(CLANG_ENV_VAR, "1", 1); // 设置环境变量表示正在使用afl-clang
if (!strcmp(name, "afl-g++"))
cc_params[0] = getenv("AFL_CXX");
else if (!strcmp(name, "afl-gcj"))
cc_params[0] = getenv("AFL_GCJ");
if (!strcmp(name, "afl-clang++")) // 如果是afl-clang++
{
u8 *alt_cxx = getenv("AFL_CXX"); // 获取环境变量AFL_CXX的值
cc_params[0] = alt_cxx ? alt_cxx : (u8 *)"clang++"; // 如果设置了AFL_CXX则使用该值作为编译器否则使用clang++
}
else // 如果是afl-clang
{
u8 *alt_cc = getenv("AFL_CC"); // 获取环境变量AFL_CC的值
cc_params[0] = alt_cc ? alt_cc : (u8 *)"clang"; // 如果设置了AFL_CC则使用该值作为编译器否则使用clang
}
}
else // 如果不是afl-clang
{
// 安装了GCJ和Eclipse后您实际上可以编译Java这个
// 仪器将工作(令人惊讶)。唉,未处理的异常确实如此
// 不调用abort因此需要修改afl-fuzz以使其相等
// 使用Java时具有崩溃条件的非零退出代码
// 二进制文件。嗯
#ifdef __APPLE__ // 如果是在MacOS X系统上
if (!strcmp(name, "afl-g++")) // 如果是afl-g++
cc_params[0] = getenv("AFL_CXX"); // 获取环境变量AFL_CXX的值作为编译器
else if (!strcmp(name, "afl-gcj")) // 如果是afl-gcj
cc_params[0] = getenv("AFL_GCJ"); // 获取环境变量AFL_GCJ的值作为编译器
else
cc_params[0] = getenv("AFL_CC");
cc_params[0] = getenv("AFL_CC"); // 其他情况获取环境变量AFL_CC的值作为编译器
if (!cc_params[0])
if (!cc_params[0]) // 如果没有设置相应的环境变量
{
SAYF("\n" cLRD "[-] " cRST
SAYF("\n" cLRD "[-] " cRST // 输出错误信息
"On Apple systems, 'gcc' is usually just a wrapper for clang. Please use the\n"
" 'afl-clang' utility instead of 'afl-gcc'. If you really have GCC installed,\n"
" set AFL_CC or AFL_CXX to specify the correct path to that compiler.\n");
FATAL("AFL_CC or AFL_CXX required on MacOS X");
FATAL("AFL_CC or AFL_CXX required on MacOS X"); // 输出错误信息并中止
}
#else
#else // 如果不是在MacOS X系统上
if (!strcmp(name, "afl-g++"))
if (!strcmp(name, "afl-g++")) // 如果是afl-g++
{
u8 *alt_cxx = getenv("AFL_CXX");
cc_params[0] = alt_cxx ? alt_cxx : (u8 *)"g++";
u8 *alt_cxx = getenv("AFL_CXX"); // 获取环境变量AFL_CXX的值
cc_params[0] = alt_cxx ? alt_cxx : (u8 *)"g++"; // 如果设置了AFL_CXX则使用该值作为编译器否则使用g++
}
else if (!strcmp(name, "afl-gcj"))
else if (!strcmp(name, "afl-gcj")) // 如果是afl-gcj
{
u8 *alt_cc = getenv("AFL_GCJ");
cc_params[0] = alt_cc ? alt_cc : (u8 *)"gcj";
u8 *alt_cc = getenv("AFL_GCJ"); // 获取环境变量AFL_GCJ的值
cc_params[0] = alt_cc ? alt_cc : (u8 *)"gcj"; // 如果设置了AFL_GCJ则使用该值作为编译器否则使用gcj
}
else
else // 如果是afl-gcc
{
u8 *alt_cc = getenv("AFL_CC");
cc_params[0] = alt_cc ? alt_cc : (u8 *)"gcc";
u8 *alt_cc = getenv("AFL_CC"); // 获取环境变量AFL_CC的值
cc_params[0] = alt_cc ? alt_cc : (u8 *)"gcc"; // 如果设置了AFL_CC则使用该值作为编译器否则使用gcc
}
#endif /* __APPLE__ */
@ -233,32 +224,35 @@ static void edit_params(u32 argc, char **argv)
if (!strcmp(cur, "-pipe"))
continue;
/ *
*
*
* FORTIFY_SOURCEASAN/MSAN
* /
#if defined(__FreeBSD__) && defined(__x86_64__)
// 检测是否在FreeBSD的64位系统上使用了-m32选项如果是则设置m32_set为1
if (!strcmp(cur, "-m32"))
m32_set = 1;
#endif
// 检测是否使用了address sanitizer或memory sanitizer选项如果是则设置asan_set为1
if (!strcmp(cur, "-fsanitize=address") ||
!strcmp(cur, "-fsanitize=memory"))
asan_set = 1;
// 检测是否设置了_FORTIFY_SOURCE宏如果是则设置fortify_set为1
if (strstr(cur, "FORTIFY_SOURCE"))
fortify_set = 1;
// 将当前命令行参数添加到编译器参数列表中
cc_params[cc_par_cnt++] = cur;
}
// 添加编译器的-B参数指定路径给as_path
cc_params[cc_par_cnt++] = "-B";
cc_params[cc_par_cnt++] = as_path;
// 如果clang_mode为真则添加-no-integrated-as参数
if (clang_mode)
cc_params[cc_par_cnt++] = "-no-integrated-as";
// 如果设置了AFL_HARDEN环境变量则添加-fstack-protector-all参数并在未设置_FORTIFY_SOURCE时定义它
if (getenv("AFL_HARDEN"))
{
@ -268,6 +262,7 @@ static void edit_params(u32 argc, char **argv)
cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
}
// 如果asan_set为真则设置AFL_USE_ASAN环境变量为1用于通知afl使用asan
if (asan_set)
{
@ -277,25 +272,27 @@ static void edit_params(u32 argc, char **argv)
}
else if (getenv("AFL_USE_ASAN"))
{
// 如果设置了AFL_USE_ASAN但未设置asan_set检查是否同时设置了MSAN和AFL_HARDEN如果是则报错
if (getenv("AFL_USE_MSAN"))
FATAL("ASAN and MSAN are mutually exclusive");
FATAL("ASAN和MSAN是互斥的");
if (getenv("AFL_HARDEN"))
FATAL("ASAN and AFL_HARDEN are mutually exclusive");
FATAL("ASAN和AFL_HARDEN是互斥的");
// 取消定义_FORTIFY_SOURCE宏添加address sanitizer参数
cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
cc_params[cc_par_cnt++] = "-fsanitize=address";
}
else if (getenv("AFL_USE_MSAN"))
{
// 如果设置了AFL_USE_MSAN检查是否同时设置了ASAN和AFL_HARDEN如果是则报错
if (getenv("AFL_USE_ASAN"))
FATAL("ASAN and MSAN are mutually exclusive");
FATAL("ASAN和MSAN是互斥的");
if (getenv("AFL_HARDEN"))
FATAL("MSAN and AFL_HARDEN are mutually exclusive");
FATAL("MSAN和AFL_HARDEN是互斥的");
// 取消定义_FORTIFY_SOURCE宏添加memory sanitizer参数
cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
cc_params[cc_par_cnt++] = "-fsanitize=memory";
}
@ -306,6 +303,7 @@ static void edit_params(u32 argc, char **argv)
* : d:\code\google_AFL\src\afl-gcc.c
********************************************************************************/
// 如果未设置AFL_DONT_OPTIMIZE环境变量则进行优化设置
if (!getenv("AFL_DONT_OPTIMIZE"))
{
@ -315,41 +313,45 @@ static void edit_params(u32 argc, char **argv)
bug*/
// 如果不是clang模式或者未设置m32选项则添加-g参数
if (!clang_mode || !m32_set)
cc_params[cc_par_cnt++] = "-g";
#else
// 添加调试信息参数-g
cc_params[cc_par_cnt++] = "-g";
#endif
// 添加-O3优化级别参数和-unroll-loops参数
cc_params[cc_par_cnt++] = "-O3";
cc_params[cc_par_cnt++] = "-funroll-loops";
/* Two indicators that you're building for fuzzing; one of them is
AFL-specific, the other is shared with libfuzzer. */
// 添加编译器标志,指示正在为模糊测试构建代码
cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1";
cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1";
}
/***********************************************
*
* "AFL_NO_BUILTIN"
*
***********************************************/
if (getenv("AFL_NO_BUILTIN"))
{
cc_params[cc_par_cnt++] = "-fno-builtin-strcmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strncmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp";
cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strstr";
cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr";
} cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp";
cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
// 如果设置了AFL_NO_BUILTIN环境变量则添加参数禁用内置的字符串和内存比较函数
if (getenv("AFL_NO_BUILTIN"))
{
cc_params[cc_par_cnt++] = "-fno-builtin-strcmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strncmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp";
cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
cc_params[cc_par_cnt++] = "-fno-builtin-strstr";
cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr";
// 注意:这里重复了两遍-fno-builtin-strncasecmp 和 -fno-builtin-memcmp应去掉重复的部分
// cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp";
// cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
}
// 设置编译器参数列表的结束标志NULL
cc_params[cc_par_cnt] = NULL;
}
@ -358,45 +360,42 @@ static void edit_params(u32 argc, char **argv)
// 主函数,程序的入口点
int main(int argc, char **argv)
{
// 检查标准错误输出是否为终端,以及环境变量是否开启安静模式
if (isatty(2) && !getenv("AFL_QUIET"))
{
SAYF(cCYA "afl-cc " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
}
else
be_quiet = 1;
// 检查传入的参数数量是否少于2
// 检查传入的参数数量是否少于2,如果是则输出使用说明并退出
if (argc < 2)
{
SAYF("\n"
"This is a helper application for afl-fuzz. It serves as a drop-in replacement\n"
"for gcc or clang, letting you recompile third-party code with the required\n"
"runtime instrumentation. A common use pattern would be one of the following:\n\n"
"这是一个辅助afl-fuzz的工具程序。它可以用作gcc或clang的替代品\n"
"让你能够使用必要的运行时检测重新编译第三方代码。\n"
"常见的使用模式如下之一:\n\n"
" CC=%s/afl-gcc ./configure\n"
" CXX=%s/afl-g++ ./configure\n\n"
"You can specify custom next-stage toolchain via AFL_CC, AFL_CXX, and AFL_AS.\n"
"Setting AFL_HARDEN enables hardening optimizations in the compiled code.\n\n",
"你可以通过AFL_CC, AFL_CXX, 和 AFL_AS指定自定义的后续编译工具链。\n"
"设置AFL_HARDEN会在编译代码时启用hardening优化。\n\n",
BIN_PATH, BIN_PATH);
exit(1);
}
// 查找汇编器
// 查找汇编器根据argv[0]来确定使用的编译器是gcc还是clang
find_as(argv[0]);
// 编辑参数
// 编辑命令行参数,根据需要添加和调整编译器参数
edit_params(argc, argv);
// 执行编译器,并传递参数
// 执行编译器,并传递编辑后的参数列表
execvp(cc_params[0], (char **)cc_params);
// 如果执行失败,输出错误信息
// 如果execvp调用失败输出错误信息并退出程序
FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
return 0;

@ -57,104 +57,108 @@
/* Get unix time in microseconds. */
// 定义一个函数来获取当前时间(以微秒为单位)
static u64 get_cur_time_us(void) {
struct timeval tv;
struct timezone tz;
struct timeval tv; // 用于存储时间值
struct timezone tz; // 用于存储时区信息
gettimeofday(&tv, &tz);
gettimeofday(&tv, &tz); // 获取当前时间
// 返回以微秒为单位的时间
return (tv.tv_sec * 1000000ULL) + tv.tv_usec;
}
/* Get CPU usage in microseconds. */
/* 获取CPU使用时间以微秒为单位。 */
static u64 get_cpu_usage_us(void) {
struct rusage u;
struct rusage u; // 用于存储进程资源使用情况
getrusage(RUSAGE_SELF, &u);
getrusage(RUSAGE_SELF, &u); // 获取当前进程的资源使用情况
// 返回用户态和内核态CPU使用时间之和以微秒为单位
return (u.ru_utime.tv_sec * 1000000ULL) + u.ru_utime.tv_usec +
(u.ru_stime.tv_sec * 1000000ULL) + u.ru_stime.tv_usec;
}
/* Measure preemption rate. */
/* 测量抢占率。 */
static u32 measure_preemption(u32 target_ms) {
static volatile u32 v1, v2;
static volatile u32 v1, v2; // 定义两个静态易失变量用于循环计数
u64 st_t, en_t, st_c, en_c, real_delta, slice_delta;
s32 loop_repeats = 0;
u64 st_t, en_t, st_c, en_c, real_delta, slice_delta; // 定义时间变量
s32 loop_repeats = 0; // 定义循环重复次数
st_t = get_cur_time_us();
st_c = get_cpu_usage_us();
st_t = get_cur_time_us(); // 获取开始时间
st_c = get_cpu_usage_us(); // 获取开始时的CPU使用时间
repeat_loop:
repeat_loop: // 定义循环标签
v1 = CTEST_BUSY_CYCLES;
v1 = CTEST_BUSY_CYCLES; // 设置v1为循环次数
// 循环v1次每次v2自增1模拟CPU繁忙
while (v1--) v2++;
sched_yield();
sched_yield(); // 让出CPU允许其他进程运行
en_t = get_cur_time_us();
en_t = get_cur_time_us(); // 获取结束时间
// 如果实际运行时间小于目标时间,则增加循环次数并继续循环
if (en_t - st_t < target_ms * 1000) {
loop_repeats++;
goto repeat_loop;
}
/* Let's see what percentage of this time we actually had a chance to
run, and how much time was spent in the penalty box. */
// 获取结束时的CPU使用时间
en_c = get_cpu_usage_us();
// 计算实际运行时间(以毫秒为单位)
real_delta = (en_t - st_t) / 1000;
// 计算CPU使用时间以毫秒为单位
slice_delta = (en_c - st_c) / 1000;
// 返回实际运行时间占CPU使用时间的百分比作为抢占率
return real_delta * 100 / slice_delta;
}
/* Do the benchmark thing. */
/* 进行基准测试。 */
int main(int argc, char** argv) {
#ifdef HAVE_AFFINITY
#ifdef HAVE_AFFINITY // 如果支持AFFINITY设置进程CPU亲和性
u32 cpu_cnt = sysconf(_SC_NPROCESSORS_ONLN),
idle_cpus = 0, maybe_cpus = 0, i;
u32 cpu_cnt = sysconf(_SC_NPROCESSORS_ONLN), // 获取CPU核心数
idle_cpus = 0, maybe_cpus = 0, i; // 初始化空闲和可能可用的CPU核心数
SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by <lcamtuf@google.com>\n"); // 输出程序信息
// 输出测量核心抢占率的信息,包括预计所需时间
ACTF("Measuring per-core preemption rate (this will take %0.02f sec)...",
((double)CTEST_CORE_TRG_MS) / 1000);
// 对每个CPU核心进行抢占率测量
for (i = 0; i < cpu_cnt; i++) {
s32 fr = fork();
s32 fr = fork(); // 创建子进程
if (fr < 0) PFATAL("fork failed");
if (fr < 0) PFATAL("fork failed"); // 如果fork失败则输出错误信息
if (!fr) {
if (!fr) { // 子进程中
cpu_set_t c;
u32 util_perc;
cpu_set_t c; // 定义CPU亲和性集合
u32 util_perc; // 定义CPU利用率
CPU_ZERO(&c);
CPU_SET(i, &c);
CPU_ZERO(&c); // 清空CPU亲和性集合
CPU_SET(i, &c); // 设置亲和性为当前核心
// 设置进程亲和性到指定的核心
if (sched_setaffinity(0, sizeof(c), &c))
PFATAL("sched_setaffinity failed for cpu %d", i);
util_perc = measure_preemption(CTEST_CORE_TRG_MS);
util_perc = measure_preemption(CTEST_CORE_TRG_MS); // 测量抢占率
// 根据抢占率输出核心状态
if (util_perc < 110) {
SAYF(" Core #%u: " cLGN "AVAILABLE " cRST "(%u%%)\n", i, util_perc);
@ -175,18 +179,21 @@ int main(int argc, char** argv) {
}
// 等待所有子进程结束,并统计结果
for (i = 0; i < cpu_cnt; i++) {
int ret;
if (waitpid(-1, &ret, 0) < 0) PFATAL("waitpid failed");
if (waitpid(-1, &ret, 0) < 0) PFATAL("waitpid failed"); // 等待子进程结束
// 根据子进程退出状态判断核心是否可用
if (WEXITSTATUS(ret) == 0) idle_cpus++;
if (WEXITSTATUS(ret) <= 1) maybe_cpus++;
}
SAYF(cGRA "\n>>> ");
SAYF(cGRA "\n>>> "); // 输出结果标题
// 根据统计结果输出最终判断
if (idle_cpus) {
if (maybe_cpus == idle_cpus) {
@ -201,24 +208,30 @@ int main(int argc, char** argv) {
}
SAYF(cGRA " <<<" cRST "\n\n");
SAYF(cGRA " <<<" cRST "\n\n"); // 输出结果结束标志
return 0;
}
// 如果有可能可用的核心,但没有完全空闲的核心
if (maybe_cpus) {
SAYF(cYEL "CAUTION: " cRST "You may still have %u core%s available.",
maybe_cpus, maybe_cpus > 1 ? "s" : "");
SAYF(cGRA " <<<" cRST "\n\n");
SAYF(cGRA " <<<" cRST "\n\n"); // 输出结果结束标志
return 1;
}
// 如果所有核心都过载
SAYF(cLRD "FAIL: " cRST "All cores are overbooked.");
SAYF(cGRA " <<<" cRST "\n\n");
SAYF(cGRA " <<<" cRST "\n\n"); // 输出结果结束标志
return 2;
#endif
}
#else
u32 util_perc;

Loading…
Cancel
Save