【问题标题】:How would I find the length of a string using NASM?如何使用 NASM 找到字符串的长度?
【发布时间】:2011-09-22 01:07:36
【问题描述】:

我正在尝试使用 NASM 制作一个从命令行参数获取输入的程序。由于没有提供字符串长度,我正在尝试创建一个函数来计算我自己的。这是我的尝试,它接受一个指向ebx寄存器中的字符串的指针,并返回ecx中字符串的长度:

len:
    push ebx
    mov ecx,0
    dec ebx
    count:
        inc ecx
        inc ebx
        cmp ebx,0
        jnz count
    dec ecx
    pop ebx
    ret

我的方法是逐个字符地遍历字符串,并检查它是否为空。如果不是,我增加ecx 并转到下一个字符。我认为问题在于cmp ebx,0 对于我正在尝试做的事情不正确。我将如何正确地检查字符是否为空?另外,还有其他我可以做得更好的事情吗?

【问题讨论】:

  • cmp ebx,0 是错误的,最后的 push ebx 应该是 pop ebx (否则你会得到一个 stack overflow !)。
  • 糟糕。我错误地将pop ebx 手动复制为push ebx
  • 尝试养成复制和粘贴实际代码的习惯,而不是重新输入代码。另外请编辑您的问题,使其与实际代码匹配。
  • @paul-r 这就是我通常做的事情。我在 VirtualBox 中工作,剪贴板共享设置不正确。

标签: linux string assembly x86 nasm


【解决方案1】:

您正在将 ebx 中的值与 0 进行比较,这不是您想要的。 ebx 中的值是内存中字符的地址,因此应该像这样取消引用:

cmp byte[ebx], 0

另外,最后一个push ebx 应该是pop ebx

【讨论】:

    【解决方案2】:

    这是我在检查argv[1] 的 64 位 Linux 可执行文件中执行此操作的方法。内核使用堆栈上的argcargv[] 启动一个新进程,如 x86-64 System V ABI 中所述。

    _start:
        pop    rsi              ; number of arguments (argc)
        pop    rsi              ; argv[0] the command itself (or program name)
        pop    rsi              ; rsi = argv[1], a pointer to a string
        mov    ecx, 0           ; counter
    .repeat:
        lodsb                   ; byte in AL
        test   al,al            ; check if zero
        jz     .done            ; if zero then we're done
        inc    ecx              ; increment counter
        jmp    .repeat          ; repeat until zero
    .done:
        ; string is unchanged, ecx contains the length of the string
    
    
    ; unused, we look at command line args instead
    section .rodata
        asciiz:    db    "This is a string with 36 characters.", 0
    

    这是缓慢且低效的,但很容易理解。

    为了效率,你会想要

    当然 SSE2 在 x86-64 中始终可用,因此我们应该使用它来检查 16 字节的块(在达到对齐边界之后)。请参阅优化的手写 strlen 实现,例如在 glibc 中。 (https://code.woboq.org/userspace/glibc/sysdeps/x86_64/strlen.S.html)。

    【讨论】:

      【解决方案3】:

      这就是我的编码方式

      len:
            push ebx
            mov  eax, ebx
      lp:
              cmp byte [eax], 0
              jz  lpend
              inc eax
              jmp lp
      lpend:
              sub eax, ebx
      
            pop ebx
            ret
      

      (结果在 eax 中)。可能有更好的方法。

      【讨论】:

      • 指针增量更好,但糟糕的循环结构是许多 CPU 的瓶颈。在每个循环迭代中您有 2 个分支(一个被采用,一个未采用),因此它只能在 Haswell 之前的 CPU 上每 2 个时钟运行一次迭代,即在您编写此代码时存在的那些。 Why are loops always compiled into "do...while" style (tail jump)?。 (当然,对于带有 SSE2 的现代 x86 来说,一次只检查一个字节是相当糟糕的。)
      猜你喜欢
      • 1970-01-01
      • 2016-03-05
      • 1970-01-01
      • 2013-06-26
      • 2016-01-01
      • 2013-01-12
      • 2022-12-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多