{
  "title": "From execve to main: how control reaches your code",
  "steps": [
    {
      "state": { "view": "ribbons", "scale": "map" },
      "highlights": [".interp", ".text"],
      "caption": "When you run `build/hello`, the kernel's `execve` does not jump straight to your `main`. This is a `ET_DYN` PIE — a file that cannot even find its own addresses until something maps it into memory. So it carries instructions *about how to be started*.\n\nThe exhibit's own data cannot show the kernel's handoff itself — how `execve` maps these segments and builds the initial stack is outside what these bytes record. What the file *does* carry, and what we can follow byte by byte, is a chain: the interpreter it asks for, the library it needs, the entry stub the loader jumps to, and the libc routine that finally calls `main`." },
    {
      "state": { "view": "ribbons", "scale": "map", "select": ".interp", "tab": "strings", "illuminate": ".interp" },
      "highlights": [".interp"],
      "caption": "First link in the chain: `.interp` (28 bytes) holds a plain string — `/lib64/ld-linux-x86-64.so.2`. Program-header index 1, `PT_INTERP`, points at these same bytes, and the `interp_requests` edge lit here arcs to that interpreter.\n\nThis names the *dynamic loader* the kernel starts on this file's behalf. It is itself a real program: in the Reference Linux Archive, `ld-linux-x86-64.so.2` (build-id 8f271b…, glibc 2.43+r22) exports 22 functions — for instance `_dl_catch_exception`, 457 bytes in `elf/dl-catch.c`. Its internals live outside this exhibit, so we cite them but cannot open them here." },
    {
      "state": { "view": "ribbons", "scale": "map", "select": ".dynamic", "illuminate": "libc.so.6" },
      "highlights": [".dynamic"],
      "caption": "The loader's next job is written in `.dynamic` (480 bytes, thirty 16-byte `Elf64_Dyn` records). One of them is a `DT_NEEDED` entry naming `libc.so.6` — the `needs_library` edge arcing out to that chip.\n\nSo before any of your code runs, the loader has a shopping list: map this file, map `libc.so.6`, and wire the two together. `hello` cannot print a character without it — the C library is where the real work lives." },
    {
      "state": { "view": "ribbons", "scale": "map", "examine": 1, "select": ".text", "tab": "disasm" },
      "highlights": [".text"],
      "caption": "Control enters this file at `e_entry = 0x1040` — the label `_start` at the top of `.text` (275 bytes, 68 instructions). `_start` is not your code; it is the small entry stub the toolchain supplies.\n\nIts job is to call into libc. At `0x105f` it does exactly that: `call *0x2f5b(%rip) # 3fc0 <__libc_start_main@GLIBC_2.34>`. Note the `*` — this is an **indirect** call. It does not name `__libc_start_main` directly; it reads a *pointer* from memory at `0x3fc0` and calls whatever that pointer holds." },
    {
      "state": { "view": "ribbons", "scale": "map", "select": ".got", "illuminate": ".rela.dyn" },
      "highlights": [".got", ".rela.dyn"],
      "caption": "That pointer lives here: `.got`, the global offset table, begins at vaddr `0x3fc0` (40 bytes) — the very slot `_start`'s indirect call reads.\n\nBecause `hello` is a PIE, no absolute address is known until load time, so the slot starts empty. The loader fills it by applying `.rela.dyn`'s relocations — the `reloc_patches` edge lit here shows `.rela.dyn` patching `.got` (among `.init_array`, `.fini_array`, and `.data`). Once the relocation is applied, `.got`'s slot at `0x3fc0` resolves to `__libc_start_main`, and `_start`'s indirect call reaches libc." },
    {
      "state": { "view": "ribbons", "scale": "map", "select": "foreign __libc_start_main", "tab": "disasm" },
      "caption": "Follow the pointer and we leave the file. `__libc_start_main` is 336 bytes at `0x277f0` (161,776) in `libc.so.6`'s own address space — the reference glibc 2.43+r22+g8362e8ce10b2-2 (build-id 020d6f7c…), symbol version `GLIBC_2.34`, recovered from `csu/libc-start.c`. The \"Outside this file\" banner means exactly that.\n\nWatch two things it does. At entry (`csu/libc-start.c:242`) it saves its first argument — `main` — onto its stack: `0x2780e mov %rdi,-0x40(%rbp)`. Then, guarded by `0x27812 test %r9,%r9` (source line 311), it registers the dynamic linker's own destructor: `0x2781e call 40950 <__cxa_atexit@@GLIBC_2.2.5>` (line 312) — the one direct call this function makes." },
    {
      "state": { "view": "ribbons", "scale": "map", "select": "foreign __libc_start_main", "tab": "source" },
      "caption": "The same routine in C — recovered `csu/libc-start.c`, sparse with ⋯ for unmapped lines. Its signature (line 234) takes `main` as the *first* parameter, then `argc`, `argv`, and later `init`, `fini`, `rtld_fini`, `stack_end`.\n\nLine 312 is the `__cxa_atexit((void (*)(void *)) rtld_fini, NULL, NULL)` you just watched compile. Line 347 calls `call_init` — the constructor runner the DWARF folds inline here (declared at line 125). And the last statement, line 360, is the handoff itself: `__libc_start_call_main (main, argc, argv …)`. (Since glibc 2.34 the legacy `init` pointer is always NULL, per the line 363 comment — so the `(*init)()` branch above is skipped.)" },
    {
      "state": { "view": "ribbons", "scale": "map", "select": "foreign __libc_start_main", "tab": "disasm" },
      "caption": "Here is line 360 in machine code. `__libc_start_main` reloads the `main` it saved at entry — `0x2786a mov -0x40(%rbp),%rdi` — sets `%rdx` to `argv` and `%esi` to `argc`, then at `0x27874` calls onward into `__libc_start_call_main`.\n\nBe precise about honesty: the archive resolves that call target only as an internal libc address (`0x276c0`, no exported name), so we cite it as an address, not a name. But the source line it maps to is unambiguous — line 360, `__libc_start_call_main(main, …)` — and the register it loads is `main`. This is the last thing libc does before your code runs. (The earlier `call *%r14` you may notice is the legacy `(*init)()` path, where `%r14` holds `init`, not `main`.)" },
    {
      "state": { "view": "ribbons", "scale": "map", "examine": 1, "select": ".text", "tab": "disasm" },
      "highlights": [".text"],
      "caption": "And control lands back in this file, at your code: `main` at `0x1139` in `.text`. `0x1139 push %rbp`, `0x113a mov %rsp,%rbp` — the standard frame — then `0x113d lea 0xec0(%rip),%rax` computes the string's address (`0x1144 + 0xec0 = 0x2004`) and `0x1147 call 1030 <puts@plt>` prints it.\n\nThat is the whole journey. `execve` handed off to the interpreter named in `.interp`; the loader pulled in the `libc.so.6` of `DT_NEEDED` and applied `.rela.dyn` so `_start`'s indirect call through `.got` reached `__libc_start_main`; and `__libc_start_main` — after registering the linker's destructor — handed `main`, `argc`, and `argv` onward to be called. The six lines of C you wrote run only at the very end of that chain." }
  ]
}
