[TXT]     [HOME]     [TOOLS]     [GAMES]     [RSS]        [ABOUT ME]    [GITHUB]

.-----------------------------------------------------------------------------.
|             ELF Magic Gone Wrong: Debugging SEGFAULTs (x86-64)              |
'-----------------------------------------------------------------------------'
updated: 2025-10-06


Part 1: Understanding Small ELFs and Fixing Broken Tools.

Learning objectives:
- Handcrafted 80-byte Linux x86-64 ELF (with bugs)
- User-space memory violation
- Wrong permission bits in memory mapping
- Broken kernel ELF mapping (what an offset can do)
- Nonexistent ELF mapping (no 'PT_LOAD')

                           .#######################.
                       ##################################.
                    #######################################-#
                 .#########################################-##+#.
               .##############.#..###########.-############-+######.
              ################.+-#+  -#####.-#.############# ## -##-#-.
            ---#################.##..## ..##..############  ## ##.-
          .-##-#.#-########-.# ##.#...#-## + #-        .#-  ###  ++#.  ...
         --+#+##.+#.#####.## +###.-#..#.- # # #+  .--   # ++ #+.--+ #####+
       .#.#.###.-#-######+.##.##.+### .###### #. #######+ +. -- +. +.+.-  #
        -++.####-. .   .##.##+.### ##+##-###### -##-   -.. .-- -+ -.+.-.###
       ---#-# #########.-+-#+.#-.   .+######-.  .++#..---.  ...+....-....###
       --#.-.   .####.+##.  .# #...###############.............---........##
      ...-.#.#-.### #  + ###-##+.##############...............-.  ........###
       -#...+#.#++### #.##.###.##############..#########........--........###
        #++##...#+--######################.###+--+########-............######
        #-#######+#####################-...............####...........-######
       # #-.....#####################.....................................###
      #####.--.......-############............#######+...................## #
      ######..+---+++......######-.........###... ######...............#####.
       ######..-.......+-.....###-.....####.     ########.............#### #.
       #######+.--.......+....###.-......#       ########............####.#
       #########..-....-+.+...###.#........       #######...........- ### .
       #+####+###..-.....---..###.#..........     -#####+...........# ##
       +###########.-.....-+-.###.#...---...-....    ##...............#-
         #####+#####+.--......###.#...........-.................-+++-...
          .############- +  ..###.#...........--....................-....
             .##########+++.+.###.+...............................-......
              ##+#######. # #####..+.....................................
              ####################.#.....................................
             .######################...........................___.....
             +######################--................................
             ########################----...........................
             ########################-----+--....................
            +########################.-+-------######-......
            #########################.    -----############+
           #-########################.+...   .. ############
           #########################+....--.. . #############
          # ######################+++-..-..-.   ############.#
         #. ######+###########++##++++-++.-...  ++###########++
         # #######+#########++++##+++.-+-----.  .++++-####### #
        #  #############+++++++++#++++++++++--+- +++--++++#### #
        # -######+#####++++++++-+#+++++++++++++..#+ ++++++++##+ #
       #  #######+####++--+++-+--#---------++++++++ .+++++++### .#
       # ##############+++------+#++++--------+ # .##+-----+#### #
      #  #######+######+---------#-----------#+#+#+# -++-----#### #
      # ########+######+---------#---------------.-----------+##+# #
     # ################------------------------. . - ---------####+#
     #-###############+--------------------------  .- -.+------###+-#
    .-################------------------------ -. - -. --------+#####
    +#################-----------------------..   - ..-  ..----+#####
    #################+----------------------.-   .  --    ------+#####
   ##################-------------------+-- - .      .     - -+--######
  #-#####+##########+--------------------- --               ++ ---######
 # ######+##########-------------++++----.--       .  .      --- +#######
 # ################+---+--------------- ---           .       ---. #######
