Skip to content

Instantly share code, notes, and snippets.

@pgoodman
Created July 30, 2014 13:21
Show Gist options
  • Save pgoodman/12961865dfa261e30e1e to your computer and use it in GitHub Desktop.
Save pgoodman/12961865dfa261e30e1e to your computer and use it in GitHub Desktop.
Trace logger in Granary+
# Print $arg1 instructions starting at address $arg0.
define pi
set $__rip = $arg0
set $__ni = $arg1
python None ; \
rip = str(gdb.parse_and_eval("$__rip")).lower() ; \
ni = str(gdb.parse_and_eval("$__ni")).lower() ; \
gdb.execute( \
"x/%si %s\n" % (ni, rip), \
from_tty=True, to_string=False) ;
end
# Print the $arg0th most recent entry in the trace log, where 0 is the
# most recent log entry.
#
# An optional second parameter can be specified, which is the number of
# instructions to disassemble from the traced block.
define pt
set $__i = granary_trace_log_index + GRANARY_TRACE_LOG_LENGTH - $arg0
set $__i = ($__i) % GRANARY_TRACE_LOG_LENGTH
set $__r = &(granary_trace_log[$__i])
# Adjust the instruction pointer for the size of `lea rsp, [rsp +- 128]`, but
# only in user space (where redzone protection is needed).
set $__rip = $__r->rip
if $in_user_space
set $__rip = $__rip + 8
end
# Print the regs state.
printf "Trace Log Entry %d\n", $arg0
printf " r15 = %#18lx r14 = %#18lx\n", $__r->r15, $__r->r14
printf " r13 = %#18lx r12 = %#18lx\n", $__r->r13, $__r->r12
printf " r11 = %#18lx r10 = %#18lx\n", $__r->r11, $__r->r10
printf " r9 = %#18lx r9 = %#18lx\n", $__r->r9, $__r->r8
printf " rdi = %#18lx rsi = %#18lx\n", $__r->rdi, $__r->rsi
printf " rbp = %#18lx rbx = %#18lx\n", $__r->rbp, $__r->rbx
printf " rdx = %#18lx rcx = %#18lx\n", $__r->rdx, $__r->rcx
printf " rax = %#18lx rip = %#18lx\n", $__r->rax, $__rip
printf " flags ="
# Print the flags state.
if $__r->rflags & (1 << 0)
printf " CF"
end
if $__r->rflags & (1 << 2)
printf " PF"
end
if $__r->rflags & (1 << 4)
printf " AF"
end
if $__r->rflags & (1 << 6)
printf " ZF"
end
if $__r->rflags & (1 << 7)
printf " SF"
end
if $__r->rflags & (1 << 8)
printf " TF"
end
if $__r->rflags & (1 << 9)
printf " IF"
end
if $__r->rflags & (1 << 10)
printf " DF"
end
if $__r->rflags & (1 << 11)
printf " OF"
end
if $__r->rflags & (1 << 13)
printf " NT"
end
if $__r->rflags & (1 << 16)
printf " RF"
end
printf "\n"
if $argc == 2
printf "\nFirst %d instructions:\n", $arg1
pi $__rip $arg1
end
dont-repeat
end
(gdb) pt 0 10
Trace log entry 0
r15 = 0x3c6ef372 r14 = 0x27999da8
r13 = 0x1f83d9ab r12 = 0x7f23066d7060
r11 = 0x7f23083a5630 r10 = 0x510e527f
r9 = 0xbb67ae85 r9 = 0x6a09e667
rdi = 0x3a6fe667 rsi = 0x7f23083a5900
rbp = 0x9b05688c rbx = 0xf377ed68
rdx = 0x98c7e2a2 rcx = 0xfc08884d
rax = 0x4 rip = 0x7f2305261618
flags = CF SF IF
First 10 instructions:
0x7f2305261618: mov QWORD PTR fs:0xfffffffffffffef8,r12
0x7f2305261621: mov r12,rax
0x7f2305261624: lahf
0x7f2305261625: seto al
0x7f2305261628: xchg r12,rax
0x7f230526162a: mov r14d,r13d
0x7f230526162d: mov r13d,ebp
0x7f2305261630: mov ebp,r10d
0x7f2305261633: mov r10d,edx
0x7f2305261636: mov edx,r15d
(gdb) pt 1
Trace Log Entry 1
r15 = 0x3c6ef372 r14 = 0x5be0cd19
r13 = 0x1f83d9ab r12 = 0x7f23066d7060
r11 = 0x7f23083a5630 r10 = 0x510e527f
r9 = 0xbb67ae85 r9 = 0x6a09e667
rdi = 0x7f23083a5608 rsi = 0x7f23083a5900
rbp = 0x9b05688c rbx = 0x5be0cd19
rdx = 0xa54ff53a rcx = 0x1f83d9ab
rax = 0 rip = 0x7f2305260d40
flags = PF ZF IF
(gdb)
/* Copyright 2014 Peter Goodman, all rights reserved. */
#include "granary/arch/x86-64/asm/include.asm.inc"
START_FILE
DECLARE_FUNC(granary_trace_block_regs)
DEFINE_FUNC(granary_trace_block)
// Saved IP is already on the stack in the form of the return address.
push %rax
push %rcx
push %rdx
push %rbx
push %rbp
push %rsi
push %rdi
push %r8
push %r9
push %r10
push %r11
push %r12
push %r13
push %r14
push %r15
pushfq
lea (%rsp), %rdi;
GRANARY_IF_KERNEL( cli ) // Disable interrupts.
call granary_trace_block_regs
popfq
pop %r15
pop %r14
pop %r13
pop %r12
pop %r11
pop %r10
pop %r9
pop %r8
pop %rdi
pop %rsi
pop %rbp
pop %rbx
pop %rdx
pop %rcx
pop %rax
ret
END_FUNC(granary_trace_block)
END_FILE
/* Copyright 2014 Peter Goodman, all rights reserved. */
#define GRANARY_INTERNAL
#define GRANARY_ARCH_INTERNAL
#include "granary/arch/x86-64/builder.h"
#include "granary/base/string.h"
#include "granary/code/fragment.h"
#include "granary/cache.h"
extern "C" {
// The entrypoint to the trace log. This is an assemble routine that records
// the register state in the form of a `struct RegisterState`, and then passes
// it off to `granary_trace_block_regs`.
extern void granary_trace_block(void);
struct RegisterState {
uint64_t rflags; // Last to be pushed.
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t rdi;
uint64_t rsi;
uint64_t rbp;
uint64_t rbx;
uint64_t rdx;
uint64_t rcx;
uint64_t rax;
uint64_t rip; // Return address.
};
enum {
GRANARY_TRACE_LOG_LENGTH = 1024
};
// The recorded entries in the trace. This is a global variable so that GDB
// can see it.
RegisterState granary_trace_log[GRANARY_TRACE_LOG_LENGTH];
// The index into Granary's trace log. Also a global variable so that GDB can
// easily see it.
unsigned granary_trace_log_index = 0;
// Record an entry in Granary's trace log.
void granary_trace_block_regs(const RegisterState *regs) {
auto index = __sync_add_and_fetch(&granary_trace_log_index, 1U);
auto &log_regs(granary_trace_log[index % GRANARY_TRACE_LOG_LENGTH]);
memcpy(&log_regs, regs, sizeof *regs);
}
} // extern C
#define PREP(...) \
do { \
__VA_ARGS__ ; \
frag->instrs.Prepend(new NativeInstruction(&ni)); \
} while (0)
namespace granary {
namespace arch {
// Adds in some extra "tracing" instructions to the beginning of a basic block.
void AddBlockTracer(Fragment *frag, BlockMetaData *meta,
CachePC estimated_encode_pc) {
Instruction ni;
if (arch::REDZONE_SIZE_BYTES) {
PREP(LEA_GPRv_AGEN(&ni, XED_REG_RSP, BaseDispMemOp(REDZONE_SIZE_BYTES,
XED_REG_RSP,
ADDRESS_WIDTH_BITS)));
}
auto encode_addr = reinterpret_cast<uintptr_t>(estimated_encode_pc);
auto target_addr = UnsafeCast<uintptr_t>(granary_trace_block);
auto target_pc = reinterpret_cast<PC>(target_addr);
auto diff = std::max(encode_addr, target_addr) -
std::min(encode_addr, target_addr);
// TODO(pag): Generalize the following pattern, as in the first Granary.
if (diff > 4187593113UL) { // > ~3.9GB away; don't risk it for a rel32.
auto cache_meta = MetaDataCast<CacheMetaData *>(meta);
auto addr = new NativeAddress(target_pc, &(cache_meta->native_addresses));
PREP(CALL_NEAR_MEMv(&ni, addr));
} else {
PREP(CALL_NEAR_RELBRd(&ni, target_pc));
}
if (arch::REDZONE_SIZE_BYTES) {
PREP(LEA_GPRv_AGEN(&ni, XED_REG_RSP, BaseDispMemOp(-REDZONE_SIZE_BYTES,
XED_REG_RSP,
ADDRESS_WIDTH_BITS)));
}
}
} // namespace arch
} // namespace granary
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment