Как получить длину длинных строк в сборке x86 для печати по утверждению - PullRequest
1 голос
/ 22 марта 2019

Я пытаюсь создать программу для x86, которая считывает файл в память. Он использует несколько разных системных вызовов и портит память и тому подобное. Там много чего нужно выяснить.

Чтобы упростить отладку и выяснить это, я хотел добавить операторы assert, которые, в случае несоответствия, выводят хорошее сообщение об ошибке. Это первый шаг в обучении ассемблеру, поэтому я могу напечатать числа и строки, которые помещаются в разные регистры и тому подобное после операций. Затем я могу распечатать их и отладить без каких-либо причудливых инструментов.

Интересно, можно ли мне помочь написать ASSERT И PRINT в NASM для Mac x86-64. У меня есть это до сих пор:

%define a rdi
%define b rsi
%define c rdx
%define d r10
%define e r8
%define f r9
%define i rax

%define EXIT 0x2000001
%define EXIT_STATUS 0

%define READ 0x2000003 ; read
%define WRITE 0x2000004 ; write
%define OPEN 0x2000005 ; open(path, oflag)
%define CLOSE 0x2000006 ; CLOSE
%define MMAP 0x2000197 ; mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t offset)

%define PROT_NONE 0x00 ; no permissions
%define PROT_READ 0x01 ; pages can be read
%define PROT_WRITE 0x02 ; pages can be written
%define PROT_EXEC 0x04 ; pages can be executed

%define MAP_SHARED 0x0001 ; share changes
%define MAP_PRIVATE 0x0002 ; changes are private
%define MAP_FIXED 0x0010 ; map addr must be exactly as requested
%define MAP_RENAME 0x0020 ; Sun: rename private pages to file
%define MAP_NORESERVE 0x0040 ; Sun: don't reserve needed swap area
%define MAP_INHERIT 0x0080 ; region is retained after exec
%define MAP_NOEXTEND 0x0100 ; for MAP_FILE, don't change file size
%define MAP_HASSEMAPHORE 0x0200 ; region may contain semaphores

;
; Assert equals.
;

%macro ASSERT 3
  cmp %1, %2
  jne prepare_error
prepare_error:
  push %3
  jmp throw_error
%endmacro

;
; Print to stdout.
;

%macro PRINT 1
  mov c, getLengthOf(%1) ; "rdx" stores the string length
  mov b, %1 ; "rsi" stores the byte string to be used
  mov a, 1 ; "rdi" tells where to write (stdout file descriptor: 1)
  mov i, WRITE ; syscall: write
  syscall
%endmacro

;
; Read file into memory.
;

start:
  ASSERT PROT_READ, 0x01, "Something wrong with PROT_READ"

  mov b, PROT_READ
  mov a, PROT_WRITE
  xor a, b

  mov f, 0
  mov e, -1
  mov d, MAP_PRIVATE
  mov c, a
  mov b, 500000
  mov a, 0
  mov i, MMAP
  syscall
  PRINT "mmap output "
  PRINT i ; check what's returned
  PRINT "\n"
  mov e, i

  mov b, O_RDONLY
  mov a, "Makefile"
  mov i, OPEN
  syscall
  mov a, i

  mov b, e
  mov i, READ
  syscall

;
; Exit status
;

exit:
  mov a, EXIT_STATUS ; exit status
  mov i, EXIT ; syscall: exit
  syscall

throw_error:
  PRINT pop() ; print error or something
  jmp exit

1 Ответ

2 голосов
/ 22 марта 2019

mov rsi, "abcdefgh" является mov-немедленной строкой содержимое , а не указателем на нее. Он существует как непосредственный, если вы это сделаете.

Ваш макрос должен будет переключиться на .rodata и обратно, чтобы поместить строку в память; возможно, вы могли бы превратить его в последовательность push-немедленного в стек с макросами NASM, но это звучит сложно.

Таким образом, вы можете использовать обычный msglen equ $ - msg, чтобы получить длину. (На самом деле используются локальные метки NASM, чтобы макрос не создавал конфликтов).


См. NASM - локальная метка макроса в качестве параметра для другого макроса , где я в основном написал этот ответ пару недель назад. Но не совсем дубликат, потому что в нем не было ошибки использования строки как непосредственного.

В любом случае, NASM не поддерживает AFAIK для переключения секций, а затем возвращается к текущей секции, как GAS .pushsection. Поэтому мы застряли в жестком коде section .text, если вы не хотите добавить необязательный параметр для имени раздела.

    ; write(1, string, sizeof(stringarray))
    ; switches to  SECTION .text regardless of previous section
    ; clobbers: RDI, RSI, RDX,   RCX,R11 (by syscall itself)
    : output: RAX = bytes written, or -errno
%macro PRINT 1
section .rodata 
;; NASM macro-local labels
    %%str    db  %1          ; put the string in read-only memory
    %%strln  equ $ - %%str   ; current position - string start
section .text
  mov     edx, %%strlen           ; len
  lea     rsi, [rel %%str]        ; buf = the string.  (RIP-relative for position-independent)
  mov     edi, 1                  ; fd = stdout
  mov     eax, WRITE
  syscall
%endmacro

Это не пытается объединить дубликаты одной и той же строки. Многократное использование его с одним и тем же сообщением будет неэффективным. Это не имеет значения для отладки.

Я мог бы оставить ваши определения% для RDI и позволить NASM оптимизировать mov rdi, 1 (7 байт) до mov edi, 1 (5 байт). Но YASM этого не сделает, поэтому лучше сделать это явно, если вы заботитесь о том, чтобы кто-нибудь создавал ваш код с помощью YASM.

Я использовал REA-относительный LEA, потому что это самый эффективный способ поместить статический адрес в регистр в позиционно-независимом коде. В исполняемых файлах Linux, отличных от PIE, используйте mov esi, %%str (5 байт и можете работать на любом порту, больше, чем LEA). Но в OS X базовый виртуальный адрес, где исполняемый файл отображается / загружается, всегда выше 2 ^ 32, и вам никогда не понадобится mov r64, imm64 с 64-битным абсолютным адресом.


В Linux, где номера системных вызовов представляют собой маленькие целые числа, вы можете использовать lea eax, [rdi-1 + WRITE] для выполнения eax = SYS_write с 3-байтовой инструкцией против 5 для mov.

...