# #################------------------ -+---           .        +--++######-
# #################-+-----+++-------.++---            .       -++---#######
# ################---+-----+-----..------ .           .       #+++--+#######
# ################-+---+-+----- -+++++--     .        .       ##+++-+#######
# ###############+-++-++---- ++----++--.                   . -##++++++######
# ###############-++--+-..+- ++++++++--    .              .  ####++++-#####+
# ###############+++--.--. +++++++++++############+#++#++++++####+++++####.#
+ ##############+++.-+--+++++++++++++###############+-+#+-########+++++##+ #
 # #############.+--- ++++++++++++++##+####+#+#####+-++--#-#+####++++++#..#
  # ###########- -+++++++++++++++++..##############-----+++++###.....-++##
  +- ########## ++++++++++++++++++.....---++---------------++##-......++.
   -#.#########+++++++++++++++++++...... ##++++#++++++++++-++#+........+.
     #..######+++++++++++++++++++........  +#######+#######+++........#++
      -# .  +#++++++++++++++++++..........   -##+#+###+#++++.  .......##+
         ##-  ++++++++++++++++++...........  .  ++##++###++   .: ....###+
             ++++++++++++++++++.............. .   +#++#++.  . ::::..#####+
             ++++++++++++++++++-+............. . .  +#-     .:: ::.######+
            .+++++++++++++++++------............  .   .-....  .:: -######+
            ++++++++++++++++++----####............ .. ..--...- ::.########.
            ++++++++++++++++++-#########+...........  ....--.... #########-
           ++++++++++++++++++############## ......... ...-.--..-.-########+
           ++++++++++++++++++##########+++----....... ....-.-.--. -#######+
           ++++++++++++++++++######++-----------....  -......---  .-#######
          +++++++++++++++++++##++-----------------..  ...-.--.-    --######.
          ++++++++++++++++++++--------------------    -.----       ---#####+
          ++++++++++++++++++----------------.-----         .        --+#####
         +++++++++++++++++++---------------.------                  ---#####
         +++++++++++++++++++----------------------                  ---#####

===[ INTRO ]~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This is part two of debugging small ELF64 binaries (for part one, see [ref1]). Here we'll look at an 80-byte overlaid x86-64 ELF binary and walk through how to track down and fix some subtle BUGs inside it. So let's ready our delving skills and delve hard into it!

===[ 80-Byte x86-64 ELF with Bugs ]~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

All chapters in this article use the following 80-byte x86-64 ELF. I've added a few deliberate bugs, marked 'BUG #'. Each chapter fixes one of them using the tools and techniques from the previous article [ref1]. -----------------------------[ elf64-fixme.nasm ]------------------------------
BITS 64
org  0x0000000000000000 ; manual address computation

ehdr:
    db 0x7F                     ; e_ident[EI_MAG]
_code_p1:
_start:
    db 'E'  ; rex.rb  ; 45      ; e_ident[EI_MAG]
    db 'L'  ; rex.wr  ; 4C      ; e_ident[EI_MAG]
    db 'F'  ; rex.rx  ; 46      ; e_ident[EI_MAG]
    ;   ^ rex.rx => register extention for the following instruction (ignored)
    syscall           ; 0F 05   ; read (0, 0, 0) = 0
    ; rax = 0
    ; rcx = next instruction

    mov cl, _str-ehdr ; B1 3A
    push rcx          ; 51                                       ; <-- ORIG #1
    ;db 0xeb                                                     ; <-- BUG #1

    times 6 nop

    ;add eax,0x3e0002 ; 05 02 00 3E 00
    db 0x05                     ; ehdr->e_ident[EI_PAD]
    db 0x02                     ; ehdr->e_type[0]
    db 0x00                     ; ehdr->e_type[1]
    db 0x3e                     ; ehdr->e_machine[0]
    db 0x00                     ; ehdr->e_machine[1]

    pop rsi           ; 5E
    mov dl, 14        ; B2 0E

    ; mov eax,0x1     ; B8 01 00 00 00
    db 0xb8
