【问题标题】:How to find characters in a string Assembly x86?如何在字符串Assembly x86中查找字符?
【发布时间】:2022-02-02 22:13:03
【问题描述】:

我正在尝试在 Assembly x86 中重写下面的 C 代码

int myFn( char * v, char c, int size ) {
  int i;
  for(i=0; i < size; i++ )
    if( v[i] == c )
      return i;
  return -1;
}

我尝试在 x86 中使用此代码:

myFn:
  mov esi, 0
  mov ebx, [esp + 8]
  mov ecx, [esp + 12]

  FOR:
    mov eax, -1
    cmp esi, [esp + 4]
    jge ENDFOR

    cmp [ecx + esi], ebx
    je EQUAL

    inc esi
    jmp FOR
  
  EQUAL:
    mov eax, [esi]
  
  ENDFOR:

  ret

我还创建了这个程序来测试功能:

section .data
  fmt: db "strfind: %d", 10, 0
  str: db "test", 0

section .text
  global main
  extern printf

main:
  mov eax, 's'
  mov ebx, 4

  push str
  push eax
  push ebx
  call myFn
  add esp, 12

  push eax
  push fmt
  call printf
  add esp, 8

  ret

myFn:
  mov esi, 0
  mov ebx, [esp + 8]
  mov ecx, [esp + 12]

  FOR:
    mov eax, -1
    cmp esi, [esp + 4]
    jge ENDFOR

    cmp [ecx + esi], ebx
    je EQUAL

    inc esi
    jmp FOR
  
  EQUAL:
    mov eax, [esi]
  
  ENDFOR:

  ret

我在尝试测试时收到Segmentation Fault 错误或错误结果。我认为问题在于将字符串的字符与我要查找的字符进行比较时

【问题讨论】:

  • 存在多个问题,但直接原因可能是mov eax, [esi]。您想要mov eax, esi,因为esi 是索引而不是地址。您还比较了 4 个字节而不是 1 个字节,破坏了被调用者保存的寄存器和未对齐堆栈以命名一些其他问题。
  • 这里有两个比较:cmp esi, [esp + 4] 这是比较 i &lt; size 并且应该是一个 int 大小的比较。 但是另一个cmp [ecx + esi], ebxv[i] == c 应该是char/字节大小的比较。
  • 是的,所以主要问题是第二次比较,我该如何解决?
  • 这是memchr;寻找 memchr 的简单示例(尽管您在搜索时找到的大部分内容都会经过高度优化)。或者查看 C 编译器的编译器输出以获取 C 代码,尽管这不会使用 NASM 语法。不过,在使用字节操作数大小时应该清楚。 How to remove "noise" from GCC/clang assembly output?

标签: string assembly x86 char


【解决方案1】:

ASCII 字符都是 8 位,因此您不需要 32 位寄存器来存储字符。您应该使用像al 这样的8 位寄存器来保存字符。下面是我用 nasm 64 位编写的程序,运行良好。

global    _start

section   .data
str:  db  "test", 0    

section   .text

_start:
    mov al, 's'
    mov bl, 4
    mov cl, 0

    mov rsi, str

; iterate over the string
L1:
    cmp [rsi], al
    je ENDL1 ; if found, jmp to ENDL1 label
    inc rsi
    inc cl
    cmp cl, 4
    jbe L1
    mov cl, -1 ; if not found


ENDL1:
    xor rax, rax ; set rax register to 0
    mov al, cl ; rax equal index in string i.e rax = 0x2

您得到SEGMENTATION FAULT 的原因可能是因为您没有在程序结束时使用退出syscall。要解决此问题,请在 main 函数的末尾写下以下内容:

; exit syscall
mov rax, 60                 ; system call for exit
xor rdi, rdi                ; exit code 0
syscall

根据您的要求调整 32 位模式的代码。

【讨论】:

  • 有多种方法可以提高效率,例如只在循环内增加一个指针,并在外面减去。另外,avoid writing partial registers,例如mov ebx, 4 而不是 mov bl, 4。 (或者删除它,因为您硬编码了cmp cl,4 而不是bl)。和xor ecx,ecx 而不是mov cl,0。但最简单和最明显的改进是将xor-zero 和mov al,cl 替换为movzx eax, cl 以用一条指令而不是两条指令将CL 零扩展为RAX。否则很好,虽然
猜你喜欢
  • 2017-03-12
  • 1970-01-01
  • 1970-01-01
  • 2011-01-11
  • 2017-03-22
  • 2019-10-01
  • 2014-11-29
  • 1970-01-01
相关资源
最近更新 更多