logoalt Hacker News

jychang01/21/20256 repliesview on HN

How do functions that not end in ret work?


Replies

mananaysiempre01/21/2025

A function with an unlikely slowpath can easily end up arranged as

    top part
    jxx slow
    fast middle part
  end:
    bottom part
    ret
  slow:
    slow middle part
    jmp end
There may be more than one slow part, the slow parts might actually be exiled from inside a loop and not a simple linear code path and can themselves contain loops, etc. Play with __builtin_expect and objdump --visualize-jumps a bit and you’ll encounter many variations.
DSMan19527601/21/2025

In addition to what others said, I'd simply point out that all 'ret' does on x86 is pop an address off the top of the stack and jump to it. It's more of a "helper" than a special instruction and it's use is never required as long as you ensure the stack will be kept correct (such as with a tail-call situation).

show 2 replies
duskwuff01/21/2025

The return is somewhere before the end of the function, e.g.

  loop:
    do stuff
    if some condition: return
    do more stuff
    goto loop
Alternatively, the function might end with a tail-call to another function, written as an unconditional branch.
jcranmer01/21/2025

There are things like compiling a tail call as JMP func_addr.

show 2 replies
to11mtm01/21/2025

My gut (been a while since I've been that low level) is various forms of inlining and/or flow continuation (which is kinda inlining, except when we talk about obfuscation/protection schemes where you might inline but then do fun stuff on the inlined version.)

ngneer01/21/2025

If compilation uses jmp2ret mitigation, a trailing ret instruction will be replaced by a jmp to a return thunk. It is up to the return thunk to do as it pleases with program state.