phdr:
    db 0x01                     ; ehdr->e_entry ; phdr->p_type   ; <-- ORIG #4
    ;db 0x00                    ; ehdr->e_entry ; phdr->p_type   ; <-- BUG #4
    db 0x00                     ; ehdr->e_entry ; phdr->p_type
    db 0x00                     ; ehdr->e_entry ; phdr->p_type
    db 0x00                     ; ehdr->e_entry ; phdr->p_type

    ; jmp short 0x30  ; EB 12
    db 0xeb                     ; ehdr->e_entry ; phdr->p_flags  ; <-- ORIG #2.1
    ;db 0xe0                    ; ehdr->e_entry ; phdr->p_flags  ; <-- BUG #2.1
    db 0x12                     ; ehdr->e_entry ; phdr->p_flags

    ; will not use
    db 0x00                     ; ehdr->e_entry ; phdr->p_flags
    db 0x00                     ; ehdr->e_entry ; phdr->p_flags

    dq phdr - $$                ; ehdr->e_phoff ; phdr->p_offset
    dq 0x12eb00000000+phdr-ehdr ; ehdr->e_shoff ; phdr->p_vaddr  ; <-- ORIG #2.2
    ;dq 0x12e000000000+phdr-ehdr; ehdr->e_shoff ; phdr->p_vaddr  ; <-- BUG #2.2
    ;dq 0x000012eb00000000      ; ehdr->e_shoff ; phdr->p_vaddr  ; <-- BUG #3

_location__0x30:
    syscall           ; 0F 05
    mov al, 0x3c      ; B0 3C   ; sys_exit
    syscall           ; 0F 05

    dw 0x0038                   ; ehdr->e_phentsize ; phdr->p_paddr
    dw 0x0001                   ; ehdr->e_phnum     ; phdr->p_filesz
    dw 0x0000                   ; ehdr->e_shentsize ; phdr->p_filesz
    dw 0x0000                   ; ehdr->e_shnum     ; phdr->p_filesz
    dw 0x0000                   ; ehdr->e_shstrndx  ; phdr->p_filesz
ehdr_size equ $ - ehdr
    dq 0x0000000000000001       ;                   ; phdr->p_memsz ; <-- ORIG #5
    ;dq 0x0000000000000002      ;                   ; phdr->p_memsz ; <-- BUG #5

_str:
    db 'WORKING', 0x0a          ;                   ; phdr->p_align
------------------------------------------------------------------------------- Build:
nasm -f bin elf64-fixme.nasm -o elf64-fixme
chmod 755 ./elf64-fixme
Running it unmodified should work:
$ ./elf64-fixme
WORKING

===[ BUG #1: Classical User-Space Memory Violation ]~~~~~~~~~~~~~~~~~~~~~~~~~~~

The first few bugs are straightforward and can be diagnosed in user space with 'strace'. Let's start with the first. If we comment out 'ORIG #1' and uncomment 'BUG #1', then build and run the binary, it fails with a 'Segmentation fault':
$ ./elf64-fixme
Segmentation fault (core dumped)
When dealing with system errors (and sometimes even logical errors), 'strace' is an essential debugging tool because it can quickly pinpoint the source of a problem:
$ strace ./elf64-fixme
execve("./elf64-fixme", ["./elf64-fixme"], 0x7ffd64047dc0 /* 42 vars */) = 0
read(0, NULL, 0)                        = 0
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x12eaffffff9a} ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)
Here we can see that 'execve(2)' and the first 'read(2)' syscall completed successfully. The program then failed with 'SIGSEGV'. The kernel reports that the fault was caused by accessing unmapped memory ('SEGV_MAPERR' -- Address not mapped to an object [ref6]), with the violation address given as 'si_addr=0x12eaffffff9b'. We can examine it further using GDB with the execve-wrapper [ref1]:
$ gdb -ex 'file ./execve_wrapper' -ex 'catch exec' -ex 'run ./elf64-fixme'

(gdb) x/6i $rip
=> 0x12eb00000001:      rex.RB
   0x12eb00000002:      rex.WR
   0x12eb00000003:      rex.RX syscall
   0x12eb00000006:      mov    cl,0x48
   0x12eb00000008:      jmp    0x12eaffffff9a                 <---
   0x12eb0000000a:      nop
Stepping over the first five instructions triggers the error: Cannot access memory at address 0x12eaffffff9a The issue is a jump to '0x12eaffffff9a', an unmapped address -- exactly as 'strace' reported. Before continuing to the next bug, don't forget to revert the code to its pristine state.

