{"id":13994299,"url":"https://github.com/tenderlove/fisk","last_synced_at":"2025-05-16T00:06:48.384Z","repository":{"id":37907099,"uuid":"342023294","full_name":"tenderlove/fisk","owner":"tenderlove","description":"A pure Ruby assembler","archived":false,"fork":false,"pushed_at":"2023-04-07T23:47:40.000Z","size":2246,"stargazers_count":307,"open_issues_count":3,"forks_count":9,"subscribers_count":14,"default_branch":"main","last_synced_at":"2025-05-11T11:39:06.588Z","etag":null,"topics":["asm","assembler","assembly","ruby","ruby-assembler","x86","x86-64"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tenderlove.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-02-24T20:13:04.000Z","updated_at":"2025-04-14T04:17:42.000Z","dependencies_parsed_at":"2024-03-24T06:41:59.044Z","dependency_job_id":null,"html_url":"https://github.com/tenderlove/fisk","commit_stats":{"total_commits":123,"total_committers":5,"mean_commits":24.6,"dds":0.08943089430894313,"last_synced_commit":"f56a70866921f0153227ba391ec720c308440247"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenderlove%2Ffisk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenderlove%2Ffisk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenderlove%2Ffisk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenderlove%2Ffisk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tenderlove","download_url":"https://codeload.github.com/tenderlove/fisk/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254442854,"owners_count":22071878,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asm","assembler","assembly","ruby","ruby-assembler","x86","x86-64"],"created_at":"2024-08-09T14:02:48.693Z","updated_at":"2025-05-16T00:06:43.365Z","avatar_url":"https://github.com/tenderlove.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# Fisk - A Pure Ruby x86-64 Assembler\n\nTired of writing Ruby in Ruby?  Now you can write assembly in Ruby with Fisk!\n\nThis is a pure Ruby x86-64 assembler (I guess).  I'm not 100% sure if it counts\nas pure Ruby because it just reads an XML file and metaprograms most of it.\nAnyway, you can use it to write assembly in Ruby, then have it assembled.\n\nI named it after Wilson Fisk mainly because it reminds me of [this\nproject](https://github.com/seattlerb/wilson) and because I've been playing\nlots of Spider-Man.\n\n## Usage\n\nHere is an example of assembling something:\n\n```ruby\nfisk = Fisk.new\n\nbinary = fisk.asm do\n  push rbp\n  mov rbp, rsp\n  int lit(3)\n  pop rbp\n  ret\nend\n```\n\nFisk uses Intel assembly syntax, so the first operand is the *destination*, and\nthe second operand is the source.  So for example `mov rax, imm8(1)` means \"put\nan immediate that is 8 bits wide with the value of 1 in the RAX register\".\n\n## Sizes and Memory Operands\n\nIn order to select the right x86 instruction to emit, Fisk needs to know the\ntypes of the operands.  Register types are already implied, but things like\nimmediates and memory operands need to be specified.\n\nBelow are a few examples.\n\nPutting a 32 bit immediate in the RAX register:\n\n```ruby\nmov rax, imm32(0xFFF)\n```\n\nDereferencing the value in RAX and storing the value in the RAX register:\n\n```ruby\nmov rax, m64(rax)\n```\n\nDereferencing the value 8 bytes away RAX and storing the value in the R9 register:\n\n```ruby\nmov r9, m64(rax, 8)\n```\n\n## Register Allocation\n\nFisk supports simple register assignment.  You can make temporary registers,\nthen have Fisk assign registers for you.  For example:\n\n```ruby\nfisk = Fisk.new\n\n# Make some temporary registers\nreg1 = fisk.register(\"temp1\")\nreg2 = fisk.register(\"temp2\")\n\n# XOR the two virtual registers\nfisk.xor reg1, reg2\n\n# Ask Fisk to assign registers from the pool of registers passed in\nfisk.assign_registers([fisk.r9, fisk.r10])\n```\n\nCurrently, Fisk won't spill registers for you, it just raises an exception.\n\n## Executing Assembly\n\nNow, it's not very fun to assemble something unless you can execute it.  So\nhere is an example of how to execute the above assembly.  This assembly code\nwill send an interrupt and tell the debugger to stop.  So let's write the\nmachine code to some executable memory, and call it from a Ruby program that we\nwill start in lldb.\n\n```ruby\nrequire \"fisk\"\nrequire \"fisk/helpers\"\n\nmodule Break\n  fisk = Fisk.new\n\n  jitbuf = Fisk::Helpers.jitbuffer 4096\n\n  fisk.asm(jitbuf) do\n    push rbp\n    mov rbp, rsp\n    int lit(3)\n    pop rbp\n    ret\n  end\n\n  define_singleton_method :dance!, \u0026jitbuf.to_function([], Fiddle::TYPE_VOID)\nend\n\ndef deep i = 2\n  if i == 0\n    Break.dance!\n  else\n    deep(i - 1)\n  end\nend\n\ndeep\n```\n\nIf we launch this script under lldb, the debugger will halt the process when we call the `dance!` method:\n\n```\n[aaron@tc-lan-adapter ~/g/fisk (master)]$ lldb ~/git/ruby/ruby -- -I lib fun.rb\nerror: module importing failed: invalid pathname\n(lldb) target create \"/Users/aaron/git/ruby/ruby\"\nprocCurrent executable set to '/Users/aaron/git/ruby/ruby' (x86_64).\n(lldb) settings set -- target.run-args  \"-I\" \"lib\" \"fun.rb\"\n(lldb) process launch\nProcess 33042 launched: '/Users/aaron/git/ruby/ruby' (x86_64)\nProcess 33042 stopped\n* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)\n    frame #0: 0x00000001007f4005\n-\u003e  0x1007f4005: popq   %rbp\n    0x1007f4006: retq   \n    0x1007f4007: addb   %al, (%rax)\n    0x1007f4009: addb   %al, (%rax)\nTarget 0: (ruby) stopped.\n(lldb) bt\n* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)\n  * frame #0: 0x00000001007f4005\n    frame #1: 0x00007fff2db538e5 libffi.dylib`ffi_call_unix64 + 85\n    frame #2: 0x00007fff2db5322a libffi.dylib`ffi_call_int + 692\n    frame #3: 0x0000000107458e3d fiddle.bundle`nogvl_ffi_call(ptr=0x00007ffeefbf9bb0) at function.c:204:5\n    frame #4: 0x00000001002e704f ruby`rb_nogvl(func=(fiddle.bundle`nogvl_ffi_call at function.c:201), data1=0x00007ffeefbf9bb0, ubf=0x0000000000000000, data2=0x0000000000000000, flags=0) at thread.c:1671:5\n    frame #5: 0x00000001002e7570 ruby`rb_thread_call_without_gvl(func=(fiddle.bundle`nogvl_ffi_call at function.c:201), data1=0x00007ffeefbf9bb0, ubf=0x0000000000000000, data2=0x0000000000000000) at thread.c:1787:12\n    frame #6: 0x00000001074585a6 fiddle.bundle`function_call(argc=0, argv=0x00000001080280c8, self=0x0000000128f85fd8) at function.c:375:15\n    frame #7: 0x0000000100383ca7 ruby`call_cfunc_m1(recv=0x0000000128f85fd8, argc=0, argv=0x00000001080280c8, func=(fiddle.bundle`function_call at function.c:211)) at vm_insnhelper.c:2594:12\n    frame #8: 0x000000010037ef99 ruby`vm_call_cfunc_with_frame(ec=0x0000000100b069b0, reg_cfp=0x0000000108127eb0, calling=0x00007ffeefbfa030) at vm_insnhelper.c:2924:11\n    frame #9: 0x0000000100377603 ruby`vm_call_cfunc(ec=0x0000000100b069b0, reg_cfp=0x0000000108127eb0, calling=0x00007ffeefbfa030) at vm_insnhelper.c:2945:12\n    frame #10: 0x0000000100376f2b ruby`vm_call_method_each_type(ec=0x0000000100b069b0, cfp=0x0000000108127eb0, calling=0x00007ffeefbfa030) at vm_insnhelper.c:3414:16\n    frame #11: 0x00000001003769e9 ruby`vm_call_method(ec=0x0000000100b069b0, cfp=0x0000000108127eb0, calling=0x00007ffeefbfa030) at vm_insnhelper.c:3507:20\n    frame #12: 0x00000001003578f5 ruby`vm_call_general(ec=0x0000000100b069b0, reg_cfp=0x0000000108127eb0, calling=0x00007ffeefbfa030) at vm_insnhelper.c:3550:12\n    frame #13: 0x000000010036d688 ruby`vm_sendish(ec=0x0000000100b069b0, reg_cfp=0x0000000108127eb0, cd=0x0000000100e2be30, block_handler=0x0000000000000000, method_explorer=mexp_search_method) at vm_insnhelper.c:4525:15\n    frame #14: 0x000000010033f3de ruby`vm_exec_core(ec=0x0000000100b069b0, initial=0x0000000000000000) at insns.def:789:11\n    frame #15: 0x0000000100361d6e ruby`rb_vm_exec(ec=0x0000000100b069b0, mjit_enable_p=true) at vm.c:2162:22\n    frame #16: 0x00000001003884c5 ruby`invoke_bmethod(ec=0x0000000100b069b0, iseq=0x000000010781d168, self=0x0000000128f7d180, captured=0x00000001294ef620, me=0x0000000128f85f10, type=0x0000000022220101, opt_pc=0) at vm.c:1292:11\n    frame #17: 0x0000000100360321 ruby`rb_vm_invoke_bmethod [inlined] invoke_iseq_block_from_c(ec=0x0000000100b069b0, captured=0x00000001294ef620, self=0x0000000128f7d180, argc=0, argv=0x00007ffeefbfcab0, kw_splat=0, passed_block_handler=0x0000000000000000, cref=0x0000000000000000, is_lambda=1, me=0x0000000128f85f10) at vm.c:1337:9\n    frame #18: 0x000000010036017d ruby`rb_vm_invoke_bmethod [inlined] invoke_block_from_c_proc(ec=0x0000000100b069b0, proc=0x00000001294ef620, self=0x0000000128f7d180, argc=0, argv=0x00007ffeefbfcab0, kw_splat=0, passed_block_handler=0x0000000000000000, is_lambda=1, me=0x0000000128f85f10) at vm.c:1434\n    frame #19: 0x00000001003600cf ruby`rb_vm_invoke_bmethod(ec=0x0000000100b069b0, proc=0x00000001294ef620, self=0x0000000128f7d180, argc=0, argv=0x00007ffeefbfcab0, kw_splat=0, block_handler=0x0000000000000000, me=0x0000000128f85f10) at vm.c:1470\n    frame #20: 0x000000010037f3e7 ruby`vm_call_bmethod_body(ec=0x0000000100b069b0, calling=0x00007ffeefbfcd20, argv=0x00007ffeefbfcab0) at vm_insnhelper.c:2983:11\n    frame #21: 0x0000000100377e8f ruby`vm_call_bmethod(ec=0x0000000100b069b0, cfp=0x0000000108127ee8, calling=0x00007ffeefbfcd20) at vm_insnhelper.c:3003:12\n    frame #22: 0x00000001003770c9 ruby`vm_call_method_each_type(ec=0x0000000100b069b0, cfp=0x0000000108127ee8, calling=0x00007ffeefbfcd20) at vm_insnhelper.c:3440:16\n    frame #23: 0x00000001003769e9 ruby`vm_call_method(ec=0x0000000100b069b0, cfp=0x0000000108127ee8, calling=0x00007ffeefbfcd20) at vm_insnhelper.c:3507:20\n    frame #24: 0x00000001003578f5 ruby`vm_call_general(ec=0x0000000100b069b0, reg_cfp=0x0000000108127ee8, calling=0x00007ffeefbfcd20) at vm_insnhelper.c:3550:12\n    frame #25: 0x000000010036d688 ruby`vm_sendish(ec=0x0000000100b069b0, reg_cfp=0x0000000108127ee8, cd=0x0000000100e2bcb0, block_handler=0x0000000000000000, method_explorer=mexp_search_method) at vm_insnhelper.c:4525:15\n    frame #26: 0x000000010033f3de ruby`vm_exec_core(ec=0x0000000100b069b0, initial=0x0000000000000000) at insns.def:789:11\n    frame #27: 0x0000000100361de7 ruby`rb_vm_exec(ec=0x0000000100b069b0, mjit_enable_p=true) at vm.c:2171:22\n    frame #28: 0x0000000100363070 ruby`rb_iseq_eval_main(iseq=0x000000010781da28) at vm.c:2419:11\n    frame #29: 0x00000001000dbefb ruby`rb_ec_exec_node(ec=0x0000000100b069b0, n=0x000000010781da28) at eval.c:317:2\n    frame #30: 0x00000001000dbd83 ruby`ruby_run_node(n=0x000000010781da28) at eval.c:375:30\n    frame #31: 0x00000001000036fc ruby`main(argc=4, argv=0x00007ffeefbff5f8) at main.c:47:9\n    frame #32: 0x00007fff20530621 libdyld.dylib`start + 1\n    frame #33: 0x00007fff20530621 libdyld.dylib`start + 1\n```\n\nNote that in order to produce a stack trace like the above, the Ruby binary must include the debugging symbols (otherwise, the `ruby` frames will not be displayed); this is accomplished by specifying the `--ggdb3` compiler flag:\n\n- if compiling Ruby from source, use `./configure optflags=-gddb3` in the build process\n- if using a version manager, refer to the help; example for RVM: `optflags=\"-ggdb3\" rvm install 3.0.2 --disable-binary`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftenderlove%2Ffisk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftenderlove%2Ffisk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftenderlove%2Ffisk/lists"}