|
|
|
@ -70,9 +70,8 @@ static u8 be_quiet, /* Quiet mode (no stderr output) */
|
|
|
|
|
static u32 inst_ratio = 100, /* Instrumentation probability (%) */
|
|
|
|
|
as_par_cnt = 1; /* Number of params to 'as' */
|
|
|
|
|
|
|
|
|
|
/* If we don't find --32 or --64 in the command line, default to
|
|
|
|
|
instrumentation for whichever mode we were compiled with. This is not
|
|
|
|
|
perfect, but should do the trick for almost all use cases. */
|
|
|
|
|
/* 如果命令行中没有找到 --32 或 --64 参数,则默认对编译该工具时使用的模式进行插桩。
|
|
|
|
|
这不是完美的,但对于大多数使用场景来说已经足够了。 */
|
|
|
|
|
|
|
|
|
|
#ifdef WORD_SIZE_64
|
|
|
|
|
|
|
|
|
@ -89,9 +88,8 @@ static u8 use_64bit = 0;
|
|
|
|
|
#endif /* ^WORD_SIZE_64 */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Examine and modify parameters to pass to 'as'. Note that the file name
|
|
|
|
|
is always the last parameter passed by GCC, so we exploit this property
|
|
|
|
|
to keep the code simple. */
|
|
|
|
|
/* 检查并修改传递给 'as' 的参数。注意 GCC 总是将文件名作为最后一个参数传递给 'as',
|
|
|
|
|
因此我们利用这一特性来简化代码。 */
|
|
|
|
|
|
|
|
|
|
static void edit_params(int argc, char** argv) {
|
|
|
|
|
|
|
|
|
@ -102,17 +100,14 @@ static void edit_params(int argc, char** argv) {
|
|
|
|
|
|
|
|
|
|
u8 use_clang_as = 0;
|
|
|
|
|
|
|
|
|
|
/* On MacOS X, the Xcode cctool 'as' driver is a bit stale and does not work
|
|
|
|
|
with the code generated by newer versions of clang that are hand-built
|
|
|
|
|
by the user. See the thread here: http://goo.gl/HBWDtn.
|
|
|
|
|
/* 在 MacOS X 上,Xcode cctool 'as' 驱动程序有点过时,无法处理由用户自己编译的较新版本的 clang
|
|
|
|
|
生成的代码。详见此线程:http://goo.gl/HBWDtn.
|
|
|
|
|
|
|
|
|
|
To work around this, when using clang and running without AFL_AS
|
|
|
|
|
specified, we will actually call 'clang -c' instead of 'as -q' to
|
|
|
|
|
compile the assembly file.
|
|
|
|
|
为了绕过这一问题,当使用 clang 且未指定 AFL_AS 时,我们将实际调用 'clang -c' 而不是 'as -q'
|
|
|
|
|
来编译汇编文件.
|
|
|
|
|
|
|
|
|
|
The tools aren't cmdline-compatible, but at least for now, we can
|
|
|
|
|
seemingly get away with this by making only very minor tweaks. Thanks
|
|
|
|
|
to Nico Weber for the idea. */
|
|
|
|
|
虽然这两个工具不是命令行兼容的,但我们可以通过进行一些小的修改来让它们在某些情况下似乎可以很好地协同工作。
|
|
|
|
|
感谢 Nico Weber 提出这一思路。 */
|
|
|
|
|
|
|
|
|
|
if (clang_mode && !afl_as) {
|
|
|
|
|
|
|
|
|
@ -126,9 +121,8 @@ static void edit_params(int argc, char** argv) {
|
|
|
|
|
|
|
|
|
|
#endif /* __APPLE__ */
|
|
|
|
|
|
|
|
|
|
/* Although this is not documented, GCC also uses TEMP and TMP when TMPDIR
|
|
|
|
|
is not set. We need to check these non-standard variables to properly
|
|
|
|
|
handle the pass_thru logic later on. */
|
|
|
|
|
/* 虽然这在文档中没有提及,但 GCC 实际上也使用 TEMP 和 TMP(当 TMPDIR 未设置时)。
|
|
|
|
|
我们需要检查这些非常规变量以正确处理 pass_thru 逻辑。 */
|
|
|
|
|
|
|
|
|
|
if (!tmp_dir) tmp_dir = getenv("TEMP");
|
|
|
|
|
if (!tmp_dir) tmp_dir = getenv("TMP");
|
|
|
|
@ -147,7 +141,7 @@ static void edit_params(int argc, char** argv) {
|
|
|
|
|
|
|
|
|
|
#ifdef __APPLE__
|
|
|
|
|
|
|
|
|
|
/* The Apple case is a bit different... */
|
|
|
|
|
/* MacOS X 的情况有点不同... */
|
|
|
|
|
|
|
|
|
|
if (!strcmp(argv[i], "-arch") && i + 1 < argc) {
|
|
|
|
|
|
|
|
|
@ -157,8 +151,7 @@ static void edit_params(int argc, char** argv) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Strip options that set the preference for a particular upstream
|
|
|
|
|
assembler in Xcode. */
|
|
|
|
|
/* 移除 Xcode 中设置特定上游汇编器的选项 */
|
|
|
|
|
|
|
|
|
|
if (clang_mode && (!strcmp(argv[i], "-q") || !strcmp(argv[i], "-Q")))
|
|
|
|
|
continue;
|
|
|
|
@ -171,8 +164,7 @@ static void edit_params(int argc, char** argv) {
|
|
|
|
|
|
|
|
|
|
#ifdef __APPLE__
|
|
|
|
|
|
|
|
|
|
/* When calling clang as the upstream assembler, append -c -x assembler
|
|
|
|
|
and hope for the best. */
|
|
|
|
|
/* 当调用 clang 作为上游汇编器时,追加 -c -x assembler 选项并希望一切顺利。 */
|
|
|
|
|
|
|
|
|
|
if (use_clang_as) {
|
|
|
|
|
|
|
|
|
@ -199,10 +191,8 @@ static void edit_params(int argc, char** argv) {
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
/* Check if this looks like a standard invocation as a part of an attempt
|
|
|
|
|
to compile a program, rather than using gcc on an ad-hoc .s file in
|
|
|
|
|
a format we may not understand. This works around an issue compiling
|
|
|
|
|
NSS. */
|
|
|
|
|
/* 检查是否为标准调用,作为编译程序的一部分,而不是使用 gcc 对一个独立的 .s 文件进行编译。
|
|
|
|
|
这解决了在编译 NSS 时遇到的问题。 */
|
|
|
|
|
|
|
|
|
|
if (strncmp(input_file, tmp_dir, strlen(tmp_dir)) &&
|
|
|
|
|
strncmp(input_file, "/var/tmp/", 9) &&
|
|
|
|
@ -221,8 +211,7 @@ wrap_things_up:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Process input file, generate modified_file. Insert instrumentation in all
|
|
|
|
|
the appropriate places. */
|
|
|
|
|
/* 处理输入文件并生成 modified_file。在所有适当的位置插入插桩代码。 */
|
|
|
|
|
|
|
|
|
|
static void add_instrumentation(void) {
|
|
|
|
|
|
|
|
|
@ -259,10 +248,8 @@ static void add_instrumentation(void) {
|
|
|
|
|
|
|
|
|
|
while (fgets(line, MAX_LINE, inf)) {
|
|
|
|
|
|
|
|
|
|
/* In some cases, we want to defer writing the instrumentation trampoline
|
|
|
|
|
until after all the labels, macros, comments, etc. If we're in this
|
|
|
|
|
mode, and if the line starts with a tab followed by a character, dump
|
|
|
|
|
the trampoline now. */
|
|
|
|
|
/* 在某些情况下,我们希望在所有标签、宏、注释等之后再插入插桩跳板代码。
|
|
|
|
|
如果处于这一模式,且行以制表符开头,后跟一个字母,则现在插入跳板代码。 */
|
|
|
|
|
|
|
|
|
|
if (!pass_thru && !skip_intel && !skip_app && !skip_csect && instr_ok &&
|
|
|
|
|
instrument_next && line[0] == '\t' && isalpha(line[1])) {
|
|
|
|
@ -275,21 +262,19 @@ static void add_instrumentation(void) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Output the actual line, call it a day in pass-thru mode. */
|
|
|
|
|
/* 输出实际的行,在 pass-thru 模式下结束操作。 */
|
|
|
|
|
|
|
|
|
|
fputs(line, outf);
|
|
|
|
|
|
|
|
|
|
if (pass_thru) continue;
|
|
|
|
|
|
|
|
|
|
/* All right, this is where the actual fun begins. For one, we only want to
|
|
|
|
|
instrument the .text section. So, let's keep track of that in processed
|
|
|
|
|
files - and let's set instr_ok accordingly. */
|
|
|
|
|
/* 现在开始真正的插桩操作。首先,我们只希望插桩 .text 部分。
|
|
|
|
|
因此,我们需要跟踪处理的汇编文件中各部分的状态,并据此设置 instr_ok。 */
|
|
|
|
|
|
|
|
|
|
if (line[0] == '\t' && line[1] == '.') {
|
|
|
|
|
|
|
|
|
|
/* OpenBSD puts jump tables directly inline with the code, which is
|
|
|
|
|
a bit annoying. They use a specific format of p2align directives
|
|
|
|
|
around them, so we use that as a signal. */
|
|
|
|
|
/* OpenBSD 在代码中直接放置跳转表,这稍微有点麻烦。
|
|
|
|
|
它们使用特定格式的 p2align 指令围绕它们,因此我们可以使用该格式作为信号。 */
|
|
|
|
|
|
|
|
|
|
if (!clang_mode && instr_ok && !strncmp(line + 2, "p2align ", 8) &&
|
|
|
|
|
isdigit(line[10]) && line[11] == '\n') skip_next_label = 1;
|
|
|
|
@ -312,9 +297,8 @@ static void add_instrumentation(void) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Detect off-flavor assembly (rare, happens in gdb). When this is
|
|
|
|
|
encountered, we set skip_csect until the opposite directive is
|
|
|
|
|
seen, and we do not instrument. */
|
|
|
|
|
/* 检测非常规汇编(罕见,例如在 gdb 中)。当遇到这种汇编时,我们设置 skip_csect
|
|
|
|
|
直到遇到相反的指令,此时我们不进行插桩。 */
|
|
|
|
|
|
|
|
|
|
if (strstr(line, ".code")) {
|
|
|
|
|
|
|
|
|
@ -323,13 +307,7 @@ static void add_instrumentation(void) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Detect syntax changes, as could happen with hand-written assembly.
|
|
|
|
|
Skip Intel blocks, resume instrumentation when back to AT&T. */
|
|
|
|
|
|
|
|
|
|
if (strstr(line, ".intel_syntax")) skip_intel = 1;
|
|
|
|
|
if (strstr(line, ".att_syntax")) skip_intel = 0;
|
|
|
|
|
|
|
|
|
|
/* Detect and skip ad-hoc __asm__ blocks, likewise skipping them. */
|
|
|
|
|
/* 检测并跳过手写汇编块(__asm__),同样不进行插桩。 */
|
|
|
|
|
|
|
|
|
|
if (line[0] == '#' || line[1] == '#') {
|
|
|
|
|
|
|
|
|
@ -338,36 +316,30 @@ static void add_instrumentation(void) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we're in the right mood for instrumenting, check for function
|
|
|
|
|
names or conditional labels. This is a bit messy, but in essence,
|
|
|
|
|
we want to catch:
|
|
|
|
|
/* 如果我们处于插桩模式,检查函数名或条件标签。这里逻辑有些复杂,但基本目标是捕获:
|
|
|
|
|
|
|
|
|
|
^main: - function entry point (always instrumented)
|
|
|
|
|
^.L0: - GCC branch label
|
|
|
|
|
^.LBB0_0: - clang branch label (but only in clang mode)
|
|
|
|
|
^\tjnz foo - conditional branches
|
|
|
|
|
^main: - 函数入口点(总是插桩)
|
|
|
|
|
^.L0: - GCC 分支标签
|
|
|
|
|
^.LBB0_0: - clang 分支标签(但仅在 clang 模式下)
|
|
|
|
|
^\tjnz foo - 条件分支
|
|
|
|
|
|
|
|
|
|
...but not:
|
|
|
|
|
...而不捕获:
|
|
|
|
|
|
|
|
|
|
^# BB#0: - clang comments
|
|
|
|
|
^ # BB#0: - ditto
|
|
|
|
|
^.Ltmp0: - clang non-branch labels
|
|
|
|
|
^.LC0 - GCC non-branch labels
|
|
|
|
|
^.LBB0_0: - ditto (when in GCC mode)
|
|
|
|
|
^\tjmp foo - non-conditional jumps
|
|
|
|
|
|
|
|
|
|
Additionally, clang and GCC on MacOS X follow a different convention
|
|
|
|
|
with no leading dots on labels, hence the weird maze of #ifdefs
|
|
|
|
|
later on.
|
|
|
|
|
^# BB#0: - clang 注释
|
|
|
|
|
^ # BB#0: - 同上
|
|
|
|
|
^.Ltmp0: - clang 非分支标签
|
|
|
|
|
^.LC0 - GCC 非分支标签
|
|
|
|
|
^.LBB0_0: - 同上(当处于 GCC 模式下)
|
|
|
|
|
^\tjmp foo - 非条件跳转
|
|
|
|
|
|
|
|
|
|
此外,MacOS X 上的 clang 和 GCC 使用不同的标签格式,没有前导点,因此我们根据这一情况处理。
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (skip_intel || skip_app || skip_csect || !instr_ok ||
|
|
|
|
|
line[0] == '#' || line[0] == ' ') continue;
|
|
|
|
|
|
|
|
|
|
/* Conditional branch instruction (jnz, etc). We append the instrumentation
|
|
|
|
|
right after the branch (to instrument the not-taken path) and at the
|
|
|
|
|
branch destination label (handled later on). */
|
|
|
|
|
/* 条件分支指令(jnz 等)。我们会在分支之后插入插桩(以插桩不执行路径),
|
|
|
|
|
并在分支目标标签处插入(稍后处理)。 */
|
|
|
|
|
|
|
|
|
|
if (line[0] == '\t') {
|
|
|
|
|
|
|
|
|
@ -384,13 +356,11 @@ static void add_instrumentation(void) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Label of some sort. This may be a branch destination, but we need to
|
|
|
|
|
tread carefully and account for several different formatting
|
|
|
|
|
conventions. */
|
|
|
|
|
/* 某类标签。这可能是分支目标,但我们需要小心处理不同的格式约定。 */
|
|
|
|
|
|
|
|
|
|
#ifdef __APPLE__
|
|
|
|
|
|
|
|
|
|
/* Apple: L<whatever><digit>: */
|
|
|
|
|
/* MacOS X: L<whatever><digit>: */
|
|
|
|
|
|
|
|
|
|
if ((colon_pos = strstr(line, ":"))) {
|
|
|
|
|
|
|
|
|
@ -398,7 +368,7 @@ static void add_instrumentation(void) {
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
/* Everybody else: .L<whatever>: */
|
|
|
|
|
/* 其他人:.L<whatever>: */
|
|
|
|
|
|
|
|
|
|
if (strstr(line, ":")) {
|
|
|
|
|
|
|
|
|
@ -406,34 +376,29 @@ static void add_instrumentation(void) {
|
|
|
|
|
|
|
|
|
|
#endif /* __APPLE__ */
|
|
|
|
|
|
|
|
|
|
/* .L0: or LBB0_0: style jump destination */
|
|
|
|
|
/* .L0: 或 LBB0_0: 风格的分支目标 */
|
|
|
|
|
|
|
|
|
|
#ifdef __APPLE__
|
|
|
|
|
|
|
|
|
|
/* Apple: L<num> / LBB<num> */
|
|
|
|
|
/* MacOS X: L<num> / LBB<num> */
|
|
|
|
|
|
|
|
|
|
if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3)))
|
|
|
|
|
&& R(100) < inst_ratio) {
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
/* Apple: .L<num> / .LBB<num> */
|
|
|
|
|
/* MacOS X: .L<num> / .LBB<num> */
|
|
|
|
|
|
|
|
|
|
if ((isdigit(line[2]) || (clang_mode && !strncmp(line + 1, "LBB", 3)))
|
|
|
|
|
&& R(100) < inst_ratio) {
|
|
|
|
|
|
|
|
|
|
#endif /* __APPLE__ */
|
|
|
|
|
|
|
|
|
|
/* An optimization is possible here by adding the code only if the
|
|
|
|
|
label is mentioned in the code in contexts other than call / jmp.
|
|
|
|
|
That said, this complicates the code by requiring two-pass
|
|
|
|
|
processing (messy with stdin), and results in a speed gain
|
|
|
|
|
typically under 10%, because compilers are generally pretty good
|
|
|
|
|
about not generating spurious intra-function jumps.
|
|
|
|
|
/* 在仅需要在标签被引用时(非调用/跳转上下文)才添加代码的情况下可以进行优化。
|
|
|
|
|
这会引入两遍处理过程的复杂性(当使用 stdin 时尤其麻烦),并且通常只能带来不到 10% 的速度提升。
|
|
|
|
|
因为编译器通常不会生成不相关的函数内跳转。
|
|
|
|
|
|
|
|
|
|
We use deferred output chiefly to avoid disrupting
|
|
|
|
|
.Lfunc_begin0-style exception handling calculations (a problem on
|
|
|
|
|
MacOS X). */
|
|
|
|
|
我们使用延迟输出主要是为了避免干扰 MacOS X 上 .Lfunc_begin0 风格异常处理计算的问题。 */
|
|
|
|
|
|
|
|
|
|
if (!skip_next_label) instrument_next = 1; else skip_next_label = 0;
|
|
|
|
|
|
|
|
|
@ -441,7 +406,7 @@ static void add_instrumentation(void) {
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
/* Function label (always instrumented, deferred mode). */
|
|
|
|
|
/* 函数标签(总是插桩,延迟模式)。 */
|
|
|
|
|
|
|
|
|
|
instrument_next = 1;
|
|
|
|
|
|
|
|
|
@ -472,7 +437,7 @@ static void add_instrumentation(void) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Main entry point */
|
|
|
|
|
/* 程序主入口点 */
|
|
|
|
|
|
|
|
|
|
int main(int argc, char** argv) {
|
|
|
|
|
|
|
|
|
@ -527,9 +492,8 @@ int main(int argc, char** argv) {
|
|
|
|
|
|
|
|
|
|
setenv(AS_LOOP_ENV_VAR, "1", 1);
|
|
|
|
|
|
|
|
|
|
/* When compiling with ASAN, we don't have a particularly elegant way to skip
|
|
|
|
|
ASAN-specific branches. But we can probabilistically compensate for
|
|
|
|
|
that... */
|
|
|
|
|
/* 使用 ASAN 时,我们没有特别优雅的方法来跳过 ASAN 特定的分支。
|
|
|
|
|
但可以通过按概率补偿来解决这个问题... */
|
|
|
|
|
|
|
|
|
|
if (getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) {
|
|
|
|
|
sanitizer = 1;
|
|
|
|
@ -554,4 +518,3 @@ int main(int argc, char** argv) {
|
|
|
|
|
exit(WEXITSTATUS(status));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|