Last active
March 23, 2021 17:36
-
-
Save tenderlove/8217f90abc58579ab520da26676c9d57 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Have you ever wanted lldb to break in a certain place, but you weren't | |
# sure where to set the breakpoint? Look no further than this script! | |
# | |
# This script creates a chunk of executable code that uses the int3 x86 | |
# instruction. This instruction is defined for use by debuggers, and you can | |
# read more about it here: https://en.wikipedia.org/wiki/INT_%28x86_instruction%29#INT3 | |
# | |
# When that instruction is executed, the debugger will halt and you can do | |
# what you need! | |
# | |
# See the bottom of this file for a sample run with lldb. | |
require "fiddle" | |
# Only works on x86_64 and probably only macOS | |
module Break | |
include Fiddle | |
# from sys/mman.h on macOS | |
PROT_READ = 0x01 | |
PROT_WRITE = 0x02 | |
PROT_EXEC = 0x04 | |
MAP_PRIVATE = 0x0002 | |
MAP_ANON = 0x1000 | |
mmap_ptr = Handle::DEFAULT["mmap"] | |
func = Function.new mmap_ptr, [TYPE_VOIDP, TYPE_SIZE_T, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT], TYPE_VOIDP, name: "mmap" | |
# Expose the mmap system call | |
define_singleton_method :mmap do |addr, len, prot, flags, fd, offset| | |
func.call addr, len, prot, flags, fd, offset | |
end | |
mem = mmap 0, 20, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0 | |
# Write machine code to our dynamically allocated memory | |
# save old frame | |
mem[0] = 0x55 # push rbp | |
# new call frame | |
mem[1] = 0x48 # mov | |
mem[2] = 0x89 # rsp | |
mem[3] = 0xe5 # rbp | |
# Hit the debugger | |
mem[4] = 0xcc # int3 | |
mem[5] = 0x5d # popq | |
mem[6] = 0xC3 # ret | |
func = Function.new mem.to_i, [], TYPE_VOIDP | |
define_singleton_method :dance do | |
func.call | |
end | |
end | |
# Example usage. Force lldb to break when n == 0 | |
def fib(n) | |
if n == 0 | |
Break.dance | |
end | |
if n < 2 | |
n | |
else | |
fib(n-2) + fib(n-1) | |
end | |
end | |
fib(20) | |
__END__ | |
$ lldb ./ruby thing.rb | |
lldb scripts for ruby has been installed. | |
(lldb) target create "./ruby" | |
Current executable set to '/Users/aaron/git/ruby/ruby' (x86_64). | |
(lldb) settings set -- target.run-args "thing.rb" | |
(lldb) process launch | |
Process 84310 launched: '/Users/aaron/git/ruby/ruby' (x86_64) | |
Process 84310 stopped | |
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0) | |
frame #0: 0x00000001007c0005 | |
-> 0x1007c0005: popq %rbp | |
0x1007c0006: retq | |
0x1007c0007: addb %al, (%rax) | |
0x1007c0009: addb %al, (%rax) | |
Target 0: (ruby) stopped. | |
(lldb) bt | |
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0) | |
* frame #0: 0x00000001007c0005 | |
frame #1: 0x00007fff2db538e5 libffi.dylib`ffi_call_unix64 + 85 | |
frame #2: 0x00007fff2db5322a libffi.dylib`ffi_call_int + 692 | |
frame #3: 0x00000001007a4e3d fiddle.bundle`nogvl_ffi_call + 61 | |
frame #4: 0x000000010024c4c4 ruby`rb_nogvl(func=(fiddle.bundle`nogvl_ffi_call), data1=0x00007ffeefbfec50, ubf=0x0000000000000000, data2=<unavailable>, flags=<unavailable>) at thread.c:1671:5 | |
frame #5: 0x00000001007a45a6 fiddle.bundle`function_call + 2230 | |
frame #6: 0x00000001002b048c ruby`vm_call_cfunc_with_frame(ec=0x0000000100906960, reg_cfp=0x0000000108127cf0, calling=<unavailable>) at vm_insnhelper.c:2897:11 | |
frame #7: 0x00000001002a5563 ruby`vm_sendish(ec=0x0000000100906960, reg_cfp=0x0000000108127cf0, cd=0x00000001009d8b80, block_handler=<unavailable>, method_explorer=<unavailable>) at vm_insnhelper.c:4498:15 | |
frame #8: 0x0000000100287d42 ruby`vm_exec_core(ec=<unavailable>, initial=<unavailable>) at insns.def:789:11 | |
frame #9: 0x000000010029f309 ruby`rb_vm_exec(ec=0x0000000100906960, mjit_enable_p=<unavailable>) at vm.c:0 | |
frame #10: 0x00000001002b5d0f ruby`invoke_bmethod(ec=0x0000000100906960, iseq=<unavailable>, self=0x000000010192dab8, captured=<unavailable>, me=0x000000010192ca28, type=<unavailable>, opt_pc=0) at vm.c:1292:11 | |
frame #11: 0x00000001002b0eea ruby`vm_call_bmethod_body [inlined] invoke_iseq_block_from_c(ec=0x0000000100906960, captured=0x00000001009dec90, self=0x000000010192dab8, argc=0, argv=<unavailable>, kw_splat=0, passed_block_handler=0x0000000000000000, cref=0x0000000000000000, is_lambda=1, me=0x000000010192ca28) at vm.c:1337:9 | |
frame #12: 0x00000001002b0d4b ruby`vm_call_bmethod_body [inlined] invoke_block_from_c_proc(ec=0x0000000100906960, proc=<unavailable>, self=0x000000010192dab8, argc=0, argv=<unavailable>, kw_splat=0, passed_block_handler=0x0000000000000000, is_lambda=1, me=0x000000010192ca28) at vm.c:1434 | |
frame #13: 0x00000001002b08c6 ruby`vm_call_bmethod_body [inlined] rb_vm_invoke_bmethod(ec=0x0000000100906960, proc=<unavailable>, self=0x000000010192dab8, argc=0, argv=<unavailable>, kw_splat=0, block_handler=0x0000000000000000, me=0x000000010192ca28) at vm.c:1470 | |
frame #14: 0x00000001002b08c6 ruby`vm_call_bmethod_body(ec=<unavailable>, calling=<unavailable>, argv=<unavailable>) at vm_insnhelper.c:2956 | |
frame #15: 0x00000001002aa74a ruby`vm_call_bmethod(ec=0x0000000100906960, cfp=0x0000000108127d28, calling=0x00007ffeefbff208) at vm_insnhelper.c:2976:12 | |
frame #16: 0x00000001002a5563 ruby`vm_sendish(ec=0x0000000100906960, reg_cfp=0x0000000108127d28, cd=0x00000001009d7f00, block_handler=<unavailable>, method_explorer=<unavailable>) at vm_insnhelper.c:4498:15 | |
frame #17: 0x0000000100287d42 ruby`vm_exec_core(ec=<unavailable>, initial=<unavailable>) at insns.def:789:11 | |
frame #18: 0x000000010029f309 ruby`rb_vm_exec(ec=0x0000000100906960, mjit_enable_p=<unavailable>) at vm.c:0 | |
frame #19: 0x00000001000b97f0 ruby`rb_ec_exec_node(ec=0x0000000100906960, n=0x00000001018efec0) at eval.c:317:2 | |
frame #20: 0x00000001000b965c ruby`ruby_run_node(n=0x00000001018efec0) at eval.c:375:30 | |
frame #21: 0x0000000100000f01 ruby`main(argc=<unavailable>, argv=<unavailable>) at main.c:47:9 | |
frame #22: 0x00007fff20530621 libdyld.dylib`start + 1 | |
(lldb) call rb_backtrace() | |
from thing.rb:60:in `<main>' | |
from thing.rb:56:in `fib' | |
from thing.rb:56:in `fib' | |
from thing.rb:56:in `fib' | |
from thing.rb:56:in `fib' | |
from thing.rb:56:in `fib' | |
from thing.rb:56:in `fib' | |
from thing.rb:56:in `fib' | |
from thing.rb:56:in `fib' | |
from thing.rb:56:in `fib' | |
from thing.rb:56:in `fib' | |
from thing.rb:50:in `fib' | |
from thing.rb:43:in `block in <module:Break>' | |
from thing.rb:43:in `call' | |
(lldb) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment