Annotation Types
- Statement: the annotation directly precedes the call site
- the target app may specify a block of statements to execute only on a native run
- Expression: the annotation appears at the beginning of the annotation function
- the remainder of the annotation function only executes on a native run
- the call site is not annotated
Every annotation function is defined as an expression, i.e., its body begins with the annotation tag. If a particular call site is annotated, that invocation becomes a statement, and the annotation function call is skipped in a native run. When calling an annotation as an expression, the annotation function is invoked (i.e., not skipped) to avoid control flow confusion in nested expressions (especially where annotation expressions are nested). The annotation tag is placed inside the annotation function for the expression context because (1) it is easy to find there, (2) it requires only one instrumentation per run, and (3) the function is being invoked anyway.
Annotation Samples from Debug Builds
Unix x64 Expression
C function definition
DR_DEFINE_ANNOTATION(char, dynamorio_annotate_running_on_dynamorio, (), return 0)
C macro for definition of dynamorio_annotate_running_on_dynamorio()
:
#define LABEL_REFERENCE_LENGTH "0x11"
#define LABEL_REFERENCE_REGISTER "%rax"
#define ANNOTATION_FUNCTION_CLOBBER_LIST "%rax","%rcx","%rdx","%rsi","%rdi","%r8","%r9"
#define _CALL_TYPE
#define DR_ANNOTATION_ATTRIBUTES \
__attribute__((noinline, visibility("hidden") _CALL_TYPE))
#define DR_DEFINE_ANNOTATION(return_type, annotation, parameters, body) \
const char *annotation##_label = "dynamorio-annotation:"#annotation; \
DR_ANNOTATION_ATTRIBUTES return_type annotation parameters \
{ \
__label__ native_run, native_end_marker; \
extern const char *annotation##_label; \
__asm__ volatile goto (".byte 0xeb; .byte "LABEL_REFERENCE_LENGTH"; \
mov _GLOBAL_OFFSET_TABLE_,%"LABEL_REFERENCE_REGISTER"; \
bsr "#annotation"_label@GOT,%"LABEL_REFERENCE_REGISTER"; \
jmp %l0;" \
::: ANNOTATION_FUNCTION_CLOBBER_LIST \
: native_run); \
goto native_end_marker; \
native_run: body; \
native_end_marker: ; \
}
ASM for dynamorio_annotate_running_on_dynamorio()
:
40213e: 55 push %rbp # function preamble
40213f: 48 89 e5 mov %rsp,%rbp
402142: eb 11 jmp 402155 # jump to native version
402144: 48 8b 04 25 b8 0e 20 mov 0x200eb8,%rax # GOT offset from (%rip + 4)
40214b: 00
40214c: 48 0f bd 04 25 b0 ff bsr 0xffffffffffffffb0,%rax # annotation name offset in GOT
402153: ff ff # `bsr` indicates function tag
402155: eb 02 jmp 402159
402157: eb 07 jmp 402160
402159: b8 00 00 00 00 mov $0x0,%eax # native version
40215e: eb 00 jmp 402160
402160: 5d pop %rbp
402161: c3 retq
Unix x64 Statement
C code
TEST_ANNOTATION_SET_MODE(init->id, MODE_1,
{
printf("Mode 1 on %d\n", init->id);
});
C macro for TEST_ANNOTATION_SET_MODE()
:
#define LABEL_REFERENCE_LENGTH "0x11"
#define LABEL_REFERENCE_REGISTER "%rax"
#define ANNOTATION_FUNCTION_CLOBBER_LIST "%rax","%rcx","%rdx","%rsi","%rdi","%r8","%r9"
#define DR_ANNOTATION_OR_NATIVE(annotation, native_version, ...) \
({ \
__label__ native_run, native_end_marker; \
extern const char *annotation##_label; \
__asm__ volatile goto (".byte 0xeb; .byte "LABEL_REFERENCE_LENGTH"; \
mov _GLOBAL_OFFSET_TABLE_,%"LABEL_REFERENCE_REGISTER"; \
bsf "#annotation"_label@GOT,%"LABEL_REFERENCE_REGISTER"; \
jmp %l0;" \
::: LABEL_REFERENCE_REGISTER \
: native_run); \
annotation(__VA_ARGS__); \
goto native_end_marker; \
native_run: native_version; \
native_end_marker: ; \
})
#define TEST_ANNOTATION_SET_MODE(context_id, mode, native_version) \
DR_ANNOTATION_OR_NATIVE(test_annotation_set_mode, native_version, context_id, mode)
ASM for annotated call to TEST_ANNOTATION_SET_MODE()
:
401b60: eb 11 jmp 401b73 <main+0x7e6> # jump over the annotation name reference
401b62: 48 8b 04 25 9a 14 20 mov 0x20149a,%rax # GOT offset from %eip
401b69: 00
401b6a: 48 0f bc 04 25 e0 ff bsf 0xffffffffffffffe0,%rax # offset of name reference within GOT (bsf indicates call site tag)
401b71: ff ff
401b73: eb 14 jmp 401b89 <main+0x7fc> # jump to the native version
401b75: 8b 05 e5 15 20 00 mov 0x2015e5(%rip),%eax # annotation args
401b7b: be 01 00 00 00 mov $0x1,%esi
401b80: 89 c7 mov %eax,%edi
401b82: e8 20 06 00 00 callq 4021a7 # annotation call
401b87: eb 17 jmp 401ba0
401b89: 8b 05 d1 15 20 00 mov 0x2015d1(%rip),%eax # native version
401b8f: 89 c6 mov %eax,%esi
401b91: bf b2 25 40 00 mov $0x4025b2,%edi
401b96: b8 00 00 00 00 mov $0x0,%eax
401b9b: e8 f0 f5 ff ff callq 401190 <printf@plt>
401ba0: c7 45 e8 00 00 00 00 movl $0x0,-0x18(%rbp) # app code...
Unix x86 Expression
C function definition
DR_DEFINE_ANNOTATION(char, dynamorio_annotate_running_on_dynamorio, (), return 0)
C macro for definition of dynamorio_annotate_running_on_dynamorio()
:
#define LABEL_REFERENCE_LENGTH "0xc"
#define LABEL_REFERENCE_REGISTER "%eax"
#define ANNOTATION_FUNCTION_CLOBBER_LIST "%rax","%rcx","%rdx"
#define _CALL_TYPE , fastcall
#define DR_ANNOTATION_ATTRIBUTES \
__attribute__((noinline, visibility("hidden") _CALL_TYPE))
#define DR_DEFINE_ANNOTATION(return_type, annotation, parameters, body) \
const char *annotation##_label = "dynamorio-annotation:"#annotation; \
DR_ANNOTATION_ATTRIBUTES return_type annotation parameters \
{ \
__label__ native_run, native_end_marker; \
extern const char *annotation##_label; \
__asm__ volatile goto (".byte 0xeb; .byte "LABEL_REFERENCE_LENGTH"; \
mov _GLOBAL_OFFSET_TABLE_,%"LABEL_REFERENCE_REGISTER"; \
bsr "#annotation"_label@GOT,%"LABEL_REFERENCE_REGISTER"; \
jmp %l0;" \
::: ANNOTATION_FUNCTION_CLOBBER_LIST \
: native_run); \
goto native_end_marker; \
native_run: body; \
native_end_marker: ; \
}
ASM for dynamorio_annotate_running_on_dynamorio()
:
8049b68: 55 push %ebp # function preamble
8049b69: 89 e5 mov %esp,%ebp
8049b6b: eb 0c jmp 8049b79 # jump to native version
8049b6d: a1 93 24 00 00 mov 0x2493,%eax # GOT offset from (%rip + 4)
8049b72: 0f bd 05 d8 ff ff ff bsr 0xffffffd8,%eax # annotation name offset in GOT
8049b79: eb 02 jmp 8049b7d # intermediate jump to native version
8049b7b: eb 07 jmp 8049b84 # jump over native version
8049b7d: b8 00 00 00 00 mov $0x0,%eax # native version
8049b82: eb 00 jmp 8049b84
8049b84: 5d pop %ebp
8049b85: c3 ret
Unix x86 Statement
C code
TEST_ANNOTATION_SET_MODE(init->id, MODE_1,
{
printf("Mode 1 on %d\n", init->id);
});
C macro for TEST_ANNOTATION_SET_MODE()
:
#define LABEL_REFERENCE_LENGTH "0xc"
#define LABEL_REFERENCE_REGISTER "%eax"
#define ANNOTATION_FUNCTION_CLOBBER_LIST "%rax","%rcx","%rdx"
#define DR_ANNOTATION_OR_NATIVE(annotation, native_version, ...) \
({ \
__label__ native_run, native_end_marker; \
extern const char *annotation##_label; \
__asm__ volatile goto (".byte 0xeb; .byte "LABEL_REFERENCE_LENGTH"; \
mov _GLOBAL_OFFSET_TABLE_,%"LABEL_REFERENCE_REGISTER"; \
bsf "#annotation"_label@GOT,%"LABEL_REFERENCE_REGISTER"; \
jmp %l0;" \
::: LABEL_REFERENCE_REGISTER \
: native_run); \
annotation(__VA_ARGS__); \
goto native_end_marker; \
native_run: native_version; \
native_end_marker: ; \
})
#define TEST_ANNOTATION_SET_MODE(context_id, mode, native_version) \
DR_ANNOTATION_OR_NATIVE(test_annotation_set_mode, native_version, context_id, mode)
ASM for annotated call to TEST_ANNOTATION_SET_MODE()
:
8049610: eb 0c jmp 804961e <main+0x6f1> # jump over the annotation name reference
8049612: a1 ee 29 00 00 mov 0x29ee,%eax # GOT offset from %eip
8049617: 0f bc 05 f0 ff ff ff bsf 0xfffffff0,%eax # offset of name reference within GOT (bsf indicates call site tag)
804961e: eb 13 jmp 8049633 <main+0x706> # jump to the native version
8049620: a1 b8 c0 04 08 mov 0x804c0b8,%eax # annotation args
8049625: ba 01 00 00 00 mov $0x1,%edx
804962a: 89 c1 mov %eax,%ecx
804962c: e8 92 05 00 00 call 8049bc3 # annotation call
8049631: eb 15 jmp 8049648 <main+0x71b> # jump over the native version
8049633: a1 b8 c0 04 08 mov 0x804c0b8,%eax # native version
8049638: 89 44 24 04 mov %eax,0x4(%esp)
804963c: c7 04 24 9a 9f 04 08 movl $0x8049f9a,(%esp)
8049643: e8 88 f6 ff ff call 8048cd0 <printf@plt>
8049648: c7 45 e8 00 00 00 00 movl $0x0,-0x18(%ebp) # app code...
Windows x64 Expression
C function definition
DR_DEFINE_ANNOTATION(char, dynamorio_annotate_running_on_dynamorio, (), return 0)
C macro for definition of dynamorio_annotate_running_on_dynamorio()
:
#define DR_DEFINE_ANNOTATION_LABELS(annotation) \
const char *annotation##_expression_label = "dynamorio-annotation:expression:"#annotation; \
const char *annotation##_statement_label = "dynamorio-annotation:statement:"#annotation;
#define DR_DEFINE_ANNOTATION(return_type, annotation, parameters, body) \
DR_DEFINE_ANNOTATION_LABELS(annotation) \
return_type __fastcall annotation parameters \
{ \
DR_ANNOTATION_FUNCTION(annotation, body) \
}
C macro for function tag of dynamorio_annotate_running_on_dynamorio()
:
#define DR_ANNOTATION_FUNCTION(annotation, body) \
if (GET_RETURN_PTR() == (void *) 0) { \
extern const char *annotation##_expression_label; \
__int2c(); \
_m_prefetchw(annotation##_expression_label); \
__debugbreak(); \
} else { \
body; \
}
ASM for annotation in dynamorio_annotate_running_on_dynamorio()
:
0000000140001FF0: 48 8D 04 24 lea rax,[# _AddressOfReturnAddress()
0000000140001FF4: 48 85 C0 test rax,rax
0000000140001FF7: 75 0F jne 0000000140002008 # jump to the native version
0000000140001FF9: CD 2C int 2Ch # detection hint
0000000140001FFB: 48 8B 05 FE BF 02 mov rax,qword ptr [dynamorio_annotate_running_on_dynamorio_expression_label](rsp])
00
0000000140002002: 0F 0D 08 prefetchw [# (guarantee that the label variable gets used)
0000000140002005: CC int 3 # (discourage compiler reorderings)
0000000140002006: EB 02 jmp 000000014000200A # jump over the native version
0000000140002008: 32 C0 xor al,al # native version (return 0)
000000014000200A: F3 C3 rep ret
Windows x64 Statement
C code
TEST_ANNOTATION_SET_MODE(init->id, MODE_1,
{
printf("Mode 1 on %d\n", init->id);
});
C macro for TEST_ANNOTATION_SET_MODE()
:
#define DR_ANNOTATION_OR_NATIVE(annotation, native_version, ...) \
do { \
if ((unsigned __int64) GET_RETURN_PTR() > (0xfffffffffffffff1 - (2 * __LINE__))) { \
extern const char *annotation##_statement_label; \
__int2c(); \
_m_prefetchw(annotation##_statement_label); \
__debugbreak(); \
annotation(__VA_ARGS__); \
} else { \
native_version; \
} \
} while ((unsigned __int64) GET_RETURN_PTR() > (0xfffffffffffffff0 - (2 * __LINE__)))
#define TEST_ANNOTATION_SET_MODE(context_id, mode, native_version) \
DR_ANNOTATION_OR_NATIVE(test_annotation_set_mode, native_version, context_id, mode)
ASM for annotated call to TEST_ANNOTATION_SET_MODE()
:
00000001400019B6: 48 8D 84 24 C8 00 lea rax,[rsp+0C8h](rax]) # _AddressOfReturnAddress() (annotation head)
00 00
00000001400019BE: 48 3D EF FD FF FF cmp rax,0FFFFFFFFFFFFFDEFh
00000001400019C4: 76 1F jbe 00000001400019E5 # jump to the native version (always taken)
00000001400019C6: CD 2C int 2Ch # detection hint
00000001400019C8: 48 8B 05 41 C8 02 mov rax,qword ptr [00
00000001400019CF: 0F 0D 08 prefetchw [rax](test_annotation_set_mode_statement_label]) # (guarantee that the label variable gets used)
00000001400019D2: CC int 3 # (discourage compiler reorderings)
00000001400019D3: BA 01 00 00 00 mov edx,1 # annotations args
00000001400019D8: 8B 0D 82 E7 02 00 mov ecx,dword ptr [# (arbitrary code may appear among the args)
00000001400019DE: E8 8D 06 00 00 call test_annotation_set_mode # annotation call
00000001400019E3: EB 12 jmp 00000001400019F7 # jump over the native version
00000001400019E5: 8B 15 75 E7 02 00 mov edx,dword ptr [140030160h](140030160h]) # native version--calls printf()
00000001400019EB: 48 8D 0D 56 26 02 lea rcx,[00
00000001400019F2: E8 5D 0C 00 00 call printf
00000001400019F7: 48 8D 84 24 C8 00 lea rax,[rsp+0C8h](??_C@_0O@NICOHPLL@Mode?51?5on?5?$CFd?6?$AA@]) # _AddressOfReturnAddress() (annotation tail)
00 00
00000001400019FF: 48 3D EE FD FF FF cmp rax,0FFFFFFFFFFFFFDEEh
0000000140001A05: 77 AF ja 00000001400019B6 # loopback to annotation head (never taken)
0000000140001A07: C7 44 24 50 00 00 mov dword ptr [# app code...
00 00
Windows x86 Expression
C function definition
DR_DEFINE_ANNOTATION(char, dynamorio_annotate_running_on_dynamorio, (), return 0)
C macro for definition of dynamorio_annotate_running_on_dynamorio()
:
#define DR_DEFINE_ANNOTATION(return_type, annotation, parameters, body) \
DR_DEFINE_ANNOTATION_LABELS(annotation) \
return_type __fastcall annotation parameters \
{ \
DR_ANNOTATION_FUNCTION(annotation, body) \
}
C macro for function tag of dynamorio_annotate_running_on_dynamorio()
:
#define DR_ANNOTATION_FUNCTION_INSTANCE(unique_id, annotation, body) \
__asm { \
__asm _emit 0xeb \
__asm _emit 0x06 \
__asm mov eax, annotation##_label \
__asm nop \
__asm jmp PASTE(native_run, unique_id) \
} \
goto PASTE(native_end_marker, unique_id); \
PASTE(native_run, unique_id): body; \
PASTE(native_end_marker, unique_id): ;
#define DR_ANNOTATION_FUNCTION(annotation, body) \
DR_ANNOTATION_FUNCTION_INSTANCE(__COUNTER__, annotation, body)
ASM for dynamorio_annotate_running_on_dynamorio()
:
00401990: 55 push ebp
00401991: 8B EC mov ebp,esp
00401993: EB 06 jmp 0040199B # jump over annotation
00401995: A1 00 B0 42 00 mov eax,dword ptr [_dynamorio_annotate_running_on_dynamorio_label](rsp+50h],0)
0040199A: 90 nop
0040199B: EB 02 jmp 0040199F # jump to native version
0040199D: EB 02 jmp 004019A1 # jump over native version
0040199F: 32 C0 xor al,al # native version
004019A1: 5D pop ebp
004019A2: C3 ret
Windows x86 Statement
C code
TEST_ANNOTATION_SET_MODE(init->id, MODE_1,
{
printf("Mode 1 on %d\n", init->id);
});
C macro for TEST_ANNOTATION_SET_MODE()
:
#define DR_ANNOTATION_OR_NATIVE(annotation, native_version, ...) \
{ \
extern const char *annotation##_name; \
__asm { \
__asm _emit 0xeb \
__asm _emit 0x06 \
__asm mov eax, annotation##_name \
__asm _emit 0x01 \
__asm jmp PASTE(native_execution_, __LINE__) \
__asm jmp PASTE(native_end_marker_, __LINE__) \
} \
annotation(__VA_ARGS__); \
PASTE(native_execution_, __LINE__) : native_version; \
PASTE(native_end_marker_, __LINE__): ; \
}
#define TEST_ANNOTATION_SET_MODE(context_id, mode, native_version) \
DR_ANNOTATION_OR_NATIVE(test_annotation_set_mode, native_version, context_id, mode)
ASM for annotated call to TEST_ANNOTATION_SET_MODE()
:
00401782: EB 06 jmp 0040178A
00401784: A1 BC B0 42 00 mov eax,dword ptr [00401789: 58 pop eax # indicates call site tag (avoids EB 05, which is common in windows core libs)
0040178A: EB 12 jmp 0040179E # jump to the native version
0040178C: BA 01 00 00 00 mov edx,1 # annotation args
00401791: 8B 0D 70 C7 42 00 mov ecx,dword ptr ds:[42C770h](_test_annotation_set_mode_label])
00401797: E8 96 F8 FF FF call @ILT+45(@test_annotation_set_mode@8) # annotation call
0040179C: EB 14 jmp 004017B2 # jump over the native version
0040179E: 8B 0D 70 C7 42 00 mov ecx,dword ptr ds:[# native version
004017A4: 51 push ecx
004017A5: 68 80 3F 42 00 push offset ??_C@_0O@NICOHPLL@Mode?51?5on?5?$CFd?6?$AA@
004017AA: E8 BB 09 00 00 call _printf
004017AF: 83 C4 08 add esp,8
004017B2: C7 45 FC 00 00 00 mov dword ptr [ebp-4](42C770h]),0 # app code...
00
Samples of Special Cases
Nested annotations: Windows x64 (/Ox /GL)
C code for nested annotations
static __inline int
annotated_function()
{
NOTE2(7, 8);
return 9;
}
int main(void)
{
NOTE1(1, 2, annotated_function());
}
ASM for nested annotations:
000000014000F936: 48 8D 5C 24 28 lea rbx,[# _AddressOfReturnAddress()
000000014000F93B: 0F 1F 44 00 00 nop dword ptr [rax+rax](rsp+28h])
000000014000F940: 48 81 FB 6D FF FF cmp rbx,0FFFFFFFFFFFFFF6Dh # NOTE1 head
FF
000000014000F947: 76 44 jbe 000000014000F98D # target is NOTE1 tail
000000014000F949: CD 2C int 2Ch # annotation hint
000000014000F94B: 0F 0D 0D 9E A9 00 prefetchw [# operand is ((char *) NOTE1 label)
00
000000014000F952: CC int 3 # (discourage compiler reorderings)
000000014000F953: 48 81 FB 79 FF FF cmp rbx,0FFFFFFFFFFFFFF79h # NOTE2 head
FF
000000014000F95A: 76 17 jbe 000000014000F973 # target is NOTE2 tail
000000014000F95C: CD 2C int 2Ch # annotation hint
000000014000F95E: 0F 0D 0D DB A9 00 prefetchw [14001A340h](14001A2F0h])
00
000000014000F965: CC int 3 # (discourage compiler reorderings)
000000014000F966: BA 08 00 00 00 mov edx,8 # NOTE2 args
000000014000F96B: 8D 4A FF lea ecx,[000000014000F96E: E8 9D 00 00 00 call note2
000000014000F973: 48 81 FB 78 FF FF cmp rbx,0FFFFFFFFFFFFFF78h # NOTE2 tail
FF
000000014000F97A: 77 D7 ja 000000014000F953
000000014000F97C: BA 02 00 00 00 mov edx,2 # NOTE1 args
000000014000F981: 44 8D 42 07 lea r8d,[rdx+7](rdx-1])
000000014000F985: 8D 4A FF lea ecx,[000000014000F988: E8 B3 00 00 00 call note1
000000014000F98D: 48 81 FB 6C FF FF cmp rbx,0FFFFFFFFFFFFFF6Ch # NOTE1 tail
FF
000000014000F994: 77 AA ja 000000014000F940
000000014000F996: 33 C0 xor eax,eax # app code...
Nested annotations with shared label: Windows x64 (/Ox /GL)
C code for nested annotations with shared label
INLINE double Triangle::get_b()
{
TEST_ANNOTATION_TWO_ARGS(__LINE__, (unsigned int) get_c(), {
printf("Native two-args in Triangle::get_b()\n");
});
return lengths[1](rdx-1]);
}
INLINE double Triangle::get_c()
{
TEST_ANNOTATION_TWO_ARGS(__LINE__, (unsigned int) get_a(), {
printf("Native two-args in Triangle::get_c()\n");
});
TEST_ANNOTATION_THREE_ARGS(__LINE__, 0x77, 0x7890);
return lengths[}
ASM for nested annotations with shared label
00000001400011AD: 48 8D 7C 24 28 lea rdi,[rsp+28h](2];) # _AddressOfReturnAddress()
00000001400011B2: 48 81 FF D9 FE FF cmp rdi,0FFFFFFFFFFFFFED9h
FF
00000001400011B9: 76 79 jbe 0000000140001234 # outer annotation head
00000001400011BB: CD 2C int 2Ch # outer annotation hint
00000001400011BD: 48 8B 05 84 FE 02 mov rax,qword ptr [# shared!!
00
00000001400011C4: 0F 0D 08 prefetchw [rax](test_annotation_two_args_statement_label]) # outer annotation label use
00000001400011C7: CC int 3 # (discourage compiler reorderings)
00000001400011C8: 0F 1F 84 00 00 00 nop dword ptr [# (compiler padded)
00 00
00000001400011D0: 48 81 FF C7 FE FF cmp rdi,0FFFFFFFFFFFFFEC7h # inner annotation head
FF
00000001400011D7: 76 18 jbe 00000001400011F1
00000001400011D9: CD 2C int 2Ch # inner annotation hint
00000001400011DB: 0F 0D 08 prefetchw [rax](rax+rax]) # inner annotation uses shared label
00000001400011DE: CC int 3 # (discourage compiler reorderings)
00000001400011DF: F2 48 0F 2C 53 08 cvttsd2si rdx,mmword ptr [# inner annotation args
00000001400011E5: B9 95 00 00 00 mov ecx,95h
00000001400011EA: E8 A1 02 00 00 call test_annotation_two_args # inner annotation call
00000001400011EF: EB 0C jmp 00000001400011FD # inner annotation jump over native version
00000001400011F1: 48 8D 0D 50 B2 02 lea rcx,[??_C@_0CG@OGPOCELO@Native?5two?9args?5in?5Triangle?3?3get@](rbx+8])
00 # inner annotation native version
00000001400011F8: E8 07 08 00 00 call printf
00000001400011FD: 48 81 FF C6 FE FF cmp rdi,0FFFFFFFFFFFFFEC6h
FF
0000000140001204: 76 09 jbe 000000014000120F # jump over inner annotation tail
0000000140001206: 48 8B 05 3B FE 02 mov rax,qword ptr [00 # inner annotation tail
000000014000120D: EB C1 jmp 00000001400011D0
000000014000120F: BA 77 00 00 00 mov edx,77h # inner three-arg expression args
0000000140001214: 41 B8 90 78 00 00 mov r8d,7890h
000000014000121A: 8D 4A 1F lea ecx,[rdx+1Fh](test_annotation_two_args_statement_label])
000000014000121D: E8 9E 02 00 00 call test_annotation_three_args # inner three-arg expression call
0000000140001222: B9 8C 00 00 00 mov ecx,8Ch # outer annotation args
0000000140001227: F2 48 0F 2C 53 18 cvttsd2si rdx,mmword ptr [000000014000122D: E8 5E 02 00 00 call test_annotation_two_args # outer annotation call
0000000140001232: EB 0C jmp 0000000140001240 # jump over native version
0000000140001234: 48 8D 0D E5 B1 02 lea rcx,[??_C@_0CG@NLJOANAO@Native?5two?9args?5in?5Triangle?3?3get@](rbx+18h])
00
000000014000123B: E8 C4 07 00 00 call printf
0000000140001240: 48 81 FF D8 FE FF cmp rdi,0FFFFFFFFFFFFFED8h # outer annotation tail
FF
0000000140001247: 0F 87 65 FF FF FF ja 00000001400011B2
Detection Algorithms
Unix
- For each ubr with exact byte sequence "`EB XX`" (where "`XX`" is "`11`" on x64 and "`0c`" on x86)
TRY_EXCEPT
:
- Attempt to read the operands of the next 2 instructions as
(GOT + offset)
- The first instruction (referring to
GOT
) must be a mov
- The only src operand must be base/disp
- The second instruction (referring to
offset
) must be a bsr
or bsf
- The only src operand must be base/disp
- If the
(GOT + offset)
can be dereferenced as **(const char ***)
, and the string matches "dynamorio-annotation:(.*)"
- The name of the annotation is the first regex group of the
(GOT + offset)
match
- If the
offset
instruction was bsr
, the code sequence is an expression (appearing at the top of an annotation function body)
- If the
offset
instruction was bsf
, the code sequence is a statement (appearing at an annotation site)
- The current instruction will be a jump to the native version, and the following instruction will be the instrumentation pc
- On
EXCEPT
: clean up resources and ignore any annotations found
Windows x86
- For each ubr with exact byte sequence "`EB 06`"
TRY_EXCEPT
:
- Attempt to read the operand of the next instruction as a memory reference
- The instruction must be a
mov
- The only src operand must be base/disp
- If the operand can be dereferenced as
*(const char **)
, and the resulting string matches "dynamorio-annotation:(.*)"
- The name of the annotation is the first regex group of the match
- Skip the next 1-byte instruction
- If it is "`nop`" (
0x90
), the code sequence appears at the top of an annotation function body
- If it is "`pop eax`" (
0x58
), the code sequence appears at an annotation site
- The current instruction will be a jump to the native version, and the following instruction will be the instrumentation pc
- On
EXCEPT
: clean up resources and ignore any annotations found
Windows x64
- For each cbr
- If
(bb->checked_end == bb->cur_pc)
, execute a safe read of 1 byte; otherwise just read the byte
- Return if the safe read is attempted and fails
- Compare the byte to "`CD`" (interrupt)
- Note: the interrupt should never execute, but this needs to be verified for C# and VB apps
TRY_EXCEPT
:
- Attempt to read the operand of the next instruction as a memory reference
- The instruction may be a
mov
or a prefetchw
- The only src operand must be rel addr
- If the operand can be dereferenced as
*(const char **)
, and the resulting string matches "dynamorio-annotation:(.**)"
- Match the first regex group (i.e., above) against a second regex
"(.**):(.*)"
- If the first group of the second regex matches "expression", the code sequence appears at the top of an annotation function body
- If the first group of the second regex matches "statement", the code sequence appears at an annotation site
- Skip arbitrary instructions past the next "`int 3`" (there should usually just be one intervening instruction)
- Memo the current pc as the instrumentation pc
- If the next 2 instructions are "`<cbr>; int 2c`", repeat "skip arbitrary instructions..."
- On
EXCEPT
: clean up resources and ignore any annotations found
Instrumentation Algorithms
Expression
Occurs at the top of an annotation function body
- For each annotation handler registered for this annotation (by name)
- insert a clean call to the handler at the instrumentation pc (as identified by the Detection Algorithms above)
- terminate the basic block
Statement
Occurs at an annotation site
- Skip translation of the annotation header
- Resume translation at the instrumentation pc (as identified by the Detection Algorithms above)
- Do not terminate the basic block at this point
Arbitrary code can appear within the annotation statement body, for example when the compiler inlines a call that appears in the annotation argument list, like this:
DYNAMORIO_SOME_ANNOTATION(foo(), bar());
Such inlined code can be very complex, including other calls and unrolled loops. To simplify the instrumentation, the annotation statement body is translated into the code cache without annotation-specific introspection. Instead of attempting to insert a clean call in place of the annotation function call (which occurs inside the annotation statement body), that call is translated like any other. The clean calls for registered annotation handlers are only inserted at the top of the annotation function body (see above).