Hardware interrupt handler in C++ I wrote this class to use a C++ function as hardware interrupt handler. The idea is to have a single entry point where I can switch stacks, and call a chain of std::function objects to handle the interrupt.

It works... but I think it's a pretty ugly hack. Is there anything I could improve here?

class __attribute__((packed)) interrupt_wrapper
{
    using function_ptr = void(*)(unsigned);
    unsigned int_vector;            // [eax-0x1C]
    selector ds;                    // [eax-0x18]
    selector es;                    // [eax-0x16]
    selector fs;                    // [eax-0x14]
    selector gs;                    // [eax-0x12]
    function_ptr entry_point;       // [eax-0x10]
    std::array&lt;byte, 0x40&gt; code;    // [eax-0x0C]

public:
    interrupt_wrapper(unsigned vec, function_ptr f) : int_vector(vec), entry_point(f)
    {
        byte* start;
        std::size_t size;
        asm volatile (
            ".intel_syntax noprefix;"
            "jmp interrupt_wrapper_end%=;"
            // --- \/\/\/\/\/\/ --- //
            "interrupt_wrapper_begin%=:;"   // On entry, the only known register is CS.
            "push ds; push es; push fs; push gs; pusha;"    // 7 bytes
            "call get_eip%=;"  // call near/relative (E8)   // 5 bytes
            "get_eip%=: pop eax;"           // Pop EIP into EAX and use it to find our variables
            "mov ds, cs:[eax-0x18];"        // Restore segment registers
            "mov es, cs:[eax-0x16];"        
            "mov fs, cs:[eax-0x14];"
            "mov gs, cs:[eax-0x12];"
            "push cs:[eax-0x1C];"           // Pass our interrupt number along
            "call cs:[eax-0x10];"           // Call the entry point
            "add esp, 4;"
            "popa; pop gs; pop fs; pop es; pop ds;"
            "sti;"                          // IRET is not guaranteed to set the interrupt flag.
            "iret;"
            "interrupt_wrapper_end%=:;"
            // --- /\/\/\/\/\/\ --- //
            "mov %0, offset interrupt_wrapper_begin%=;"
            "mov %1, offset interrupt_wrapper_end%=;"
            "sub %1, %0;"                   // size = end - begin
            ".att_syntax prefix"
            : "=r" (start)
            , "=r" (size));
        assert(size &lt;= code.size());

        auto* ptr = memory_descriptor(get_cs(), start).get_ptr&lt;byte&gt;(); // Get near pointer to cs:[start]
        std::copy_n(ptr, size, code.data());

        asm volatile (
            ".intel_syntax noprefix;"
            "mov ax, ds;"
            "mov bx, es;"
            "mov cx, fs;"
            "mov dx, gs;"
            ".att_syntax prefix"
            : "=a" (ds)
            , "=b" (es)
            , "=c" (fs)
            , "=d" (gs));
    }

    auto get_ptr(selector cs) { return far_ptr32 { cs, reinterpret_cast&lt;std::size_t&gt;(code.data()) }; }
};
We're missing some pieces, such as the memory_descriptor and far_ptr32 classes and get_cs function and I guessed at the definitions of selector and byte, but with that said, here's what I found that may help you improve your code.

Make the constructor noexcept

It's unlikely that an exception within the constructor would be a welcome event.  For that reason, your constructor should be declared noexcept.

Make function_ptr noexcept

For similar reasons, I'd suggest making function_ptr also noexcept:

using function_ptr = void(*)(unsigned) noexcept;


Be careful with interrupt flags

Instead of disabling all interrupts using sti, it might be better to restore the interrupt flag to whatever it had been.  Although the interrupt could not have been called by hardware if the interrupt flag had been reset (that is, interrupts disabled) but it's not uncommon to chain interrupts by having one hardware interrupt chain to another via explicit call.  In that circumstance, the hardware flag should not be re-enabled lest the interrupt chain be incorrectly interrupted.