===[ BUG #2: Wrong Permission Bits ]~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The second bug can also be solved easily in user space (but don't worry, this luxury won't last). Comment out all 'ORIG #2.*' lines and uncomment all 'BUG #2.*' lines, then build and run the binary. It again throws a 'Segmentation fault' at us:
$ strace ./elf64-fixme
execve("./elf64-fixme", ["./elf64-fixme"], 0x7ffc5aabbc10 /* 42 vars */) = 0
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_ACCERR, si_addr=0x12e000000001} ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)
This time it tells us that the permissions are wrong: 'SEGV_ACCERR' (invalid permissions for mapped object [ref6]). We can confirm this with GDB:
$ gdb -ex 'file ./execve_wrapper' -ex 'catch exec' -ex 'run elf64-fixme'

(gdb) x/3i $rip
=> 0x12e000000001:      rex.RB
   0x12e000000002:      rex.WR
   0x12e000000003:      rex.RX syscall

(gdb) info proc mappings
       Start Addr         End Addr     Size   Offset  Perms  objfile
   0x12e000000000   0x12e000001000   0x1000      0x0  ---p   /elf64-fixme
                                                        ^
                                                 No EXEC permission!
NOTE: Modern x86-64 CPUs support 'NX/XD' (the non-executable bit for pages), and Linux enables 'EFER.NXE' (Execute Disable Bit Enable). A page is executable only if 'NX=0' in the mapping. This is contrary to user-space behavior: when we call 'mmap/mprotect' with the 'PROT_EXEC' bit, the kernel maps those pages with 'NX' cleared. Btw, there is no per-page read bit on x86. Our bug effectively prevents the CPU from fetching an instruction from that page, and therefore we get 'SEGV_ACCERR'. Before continuing to the next bug, don't forget to revert the code to its pristine state.

===[ BUG #3: Broken Kernel ELF Mapping ]~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

We'll stay on the topic of ELF mapping. Bugs are way more interesting when the kernel is involved. Comment out 'ORIG #2.2' and uncomment 'BUG #3', then build and run it. It fails with ... a 'Segmentation fault' message:
$ ./elf64-fixme
Segmentation fault
Now, let's run GDB (with the execve wrapper) first, since it fails with a mysterious error:
$ gdb -ex 'file ./execve_wrapper' -ex 'catch exec' -ex 'run ./elf64-fixme'

Cannot find user-level thread for LWP 1006822: generic error
(gdb) info registers
Selected thread is running.
(gdb) continue
Cannot execute this command while the selected thread is running.
'Selected thread is running' my ass. In reality, the 'execve()' syscall fails, so the program is definitely not running, as the holy 'strace' confirms:
$ strace ./elf64-fixme
execve("./elf64-fixme", ["./elf64-fixme"], ...) = -1 EINVAL (Invalid argument)
+++ killed by SIGSEGV +++
Segmentation fault (core dumped)
Why does 'execve' return 'EINVAL'? Let's check the Linux kernel source code. Under what conditions can 'load_elf_binary' return 'EINVAL'? Well, there are several, but we can narrow them down since we don't care about 'ET_DYN' or the interpreter (the binary is a static 'ET_EXEC'). That leaves two candidates: 1. 'elf_map' returned a bad address [ref2]:
    error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,
        elf_prot, elf_flags, total_size);
    if (BAD_ADDR(error)) {
      retval = IS_ERR((void *)error) ?
        PTR_ERR((void*)error) : -EINVAL;
      goto out_free_dentry;
    }
2. A memory "overflow" occurred [ref3]:
    /*
     * Check to see if the section's size will overflow the
     * allowed task size. Note that p_filesz must always be
     * <= p_memsz so it is only necessary to check p_memsz.
     */
    if (BAD_ADDR(k) || elf_ppnt->p_filesz > elf_ppnt->p_memsz ||
        elf_ppnt->p_memsz > TASK_SIZE ||
        TASK_SIZE - elf_ppnt->p_memsz < k) {
      /* set_brk can never work. Avoid overflows. */
      retval = -EINVAL;
      goto out_free_dentry;
    }
Anyway, we'll be hunting for 'EINVAL'. But first: what number does 'EINVAL' represent? That way, we know what to look for in GDB:
$ errno -l | grep -w EINVAL
EINVAL 22 Invalid argument
Now we can start QEMU and set a breakpoint on the ELF loader function 'load_elf_binary'. When the breakpoint triggers, set another on 'elf_map', then finish the function so we can inspect the return address:
(gdb) hbreak load_elf_binary
(gdb) c
(gdb) hbreak elf_map
(gdb) c
(gdb) fin
(gdb) info registers rax
rax            0xffffffffffffffea  -22
Ok, we verified that the error originates in the 'elf_map' function. From the linux source code, we see that it has two return points and a prototype with six arguments [ref4]:
static unsigned long elf_map(
    struct file *filep,               // rdi
    unsigned long addr,               // rsi
    const struct elf_phdr *eppnt,     // rdx
    int prot,                         // r10
    int type,                         // r8
    unsigned long total_size          // r9
)
Let's run it again (with a breakpoint on 'elf_map'), but this time check the argument values, focusing on the 'addr' argument:
(gdb) hbreak elf_map
(gdb) c
(gdb) info register rdi   rsi   rdx   r10   r8    r9

rdi            0xffff888005de5300     ; struct file *filep
rsi            0x12eb00000000         ; unsigned long addr
rdx            0xffff888006012c80     ; const struct elf_phdr *eppnt
r10            0x0                    ; int prot
r8             0x100002               ; int type
r9             0x0                    ; unsigned long total_size
From the source code, we see that 'elf_map' calls 'vm_mmap' twice (we're interested in the second call; see [ref5]), and these arguments are passed more or less directly to the 'vm_mmap' function:
map_addr = vm_mmap(filep, addr, size, prot, type, off);
The 'vm_mmap' function prototype is as follows:
unsigned long vm_mmap(
  struct file *file,        // rdi
  unsigned long addr,       // rsi
  unsigned long len,        // rdx
  unsigned long prot,       // r10
  unsigned long flag,       // r8
  unsigned long offset      // r9
)
And here are the arguments when we break on it in GDB:
(gdb) info register rdi   rsi   rdx   r10   r8    r9

rdi            0xffff888005de5300     ; struct file *file
rsi            0x12eb00000000         ; unsigned long addr
rdx            0x1000                 ; unsigned long len
r10            0xffff888006012c80     ; unsigned long prot
r8             0x100002               ; unsigned long flag
r9             0x18                   ; unsigned long offset  <---
We're almost there. Look at the 'vm_mmap' function. It has two conditions that return '-EINVAL' if they are not met:
  if (unlikely(offset + PAGE_ALIGN(len) < offset))
    return -EINVAL;
  if (unlikely(offset_in_page(offset)))
    return -EINVAL;
Both conditions check for an invalid offset. And there is one: '0x18'! Now, how does it get there? For that, we must go back to 'elf_map'. At the beginning of the function, the 'off' value is computed as follows: unsigned long off = eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr); 'eppnt' is of type 'struct elf_phdr *' => the program header. This narrows down the area we should be looking at. There are two likely possibilities: either 'phdr->p_offset' or 'phdr->p_vaddr'. Let's check this insight against the values in our NASM code:
phdr->p_offset = 0x18
phdr->p_vaddr  = 0x000012eb00000000
-----------------------------------
off = 0x18 - ELF_PAGEOFFSET(0x000012eb00000000) =
    = 0x18 - (0x000012eb00000000 & (PAGE_SIZE - 1)) =
    = 0x18 - (0x000012eb00000000 & (0x1000 - 1)) =
    = 0x18 - (0x000012eb00000000 & 0xfff) =
    = 0x18
Well, there it is. Now we know where the '0x18' came from and that 'phdr->p_vaddr' is wrong. It points to the beginning of the binary instead of the Program Header, meaning there is an incorrect or missing offset. Before continuing to the next bug, don't forget to revert the code to its pristine state.

