Search code examples
cwindowscallnasmextern

How to call C extern function and get return struct?


I have an extern function and a struct defined in token.c:

#include "stdio.h"

typedef struct token {
    int start;
    int length;
} t;

extern t get_token(int, int);

t get_token(int s, int l) {
    printf("[C] new token: start [%d]  length [%d]\n\n", s, l);

    t m_T = {};
    m_T.start = s;
    m_T.length = l;

    return m_T;
}

... so that I can call _get_token from my assembly and get a new token. In make_token.asm I have the following:

SECTION .data       ; initialized data
    mtkn:       db  "call: token(%d, %d)", 10, 0
    mlen        db  "length: %d", 10, 0
    mstt:       db  "start: %d", 10, 0

    mend:       db  10, "*** END ***", 10, 0

SECTION .text       ; code
    extern _get_token

    extern _printf

    global _main

    _main:
        ;   stash base stack pointer
        push    ebp
        mov     ebp,        esp

        mov     eax,        5
        mov     ebx,        10
        push    ebx                     ; length
        push    eax                     ; start
        call    _get_token              ; get a token
        mov     [tkn],      eax

        add     esp,        8

        ;   test token properties
        push    DWORD [tkn]
        push    mstt
        call    _printf

        push    DWORD [tkn + 4]
        push    mlen
        call    _printf

        add     esp,        16

        .end:
        push    DWORD   mend
        call    _printf
        ;   restore base stack pointer
        mov     esp,        ebp
        pop     ebp

SECTION .bss        ; uninitialized data
    tkn:        resd        1

The output is:

[C] new token: start [5] length [10]

start: 5
length: 0

What am I missing to get both start and length? The output verifies that the extern function in C is getting called and the values are pushed into the function.


Solution

  • I decided instead of a single token at a time, that I should allocate a buffer and fill it: determine how many tokens are required, malloc the buffer, call get_tokens and pass in the pointer to the buffer and number of tokens.

    The get_tokens method fills the buffer and returns a count of tokens created.

    The assembly then iterates the token buffer and displays the values - start and length - for each token.

    token.c:

    #include <stdio.h>
    
    typedef struct token {
        int start;
        int length;
    } t;
    
    extern int get_tokens(t*, int);
    extern int token_size();
    
    /*
        p_t: pointer to allocated buffer
        num: number of tokens with which to fill buffer
    */
    int get_tokens(t* p_t, int num) {
        printf("[C] create %d tokens: %d bytes\n", num, token_size() * num);
        int idx = 0;
    
        while (idx < num) {
            //  values are arbitrary for testing purposes
            t tkn = {idx, idx * 10};
            p_t[idx] = tkn;
            printf("[C] [%d] start: %d; len: %d\n", idx, tkn.start, tkn.length);
    
            ++idx;
        }
    
        return idx;
    }
    
    int token_size() {
        return sizeof(t);
    }
    

    make_tokens.asm:

    SECTION .data       ; initialized data
        endl:       db  10, 0
        mszt:       db  "token size: %d bytes", 10, 0
        tk_info:    db  "[%d]: s[%d] l[%d]", 10, 0
        mlen        db  "length: %d", 10, 0
        mstt:       db  "start: %d", 10, 0
    
        mend:       db  10, "*** END ***", 10, 0
    
        mt1         db  "malloc space for 3 tokens: %d bytes", 10, 0
        mty         db  10, "success", 10, 0
        mtn         db  10, "fail", 10, 0
    
    SECTION .text       ; code
        extern _get_tokens
        extern _token_size
    
        extern _free
        extern _malloc
        extern _printf
    
        global _main
    
        _main:
            ;   stash base stack pointer
            push    ebp
            mov     ebp,        esp
    
            ;   get token size
            call    _token_size
            mov     [tsz],      eax
    
            push    DWORD [tsz]
            push    DWORD mszt
            call    _printf
            add     esp,        8
    
            mov     eax,        [tsz]
            mov     edx,        3               
            mul     edx
            mov     [tbsz],     eax
    
            push    DWORD [tbsz]
            push    DWORD mt1
            call    _printf
            add     esp,        8
    
            push    DWORD [tbsz]               ; malloc 3 tokens
            call    _malloc
            mov     [tkn_buf],  eax
            add     esp,        4
    
            mov     ecx,        3               ; 3 tokens
            push    DWORD ecx
            push    DWORD [tkn_buf]
            call    _get_tokens
            add     esp,        8
            cmp     eax,        3
            je      .yes
    
            .no:
            push    DWORD mtn
            call    _printf
            add     esp,        4
            jmp     .end
    
            .yes:
            push    DWORD mty
            call    _printf
            add     esp,        4
    
            mov     ecx,        0
            mov     ebx,        [tkn_buf]
            .loopTokens:
                mov     eax,    [tsz]       ; determine next token
                mul     ecx                 ; start location => eax
    
                mov     edi,    ecx         ; preserve counter
    
                push    DWORD [ebx + eax + 4]   ; length
                push    DWORD [ebx + eax]       ; start
                push    DWORD ecx
                push    DWORD tk_info
                call    _printf
                add     esp,    16
    
                mov     ecx,    edi
                inc     ecx
                cmp     ecx,    3
                jl      .loopTokens
    
            .end:
            push    DWORD [tkn_buf]
            call    _free
    
            push    DWORD mend
            call    _printf
            ;   restore base stack pointer
            mov     esp,        ebp
            pop     ebp
    
    SECTION .bss        ; uninitialized data
        tkn_buf:    resd        1
        tbsz:       resd        1
        tsz:        resd        1
    

    ...and the resulting output:

    token size: 8 bytes
    malloc space for 3 tokens: 24 bytes
    [C] create 3 tokens: 24 bytes
    [C] [0] start: 0; len: 0
    [C] [1] start: 1; len: 10
    [C] [2] start: 2; len: 20

    success
    [0]: s[0] l[0]
    [1]: s[1] l[10]
    [2]: s[2] l[20]