Search code examples
cgccinline-assembly

Pass parameters via stack manipulation


I have a quite strange and exotic question. My goal is to program object oriented in C. For this, a common approach is to define function pointers inside a struct and define as first argument an explicit reference to the calling struct:

struct Point {
    int x;
    int y;
    
    int (*sum)(struct Point* p);
};

int sum(struct Point* p) {
    return p->x + p->y;
}

int main() {
    struct Point p;
    p.sum = ∑
    p.sum(&p);
}

However, I was wondering if it is possible to do this without the additional struct Point* argument.

For this I need to manipulate the stack (probably via inline assembly) to have a reference to p which then can be accessed inside sum.

My current idea is to declare an additional local variable right before the function call, which holds a reference to the struct

void* ptr = &p;

and then push this value onto the stack with

__asm__("push %rax\n");

But I couldn't figure out how I can access my pushed value in the sum function. I use GCC on x86.


Solution

  • Thanks for your comments. After a bit of gdb debugging, I was able to figure it out:

    #include <stdio.h>
    
    #define GET_SELF_REFERENCE \
        __asm__("movq 16(%rbp), %r8\n" \
                "movq %r8, -8(%rbp)\n");
    
    #define CALL_RESULT(object, method, result) \
        void* __##object = &object; \
        __asm__("push %rax\n"); \
        result = object.method(); \
        __asm__("pop %rax\n");
    
    
    struct Point {
        int a;
        int b;
        int (*sum)();
    };
    
    void function() {
        struct Point* st = NULL;
        __asm__("movq 16(%rbp), %r8\n"
                "movq %r8, -8(%rbp)\n");
    
        printf("%d\n", st->a);
    }
    
    int sum() {
        struct Point* self = NULL;
        GET_SELF_REFERENCE
    
        return self->a + self->b;
    }
    
    int main() {
        struct Point p;
        p.a = 12;
        p.b = 49;
        p.sum = &sum;
    
        int result;
    
        CALL_RESULT(p, sum, result)
        printf("%d\n", result);
    
        return 0;
    }
    

    I needed to add offset 16 to my rbp to get the address stored in my pr variable.

    For those of you wondering why i want to do this: It's just out of curiosity and as part of an university project. I'd never do this in production code as it is really fragile and essentially useless. I just want to implement a "real" object orientation mechanism like in Python where the self parameter is implicitly handed to the function.