Как передать указатель файла из c на вызов в asm - PullRequest
0 голосов
/ 27 апреля 2019

Я бездельничаю с nasm, и после того, как без проблем устрою привет, я попытаюсь сделать некоторую интеграцию с.

Я использую c, чтобы открыть файл, иЗатем я хочу использовать указатель, возвращенный для открытого файла, для обработки текста.Однако, когда я вызываю fgetc с указателем в rdi, я получаю «нет такого файла или каталога», за которым следует ошибка по умолчанию.

Что я делаю не так?

int64_t asmFunc(FILE* a, char* b);

int main()
{
   int num;
   FILE *fptr;
   size_t line_buf_size = 0;
   char *ret = malloc(100);
   fptr = fopen("./test.txt","r");

   if(fptr == NULL)
   {
      printf("Error!");   
      exit(1);             
   }

   printf("%ld", asmFunc(fptr, ret));

   return 0;
}
global asmFunc

section .text

extern fgetc

asmFunc:
  call fgetc  ; segfault occurs here.
(...)
  ret

Ответы [ 2 ]

3 голосов
/ 27 апреля 2019

Первая инструкция asmFunc также не является вызовом, но я удалил некоторые настройки для более поздних операций, чтобы было легче читать.

Хорошо, это просто побеждает всю цельMCVE.Вам необходимо повторно запустить тест после , чтобы убедиться, что он по-прежнему показывает ту же проблему, что и полная версия.Но для этого ответа я предполагаю, что ваша установка не сожгла аргумент fptr в RDI и не изменила RSP.


asmFunc:
  call fgetc  ; segfault occurs here.

fptr все еще будет в RDI, где ваш абонентпередал его, так что это правильно для int fgetc(FILE *fp).

Итак, предположительно fgetc является ошибкой сегмента, потому что вы назвали его со смещенным стеком. (Это было выровнено по 16 байтам до call это прыгнуло до asmFunc, но у вас нет ни одного нечетного количества нажатий или каких-либо sub rsp, 8*n).Современные сборки glibc действительно зависят от 16-байтового выравнивания для scanf ( glibc scanf Ошибки сегментации при вызове из функции, которая не выравнивает RSP ), поэтому легко представить, что fgetc включает в себя код, который также компилируется ввключите movaps чего-либо в стек.


Как только вы исправите эту ошибку, у вас будет проблема, что call fgetc уничтожает ваш char *ret аргумент, потому что вашзвонящий передает его в RSI. Регистры прохождения аргументов закрыты при вызове . Какие регистры сохраняются при вызове функции linux x86-64

asmFunc:            ; (FILE *fptr,  char *ret)
  push   rsi        ; save ret
  call   fgetc
  pop    rsi
  mov    [rsi], al
  ret

Компилятор переменного тока обычно сохраняет / восстанавливает RBX и использует mov для сохранения ret там.

asmFunc:            ; (FILE *fptr,  char *ret)
  push   rbx
  mov    rbx, rsi   ; save ret
  call   fgetc
  mov    [rbx], al

  pop    rbx        ; restore rbx
  ret

Однако, когда я вызываю fgetc с указателем в rdi, я получаю «нет такого файла или каталога», за которым следует segfault.

Не знаю, как выполучаю "нет такого файла или каталога".Это из вашего отладчика ищет источник для функций glibc?Если это часть того, что печатает сама ваша программа, это имеет смысл, близкий к нулю, потому что вы правильно делаете exit(1), когда fptr == NULL.И вы не используете perror() или что-либо еще, что ищет коды ошибок для генерации стандартных строк ошибок.

1 голос
/ 27 апреля 2019

Вам необходимо изучить и следовать соглашениям о вызовах , задокументированным в спецификации Linux x86-64 ABI , в частности ее §3.2.3 Раздел передачи параметров .Таким образом, значение указателя fptr находится в %rdi, а значение указателя ret находится в %rsi, и вам, вероятно, следует нажать кадр вызова для вашего asmFunc

Читайте также Соглашения о вызовах x86 wikipage.

Если вы можете закодировать эквивалент (даже упрощенный) asmFunc в C в каком-то файле example.c, я рекомендую скомпилировать его с gcc -O -fverbose-asm -Wall -S example.c иищите испущенный example.s файл ассемблера для вдохновения.В большинстве случаев первая машинная инструкция такой функции - это не call (а что-то, называемое прологом функции , изменяющий указатель стека %esp и выделяющий некоторый кадр вызова на стек вызовов )

Например, на моем Linux / Debian / x86-64 с gcc-8

void asmfunc(FILE* fil, char*s) {
   fputc ('\t', fil);
   fputs (s, fil);
   fputc ('\n', fil);
   fflush (fil);
}

компилируется в:

    .text
    .globl  asmfunc
    .type   asmfunc, @function
asmfunc:
.LFB11:
    .cfi_startproc
    pushq   %rbp    #
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    pushq   %rbx    #
    .cfi_def_cfa_offset 24
    .cfi_offset 3, -24
    subq    $8, %rsp    #,
    .cfi_def_cfa_offset 32
    movq    %rdi, %rbx  # fil, fil
    movq    %rsi, %rbp  # s, s
# /tmp/example.c:4:    fputc ('\t', fil);
    movq    %rdi, %rsi  # fil,
    movl    $9, %edi    #,
    call    fputc@PLT   #
# /tmp/example.c:5:    fputs (s, fil);
    movq    %rbx, %rsi  # fil,
    movq    %rbp, %rdi  # s,
    call    fputs@PLT   #
# /tmp/example.c:6:    fputc ('\n', fil);
    movq    %rbx, %rsi  # fil,
    movl    $10, %edi   #,
    call    fputc@PLT   #
# /tmp/example.c:7:    fflush (fil);
    movq    %rbx, %rdi  # fil,
    call    fflush@PLT  #
# /tmp/example.c:8: }
    addq    $8, %rsp    #,
    .cfi_def_cfa_offset 24
    popq    %rbx    #
    .cfi_def_cfa_offset 16
    popq    %rbp    #
    .cfi_def_cfa_offset 8
    ret 
    .cfi_endproc
.LFE11:
    .size   asmfunc, .-asmfunc
    .ident  "GCC: (Debian 8.3.0-6) 8.3.0"

Обратите внимание, что в некоторых случаях GCC может (например, с -O2) оптимизировать tail-call и может вызывать некоторые leaf-функции специально.

...