2015-04-22 5 views
5

У меня есть Экстерн функция и struct определенных в token.c:Как вызвать функцию C extern и получить структуру возврата?

#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 вызывается, а значения вставляются в функцию.

+0

Если я читал [эту таблицу] (http://en.wikipedia.org/wiki/X86_calling_conventions#List_of_x86_calling_conventions) правильно, чем вторая половина вашего ответа должен быть в EDX - «возвращение POD значения 33-64 бита в размер возвращается через регистры EAX: EDX ». – zch

+0

1) не определяют определения типа typedef. 2) поскольку эта структура объявлена ​​в другом файле, определение должно быть в файле заголовка, так что оба исходных файла #include. 3) ссылка на внешнюю функцию не выполняется через extern. скорее в общем заголовочном файле, который включен как в исходный файл, где объявлена ​​функция, так и в локальный файл через #include. файл заголовка будет иметь прототип, компоновщик обрабатывает остальные. 4) порядок поиска файлов #include зависит от того, что используется '< ...>' или '"..."'. системные заголовки всегда должны использовать '<' and '>' – user3629249

+0

: 'return m_T;' не будет работать корректно, потому что, когда функция выходит, что-либо в стеке «потеряно», поэтому возврат элемента, находящегося в стеке, является неопределенным поведением. Однако, поскольку возвращаемая фактическая полная структура, а не указатель, компилятор будет вызывать memcpy() для копирования структуры в «скрытую» область памяти (которую нельзя использовать ни для чего другого), а затем снова вызвать memcpy() копировать из скрытой области памяти в переменную вызывающих. Гораздо лучше передать третий параметр, который является указателем на экземпляр вызывающих объектов структуры, и использовать этот указатель – user3629249

ответ

0

я решил вместо одного маркеров в то время, что я должен выделить буфер и заполнить его: определить, сколько токенов требуется, malloc буфер, вызвать get_tokens и передать указатель на буфер и количество токенов.

Метод get_tokens заполняет буфер и возвращает количество созданных токенов.

Затем сборка выполняет итерацию буфера токена и отображает значения - start и length - для каждого токена.

токен.с:

#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 

... и полученный выход:

маркер размер: 8 байт
таНос пространство для 3 жетонов: 24 байта
[C] создать 3 жетона: 24 байта
[C] [0] начало: 0; len: 0
[C] [1] начало: 1; len: 10
[C] [2] начало: 2; Len: 20

успех
[0]: s [0] л [0]
[1]: с [1] л [10]
[2]: s [2] л [20]

-2
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); 
... 
+0

Кстати, у меня все еще есть что-то extern для вызова из сборки. Пример кода, который у меня есть в моем вопросе, основан на очень эскизных деталях в лучшем случае и как создать внешнюю доступную функцию. – IAbstract

+0

Честно говоря, я действительно не понимаю, как это предлагает какое-либо решение вопроса. – IAbstract

0

Я считаю, что проблема заключается в вашем .bss раздел:

SECTION .bss  ; uninitialized data 
    tkn:  resd  1 

Здесь вы выделяете одну единственную dword (ценность целого числа памяти) для токена. Однако в вашем C-коде вы определяете структуру токена как имеющую 2 int s (start и length), или 2 dword s ценность памяти. Это означает, что вы можете писать только часть структуры токенов (start), а член length рассматривается как несуществующий. Ваша проблема, вероятно, может быть решена путем простого определения tkn, как

tkn:  resd  2 

или

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

Надежда это помогает;)

+0

Я пробовал это, но это не сработало. – IAbstract