【问题标题】:How to call C extern function and get return struct?如何调用 C 外部函数并获取返回结构?
【发布时间】:2015-06-30 21:16:38
【问题描述】:

我有一个外部函数和一个在token.c 中定义的struct

#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;
}

... 这样我就可以从我的程序集中调用_get_token 并获得一个新令牌。在 make_token.asm 我有以下内容:

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

输出是:

[C] 新令牌:开始 [5] 长度 [10]

开始:5
长度:0

我缺少什么来获得开始和长度?输出验证 C 中的 extern 函数是否被调用,并且值被推送到函数中。

【问题讨论】:

  • 如果我正确阅读了this table,那么您的答案的后半部分应该在 EDX 中 - “通过 EAX:EDX 寄存器返回大小为 33-64 位的 POD 返回值。”
  • 1) 不要 typedef 结构定义。 2)由于该结构是在不同的文件中声明的,因此定义应该在头文件中,即两个源文件#include。 3) 引用外部函数不是通过 extern 完成的。而是在一个通用头文件中,该头文件包含在声明函数的源文件和通过#include 的本地文件中。头文件将具有原型,链接器处理其余部分。 4) #include 文件的搜索顺序取决于使用 '' 还是 '"..."'。系统标头应始终使用 ''
  • 这一行:'return m_T;'将无法正常工作,因为当函数退出时,堆栈上的任何内容都会“丢失”,因此返回堆栈上的项目是未定义的行为。但是,由于返回的是实际的完整结构,而不是指针,因此编译器将调用 memcpy() 将结构复制到“隐藏”内存区域(不能用于其他任何内容),然后再次调用 memcpy() 以从隐藏的内存区域复制到调用者的变量。最好传递第三个参数,它是指向调用者的结构实例的指针,并使用该指针
  • 请使用有意义的变量/参数名称。 's' 和 'l' 和 't' 很少或根本没有说明内容及其用法
  • 假设发布的汇编代码是忠实的复制品,那么编译器只处理结构的第一个字段,即 I.E.它未能复制/返回第二个字段

标签: c windows call nasm extern


【解决方案1】:
As I stated in a comment.
it would be far better to pass a pointer to 
an instance of struct token
rather than the current code.
The following follows the current code.
but remember all those hidden calls to memcpy()
and the hidden ram allocation

otherfile.h 包含

#ifndef OTHER_FILE_H
#define OTHER_FILE_H

struct token 
{
    int start;
    int length;
};

struct token get_token( int, int );

#endif // OTHER_FILE_H

在文件 otherfile.c 中

#include <stdio.h>
#include "otherfile.h"

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

    struct token m_T = {0,0};
    m_T.start = tokenStart;
    m_T.length = tokenLength;

    return m_T;
}

在文件 token.c 中

#include <stdio.h>
#include "otherfile.h"
...
    struct token myToken = {0,0};
    myToken = get_token( tokenStart, tokenLength );
...

【讨论】:

  • 顺便说一句,我仍然需要一些外部的东西才能从程序集中调用。我的问题中的示例代码充其量是基于非常粗略的细节以及如何创建外部可访问的函数。
  • 说实话,我真的不明白这如何为问题提供任何解决方案。
【解决方案2】:

我相信问题出在您的.bss 部分:

SECTION .bss        ; uninitialized data
    tkn:        resd        1

在这里,您为令牌留出了一个 dword(一个整数的内存值)内存。但是,在您的 C 代码中,您将令牌结构定义为具有 2 个ints(startlength)或 2 个dwords 的内存。这意味着您只能写入令牌结构 (start) 的一部分,并且成员 length 被视为不存在。您的问题可能只需将tkn 定义为即可解决

    tkn:        resd        2

    tkn:        resq        1     ;; 1 QWORD == 2 DWORDs

希望这会有所帮助;)

【讨论】:

    【解决方案3】:

    我决定一次分配一个标记而不是一个标记,我应该分配一个缓冲区并填充它:确定需要多少标记,malloc 缓冲区,调用 get_tokens 并传入指向缓冲区的指针,然后令牌的数量。

    get_tokens 方法填充缓冲区并返回创建的令牌计数。

    然后程序集迭代令牌缓冲区并显示每个令牌的值 - startlength

    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);
    }
    

    ma​​ke_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
    

    ...以及结果输出:

    令牌大小:8 字节
    3 个令牌的 malloc 空间:24 个字节
    [C] 创建 3 个令牌:24 字节
    [C] [0] 开始:0;长度:0
    [C] [1] 开始:1;长度:10
    [C] [2] 开始:2;长度:20

    成功
    [0]: s[0] l[0]
    [1]: s[1] l[10]
    [2]: s[2] l[20]

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-08-30
      • 1970-01-01
      • 2018-08-19
      • 2015-08-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-14
      相关资源
      最近更新 更多