===[ BUG #4: Nonexistent ELF Mapping ]~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

We'll finish the article with -- drum roll, please -- a 'Segmentation fault' message. Comment out 'ORIG #4' and uncomment 'BUG #4', build and run it, and wait for a shocking surprise:
$ ./elf64-fixme
Segmentation fault (core dumped)
This segfault also has a different meaning. At first glance, we see that the 'execve' syscall did not fail and that there is an issue with memory mapping:
$ strace ./elf64-fixme
execve("./elf64-fixme", ["./elf64-fixme"], 0x7ffc427319f0 /* 42 vars */) = 0
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x12eb00000000} ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)
In GDB, we can see that the address '0x12eb00000000' was not mapped at all:
$ gdb -ex 'file ./execve_wrapper' -ex 'catch exec' -ex 'run ./elf64-fixme'

(gdb) x/10i $rip
=> 0x12eb00000000:      Cannot access memory at address 0x12eb00000000

(gdb) info proc mappings
Mapped address spaces:

          Start Addr           End Addr       Size     Offset  Perms  objfile
      0x7ffff7ff9000     0x7ffff7ffd000     0x4000        0x0  r--p   [vvar]
      0x7ffff7ffd000     0x7ffff7fff000     0x2000        0x0  r-xp   [vdso]
      0x7ffffffde000     0x7ffffffff000    0x21000        0x0  rw-p   [stack]
In the previous chapter, we saw that ELF segment mappings are implemented in the 'elf_map' function. Let's use 'bpftrace' to check what it does: 1. Start a new shell to trace the specific PID (otherwise we will get lost among system executions):
$ sh
$ echo $$
1013154
2. Trace the PID and the 'elf_map' function, which we already know maps the binary into memory:
# bpftrace -e 'kr:elf_map {
  if (pid == $1) {
    printf ("%d\n%d %s\n", pid, retval, kstack);
  }
}'  1013154

Attaching 1 probe...
3. Run the program without forking (= replace the shell with our program while keeping the same PID):
$ sh
$ echo $$
1013154

$ exec ./elf64-fixme
4. Check the 'bpftrace' output... aaaand there is nothing! (NOTE: Always make sure you can trace other executables so you don't end up chasing ghosts. Especially in cases like this where nothing is printed. It is better to set two kprobes: one for 'load_elf_binary' and one for 'elf_map', and print the PID/TID for both => the 'load_elf_binary' kprobe triggers when 'execve' occurs, and if the 'elf_map' kprobe doesn't trigger, we know it never even tried to map the ELF into memory.) From the absence of output, we can infer that the mapping is broken. Let's run our 'readsmallelf' tool [ref1] to check whether this hypothesis is correct:
$ ./readsmallelf ./elf64-fixme
...
p_type      = 00000000 (0)                      <--- NO 'PT_LOAD'!
...
Well, yes -- on the 'BUG #2' line, we set 'phdr->p_type' to 'PT_NULL', so there was no 'PT_LOAD' segment and nothing from the binary was mapped into memory. Moreover, if we had properly implemented ELF and program header checks in our 'readsmallelf', this would never happen, as we would catch it soon enough. Before continuing to the next bug, don't forget to revert the code to its pristine state.

===[ OUTRO ]~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

As we have seen, most of the time it's 'Segmentation fault' when working with barely legal small ELFs. Many ways exist to botch ELF creation, especially when making "smart" decisions. For instance, using 'phdr->p_flags' and 'phdr->p_type' as part of the code can make debugging quite an experience. Another common error is 'Exec format error', which indicates a broken ELF structure. Here, the best approach is to make incremental changes, test them, and roll back when an executable breaks. There is one more bug I want to show ('BUG #5'). It's a real treat because the execution partially works. NEXT: Stay tuned for the next article. PREVIOUS: Part 1: Understanding Small ELFs and Fixing Broken Tools. Hack the planet!

===[ References ]~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

[ref1] https://research.h4x.cz/html/2025/2025-09-11--touching_small_elfs-p1-broken_tools.html [ref2] https://elixir.bootlin.com/linux/v6.1.137/source/fs/binfmt_elf.c#L1167 [ref3] https://elixir.bootlin.com/linux/v6.1.137/source/fs/binfmt_elf.c#L1205 [ref4] https://elixir.bootlin.com/linux/v6.1.137/source/fs/binfmt_elf.c#L365 [ref5] https://elixir.bootlin.com/linux/v6.1.137/source/fs/binfmt_elf.c#L394 [ref6] https://www.man7.org/linux/man-pages/man2/sigaction.2.html