Skip to content

Instantly share code, notes, and snippets.

@tenderlove
Last active March 23, 2021 17:36
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tenderlove/8217f90abc58579ab520da26676c9d57 to your computer and use it in GitHub Desktop.
Save tenderlove/8217f90abc58579ab520da26676c9d57 to your computer and use it in GitHub Desktop.
# 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