{"id":35145330,"url":"https://github.com/cs01/lldbhotreload","last_synced_at":"2026-05-21T09:05:59.514Z","repository":{"id":325365490,"uuid":"1100182622","full_name":"cs01/lldbhotreload","owner":"cs01","description":"Hot reload C++ functions while debugging with lldb","archived":false,"fork":false,"pushed_at":"2025-11-20T23:21:51.000Z","size":54,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-13T20:42:55.973Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cs01.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-20T00:19:00.000Z","updated_at":"2025-11-20T23:21:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cs01/lldbhotreload","commit_stats":null,"previous_names":["cs01/lldbhotreload"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cs01/lldbhotreload","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cs01%2Flldbhotreload","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cs01%2Flldbhotreload/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cs01%2Flldbhotreload/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cs01%2Flldbhotreload/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cs01","download_url":"https://codeload.github.com/cs01/lldbhotreload/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cs01%2Flldbhotreload/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33295301,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-21T02:57:32.698Z","status":"ssl_error","status_checked_at":"2026-05-21T02:57:31.990Z","response_time":62,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2025-12-28T13:44:30.337Z","updated_at":"2026-05-21T09:05:59.508Z","avatar_url":"https://github.com/cs01.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LLDB Hot Reload for C++\n\nEdit code while debugging and see changes immediately without a restart.\n\n## Quick Start\n\nDownload hotreload.py:\n```\ncurl -O  https://raw.githubusercontent.com/cs01/lldbhotreload/main/src/hotreload.py\n```\n\n```bash\n$ lldb ./your_program\n(lldb) command script import hotreload.py\n(lldb) b your_file.cpp:50\n(lldb) run\n\n# Edit your_file.cpp in your editor and save\n(lldb) hotreload your_file.cpp\n(lldb) continue\n```\n\nYou can also add `command script import /path/to/hotreload.py` to your `~/.lldbinit` so the `hotreload` command is always available.\n\nChanges apply immediately!\n\n## Examples\n\nSee [examples/](examples/) for complete working demos:\n- **[simple/](examples/simple/)** - Basic single-file hot reload\n- **[inter-file-deps/](examples/inter-file-deps/)** - Hot reload functions calling other files\n\n## Requirements\n\n- Linux x86_64\n- LLDB\n- Clang\n\n```bash\n# When building your program:\nclang++ -rdynamic -g -O0 main.cpp utils.cpp -o myapp\n```\n\nThe `-rdynamic` flag exports symbols so hot-reloaded code can find functions from your original binary.\n\n## API\n\n### `hotreload \u003cpath\u003e [flags...]`\nRecompile and patch all functions in a source file.\n\n```bash\nhotreload your_file.cpp\nhotreload src/math.cpp -std=c++20 -I./include\nhotreload lib/utils.cpp -O2 -DDEBUG\n```\n\nCompiler flags precedence:\n1. Explicit flags - If you pass flags on command line, they're used\n2. compile_commands.json - Automatically searches for and loads flags from compilation database\n3. Default flags - Uses `-std=c++17 -O0 -g` if no other source is found\n\n## What Works\n- Free functions (functions not inside classes) with standard return types (`int`, `float`, `double`, `void`)\n- Pointers and references\n- STL types like `std::vector`, `std::string`\n\n## What Doesn't Work\n\n* On-stack functions: Functions currently on the call stack cannot be patched. Set a breakpoint outside the function to reload it.\n* Class Methods\n* Templates\n* Function signature changes\n\nCompilation errors will show if you try to hot reload any of the above.\n\n## Example session\n```\nhotreload example.cpp\nHot reloading: example.cpp\n  Found 1 functions: ['addOne(int)']\n  Compiling .so...\n  → (int)dlclose((void*)0x417300)\n  → dlclose() succeeded\n  → g++ -std=c++17 -g -O0 -fPIC -shared -o /tmp/lldb_hotreload/hotreload_example_de7d20e4.so /tmp/lldb_hotreload/hotreload_example_de7d20e4.cpp\n  Compiled to /tmp/lldb_hotreload/hotreload_example_de7d20e4.so\n  → (void*)dlopen(\"/tmp/lldb_hotreload/hotreload_example_de7d20e4.so\", 2 | 256)\n  → dlopen() returned handle 0x417300\n  → LLDB auto-detected module: hotreload_example_de7d20e4.so\n  Processing addOne(int) @ 0x401176\n  → ((void*(*)())dlsym((void*)0x417300, \"__addOne_hotreload_de7d20e4_ptr\"))())\n  → Resolved addOne(int) to 0x7ffff7fb5169\n  Re-patching addOne(int): 0x401176 → 0x7ffff7fb5169\n  → WriteMemory(0x401176, 21 bytes)\n     [48 b8 69 51 fb f7 ff 7f 00 00 ff e0] + 9 NOPs\n     Disassembly: movabsq $0x7ffff7fb5169, %rax; jmp *%rax; nop×9\n  Deleted 1 old breakpoint(s) from previous hot reloads\n✓ Patched 1/1 functions\n  Auto-breakpoint: addOne(int) at hotreload_example_de7d20e4.cpp:23 (addr 0x7ffff7fb5169)\n============================================================\n  ✓ Created 1 auto-breakpoint(s) in hot-reloaded code\n  Next 'continue' will hit breakpoints in new code!\n  Manual breakpoints: b /tmp/lldb_hotreload/hotreload_example_de7d20e4.cpp:\u003cline\u003e\n============================================================\n```\n\n## How It Works\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│ 1. Edit code.cpp and save                                      │\n└─────────────────────────────────────────────────────────────────┘\n                              │\n                              ▼\n┌─────────────────────────────────────────────────────────────────┐\n│ 2. (lldb) hotreload code.cpp                                    │\n│    → Compile to code_hash123.so with renamed functions          │\n│    → dlopen() loads .so into running process                    │\n│    → dlsym() finds new function addresses                       │\n└─────────────────────────────────────────────────────────────────┘\n                              │\n                              ▼\n┌─────────────────────────────────────────────────────────────────┐\n│ 3. Patch original function with JMP to new code                 │\n│                                                                 │\n│    Original (0x1000):        After patch:                       │\n│    ┌──────────────┐          ┌──────────────┐                  │\n│    │ push rbp     │          │ jmp 0x7f...  │─┐                │\n│    │ mov rbp, rsp │          │ nop nop ...  │ │                │\n│    │ ...          │          │ ...          │ │                │\n│    └──────────────┘          └──────────────┘ │                │\n│                                                │                │\n│    New code (0x7f...):                         │                │\n│    ┌──────────────┐          ◀────────────────┘                │\n│    │ push rbp     │                                             │\n│    │ mov rbp, rsp │                                             │\n│    │ NEW CODE!    │                                             │\n│    │ ret          │                                             │\n│    └──────────────┘                                             │\n└─────────────────────────────────────────────────────────────────┘\n                              │\n                              ▼\n┌─────────────────────────────────────────────────────────────────┐\n│ 4. (lldb) continue                                              │\n│    → Calls to old address execute new code                      │\n│    → Breakpoints auto-updated to new code                       │\n└─────────────────────────────────────────────────────────────────┘\n```\n\nWe load new code via `dlopen()`, then overwrite the old function's first bytes with a JMP instruction. Callers never know — they jump to the old address, hit the JMP, and bounce to the new implementation.\n\n### Background\nOn x86-64 Linux, functions are just sequences of machine code at memory addresses. When you call a function, the CPU jumps to that address and executes. The `dlopen()` API allows a running process to load new shared libraries at runtime.\n\nWhy LLDB? LLDB's JIT expression evaluator (`frame.EvaluateExpression()`) makes this possible. It lets us execute arbitrary C code (like `dlopen()`, `dlsym()`) inside the debugged process without manually injecting shellcode. LLDB also provides access to DWARF debug info, process memory (`WriteMemory()`), and stack introspection.\n\n### 1. DWARF Analysis\nUses LLDB's debug information to discover all functions compiled from the source file.\n\n### 2. Compilation with Function Renaming and Wrappers\nThe system compiles your modified code.\n\n```cpp\n// Your original function (mangled name: _Z9calculatei)\nint calculate(int x) { return x * 2; }\n```\n\n**Step 1:** Rename with content hash:\n```cpp\nint calculate_hotreload_12ab34cd(int x) { return x * 2; }\n```\n\n**Step 2:** Create inline wrapper for intra-file calls:\n```cpp\ninline int calculate(int x) {\n    return calculate_hotreload_12ab34cd(x);\n}\n```\n\n**Step 3:** Add `extern \"C\"` pointer getter for `dlsym()`:\n```cpp\nextern \"C\" {\nvoid* __calculate_hotreload_12ab34cd_ptr() {\n    return (void*)\u0026calculate_hotreload_12ab34cd;\n}\n}\n```\n\nC++ name mangling makes function names unpredictable. The `extern \"C\"` pointer getters give us predictable names for `dlsym()` lookup, while inline wrappers preserve function calls between hot-reloaded functions.\n\n### 3. Dynamic Loading\n- Calls `dlopen()` via `frame.EvaluateExpression()` to load the `.so`\n- Adds module to LLDB with `AddModule()` for debug symbols\n- Uses `dlsym()` to get the pointer-getter, then calls it for the actual address\n\nOn subsequent reloads, we `dlclose()` the old .so and load a new one with a different hash, re-patching the same original address.\n\n### 4. Runtime Patching\nDWARF provides the exact memory address where each old function starts. The system writes a JMP instruction at that address using lldb's `process.WriteMemory()`, replacing the function's prologue with a trampoline to the new code.\n\n### 5. Breakpoint Refresh\nAfter patching, the system automatically refreshes breakpoints. It deletes old breakpoints in patched functions and recreates them at the same source lines, which LLDB resolves to the new module's addresses.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcs01%2Flldbhotreload","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcs01%2Flldbhotreload","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcs01%2Flldbhotreload/lists"}