systems programming

The Hidden Machine Behind OOP, SEH, and Concurrency

The Hidden Machine Behind OOP, SEH, and Concurrency

The moment OOP stops being magic

There’s a particular kind of “wait… what?” moment that assembly learning brings. You write something that feels like high-level magic in a language like C++: a virtual method call, a try/except block, a couple of threads coordinating with a lock. Everything looks clean.

Then you drop down to 64-bit assembly and suddenly the magic has receipts.

A virtual method call becomes a chain of pointer reads and an indirect jump. An exception becomes a promise about unwinding and control transfer. Concurrency becomes raw scheduling reality plus the tiny, precise rules that keep two cores from tearing the same data apart.

This is the spirit behind The Art of 64-Bit Assembly, Volume 2: taking constructs you’ve used in higher-level languages and rebuilding them from the instruction level, where “works on my machine” stops being a strategy.

One question that keeps coming up in searches is: what does a vtable actually look like in memory? The answer is less mystical than it first feels.


vtables: method calls as pointer choreography

In object-oriented languages, a virtual function is a method that can be overridden by a derived type, with the decision of which method runs happening at runtime.

To make that runtime decision cheap, most ABIs (Application Binary Interfaces, meaning the calling and data-layout conventions a platform uses) represent polymorphism using a vtable.

A vtable (virtual method table) is typically a contiguous array of function pointers. A function pointer is a stored address of executable code. Each object that participates in polymorphism usually contains a pointer to its vtable (sometimes embedded directly at the start of the object layout).

So what does a virtual dispatch do?

  1. Read the object’s vtable pointer.
  2. Index into the vtable to find the target function pointer.
  3. Call through that function pointer (an indirect call).

In assembly terms, it’s mostly “load, load again, then indirect call.” That’s why so many explanations that sound plausible still feel shallow: if the vtable layout, calling convention, or object/vtable relationship is even slightly off, the CPU will happily call the wrong address.

Here’s an illustrative memory picture (conceptual, not a strict promise of every compiler’s exact layout):

; object memory
[ object + 0x00 ] -> vtable_ptr
[ object + 0x08 ] -> fields...

; vtable memory
[ vtable_ptr + 0x00 ] -> &Method0
[ vtable_ptr + 0x08 ] -> &Method1
[ vtable_ptr + 0x10 ] -> &Method2
...

And the dispatch sequence becomes:

; rdi = pointer to object (common pattern on Windows x64 for first arg)
mov rax, [rdi]; rax = vtable_ptr
mov rcx, [rax + idx*8]; rcx = function pointer
call rcx; indirect call

Even if the exact registers differ in real code, the shape is the lesson: vtables turn a language feature into data and then turn data into control flow.

The tricky part: conventions

What breaks “when you deviate from convention” is usually not the concept—it’s the details:

  • The vtable must match the ABI’s expectations for how parameters are passed.
  • The this pointer must be passed the way the callee expects.
  • Inheritance and overrides must map to the correct vtable slot.
  • On some systems, there may be additional hidden entries or adjustment thunks for multiple inheritance.

If that feels like a lot, it’s because it is—at first. But once you see every virtual call as “dereference pointers until you reach code,” the rest becomes bookkeeping.


Exceptions: the difference between “throw” and “control transfer”

Higher-level languages describe exceptions using keywords, stack traces, and runtime-managed semantics. Assembly has none of that built in.

On Windows, structured exception handling (SEH) is the mechanism that lets the operating system and runtime cooperate when hardware exceptions or explicit raises occur.

A hardware exception is what the CPU triggers for things like divide-by-zero, access violations, and illegal instructions.

SEH isn’t “just catch a thing.” It’s more like a carefully designed contract:

  • Where should execution continue if an exception happens?
  • How should the stack be unwound (meaning: reversed back to a safe state so handlers can run)?
  • What registers and machine state should be restored?

On 32-bit Windows, SEH is often explained as a linked list of registration records stored on the stack. On 64-bit Windows, the story involves unwind metadata and runtime support, but the essence remains: the system needs structured information to find the right handler.

What compilers really do for try/except

When you write a try/except, the compiler emits two categories of machinery:

  1. A protected region description (so the runtime knows what to unwind).
  2. Handler code (so there’s a control transfer target).

From an assembly perspective, the “art” is realizing that an exception is a cross-cutting flow change. It can’t be implemented with a single jump; it must cooperate with unwind rules.

So instruction-level SEH work tends to look like:

  • Installing exception metadata/records for a scope.
  • Ensuring the stack frame layout matches the unwind rules.
  • Writing handlers that run with a consistent view of state.

If you’ve only ever used exceptions at the language level, this can feel intimidating. It helps to remember: the OS and runtime are doing for SEH what the assembler does not automatically provide—state choreography.


Concurrency: when threads meet shared memory

Concurrency is where assembly learning stops being philosophical and becomes physical. Two execution contexts can interleave at instruction granularity.

A thread is a separately scheduled stream of execution inside a process (a process is the operating system container for resources like memory mappings).

When threads share memory, the big enemy is a race condition: when the correctness depends on timing, and timing changes produce wrong results.

The minimal building blocks

At the machine level, concurrency is built from a few core ideas:

  • Atomic operations: operations that appear indivisible to other threads.
  • Synchronization primitives: mechanisms that coordinate progress, like mutexes or events.
  • Memory ordering rules: guarantees about when writes become visible to other cores.

In assembly, you often use atomic instructions directly. A common example pattern is “compare-and-swap”:

  • CAS (compare-and-swap) compares a memory value to an expected value.
  • If it matches, it atomically swaps in a new value.
  • If it doesn’t match, you retry.

That retry loop is the heartbeat of many lock-free structures.

Locks and the Windows reality

Higher-level languages often present a lock as a concept. Assembly presents it as a pile of state transitions.

A mutex (mutual exclusion object) is a synchronization primitive that ensures only one thread enters a critical section at a time.

Windows also provides multiple mechanisms (mutexes, critical sections, events, slim reader/writer locks, and more). The core assembly lesson stays consistent: you call into the kernel/user runtime to block and wake, or you avoid blocking by spinning and using atomics.

If the code is “real” (not toy concurrency), it usually needs:

  • A correct locking protocol.
  • Careful lifetime management for shared objects.
  • Unambiguous ownership of what each thread is allowed to read/write.

It’s not enough to be thread-safe “in theory.” At the instruction level, safety is a sequence of observable effects.


Why rebuilding these features matters

By the time you manually implement vtables, SEH scope setup, and concurrency synchronization, you stop treating language features as spells. You start treating them as engineering artifacts.

  • Virtual dispatch is data-driven control flow.
  • Exceptions are state-aware control transfer with unwind correctness.
  • Concurrency is shared-memory coordination governed by atomicity and ordering.

Once those mental models click, the rest of assembly stops being a jumble of mnemonics. It becomes a craft: matching ABI details, respecting runtime contracts, and using the smallest correct primitives to build bigger behavior.

And that’s the real “art” part—because the CPU will run your program even when your abstraction is wrong. The craft is making sure it never has the chance to.


Closing thought

64-bit assembly isn’t just “harder C.” It’s a different level of responsibility. OOP, exceptions, and concurrency don’t disappear there—they get unwrapped.

And once you’ve seen what’s inside those wrappers, those features in higher-level languages start feeling less like magic and more like a well-lit machine you finally understand.

ahsan

ahsan

Hello! I am Mr Ahsan, the writer of the Website. I am from Netherland. I like to write about technology and the news around it.

Comments (0)

No comments yet. Be the first to respond!

Leave a Comment

Your comment will be visible after review.