如何在x86程序集中获取长字符串的长度以在断言上打印

问题描述 投票:1回答:1

我正在尝试构建一个将文件读入内存的x86程序。它使用了一些不同的系统调用,以及内存等等。那里有很多要弄清楚。

为了简化调试并弄清楚这一点,我想添加assert语句,如果不匹配,它会输出一个很好的错误信息。这是学习汇编的第一步,因此我可以打印放在不同寄存器上的数字和字符串等操作后。然后我可以打印出来并调试它们,而无需任何花哨的工具。

想知道是否可以帮我在NASM for Mac x86-64上写一个ASSERTPRINT。到目前为止我有这个:

%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
macos unit-testing assembly x86-64 nasm
1个回答
2
投票

mov rsi, "abcdefgh"是字符串内容的mov-immediate,而不是指向它的指针。如果你这样做,它只会立即存在。

您的宏需要切换到.rodata并返回以将字符串放入内存;可能你可以用NASM宏将它变成一个直接推送到堆栈的序列,但听起来很难。

所以你可以使用通常的msglen equ $ - msg来获得长度。 (实际上使用NASM本地标签,因此宏不会产生冲突)。


请参阅NASM - Macro local label as parameter to another macro,几周前我基本上写了这个答案。但不完全是重复,因为它没有使用字符串作为立即的错误。

无论如何,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构建你的代码,最好明确它。

我使用了RIP相对LEA,因为这是在与位置无关的代码中将静态地址放入寄存器的最有效方法。在Linux非PIE可执行文件中,使用mov esi, %%str(5个字节,可以在任何端口上运行,超过LEA)。但是在OS X上,映射/加载可执行文件的基本虚拟地址总是高于2 ^ 32,并且您永远不希望mov r64, imm64具有64位绝对地址。


在Linux上,系统调用号是小整数,你可以使用lea eax, [rdi-1 + WRITE]用3字节指令执行eax = SYS_write而对于mov执行5。

© www.soinside.com 2019 - 2024. All rights reserved.