Как преобразовать 64-битную функцию в 32-битную в 16-битную в 8-битную? - PullRequest
0 голосов
/ 15 апреля 2020

Я новичок в сборке, я не знаю, как преобразовать 64-битную функцию в 32-битную в 16-битную в 8-битную

Цель следующих функций - напечатать число и вернуть количество цифр в нем.

64 бит:

          global print_uint64
    section .text
        print_uint64:

          mov rax,rdi
          mov rdi,10
          mov rsi,rsp

       while:
          xor  rdx  ,rdx
          div  rdi
          add  rdx  ,48
          dec  rsi
          mov  [rsi],dl
          cmp  rax  ,0
          jne  while 

          mov rax,1
          mov rdi,1
          lea rdx,[rsp]
          sub rdx,rsi
          syscall

          lea rax,[rsp]
          sub rax,rsi
          ret

this works fine 

32 бит:

      global print_uint32
section .text
    print_uint32:

      mov eax,edi
      mov edi,10
      mov rsi,rsp

   while:
      xor  edx  ,edx
      div  edi
      add  edx  ,48
      dec  rsi
      mov  [rsi],dl
      cmp  eax  ,0
      jne  while 

      mov eax,1
      mov edi,1
      lea edx,[rsp]
      sub edx,esi
      syscall

      lea eax,[rsp]
      sub eax,esi
      ret

это прекрасно работает

16 бит:

      global print_uint16
section .text
    print_uint16:

      mov ax,di
      mov di,10
      mov rsi,rsp

   while:
      xor  dx  ,dx
      div  di
      add  dx  ,48
      dec  rsi
      mov  [rsi],dl
      cmp  ax  ,0
      jne  while 

      mov ax,1
      mov di,1
      lea dx,[rsp]
      sub dx,si
      syscall

      lea ax,[rsp]
      sub ax,si
      ret

но это не сработало

Я изучил некоторые вопросы о переполнении стека, касающиеся этого вопроса. Я понимаю, что не могу изменить rsp на esp, потому что esp устанавливает старшие 32 бита в ноль, поэтому, когда мы используем [] при этом доступе к памяти не выделяется этой программе, поэтому она выдает ошибку сегмента.

Мой вопрос:

1) Каковы основные c правила для преобразования 64-разрядных в 32-разрядные в 16-разрядные в 8-разрядные.

1 Ответ

3 голосов
/ 15 апреля 2020

Основными правилами c является то, что указатели все еще являются 64-битными независимо от ширины данных. Как и в C, sizeof(int*) и sizeof(char*) одинаковы (в обычных системах).

Вот почему все ваши версии должны использовать dec rsi и mov [rsi],dl: RSP содержит 64 указатель Сокращение его до 32-разрядного не приведет к правильному указателю.

Кроме того, числа системного вызова и fd все еще имеют одинаковый размер; mov ax,1 и mov di,1 оставляют мусор в старших байтах регистра. Используйте strace ./my_program, чтобы декодировать то, что вы фактически передали в syscall.


Узкие версии могут просто расширить нулевой ввод до 32-битного и перейти к 32-битной версии.

Но кроме этого, основные правила c - это использовать 32-битный размер операнда всякий раз, когда это возможно; это естественный размер для x86-64 ( Преимущества использования 32-битных регистров / инструкций в x86-64 ). например, всегда ноль RDX / EDX / DX с xor edx,edx.

Запись 32-битного регистра с нуля расширяется до 64-битного, в отличие от 8/16, которые просто сливаются в старое значение.
Почему инструкции x86-64 для 32-разрядных регистров обнуляют верхнюю часть полного 64-разрядного регистра? Использование 16-разрядного mov reg,imm16 и оставление большого количества мусора, вероятно, является причиной того, что ваш системный вызов не работает.
Почему G CC не использует частичные регистры?

Примечательно, что lea dx,[rsp] / sub dx,si потенциально оставляет мусор в верхних битах RDX , arg to write syscall.

Это вычитание указателя, вычисляющее количество элементов в буфере char. Нет смысла выбирать размер операнда для этого на основе размера входного числа. На самом деле хорошо делать узкое вычитание при условии, что результат будет расширен до нуля в RDX, потому что в этом случае вы знаете, что число цифр будет не более 19 (для 64-битной версии), потому что это то, как долго 2 ^ 64-1 находится в базе 10.

Так что mov edx, esp / sub edx, esi - это то, что вы должны были делать во всех версиях. Поскольку полный RSP и RSI находятся рядом, их разница невелика. Усечение входных данных до вычитания вместо усечения результата после не меняет результат; перенос распространяется от младших к старшим битам. См. Какие целые операции дополнения 2 можно использовать без обнуления старших битов на входах, если требуется только младшая часть результата?

Использование LEA для копирования регистра неэффективно; lea dx, [rsp] архитектурно идентичен mov dx, sp, но медленнее.

...