首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >模拟strcat的x86装配方法

模拟strcat的x86装配方法
EN

Code Review用户
提问于 2016-04-23 05:46:12
回答 2查看 3.9K关注 0票数 6

我正在努力学习x86程序集,并希望能够继续从事涉及多个人的项目。

这是一个(相对的)简单的子例程,它是在Intel程序集中编写的,它将一个字符串复制到另一个字符串的末尾。我将其命名为strglue,以避免与C库函数strcat (因为此方法的驱动程序是C程序)和MASM宏CatStr发生冲突。

我想知道我是否能对以下内容有一些了解:

  1. 代码本身(指令的最优性和注册使用)
  2. 文献资料
  3. 圆环和标签的压痕
代码语言:javascript
复制
.386
.MODEL FLAT, C
.DATA
.CODE

    ; Glue together (concatenate) two strings
    ; PARAMS:
    ;  [ESP+4] - Pointer to first character in null-terminated destination string
    ;  [ESP+8] - Pointer to first character in null-terminated source string
    ; USES:
    ;  EAX: Holds the destination string during execution
    ;  ECX: Holds the source string during execution:
    ;  EDX: Lower 8 bits (DL) holds characters being copied from source to destination
    ; OUTPUT:
    ;  [ESP+4] - Pointer to first character in null-terminated concatenation of destination string + source string (excluding the null-terminator from the destination string)
    strglue PROC

        ; Step 1: Get a pointer to the null-terminator character in the destination string
        MOV EAX, DWORD PTR [ESP+4]
        ENDOFDEST:  
            INC EAX
            CMP BYTE PTR [EAX], 0
        JNZ ENDOFDEST

        ; Step 2: Copy the source string into the pointer to the end of dest, overwriting the null-terminator
        MOV ECX, DWORD PTR [ESP+8]
        STRCATLOOP:
            MOV DL, BYTE PTR [ECX]
            LEA EAX, [EAX+1]
            MOV BYTE PTR [EAX-1], DL
            LEA ECX, [ECX+1]

            ; If ZF=0  the null terminator in the source string has been reached; stop looping
            TEST DL, DL
        JNZ STRCATLOOP
        RET
    strglue ENDP
END
EN

回答 2

Code Review用户

回答已采纳

发布于 2016-04-23 14:52:56

在我看来,这有点像我希望从编译器那里看到的代码--可以工作的代码,但完全忽略了传统的x86寄存器用法,以及x86为工作提供的特殊指令。

传统上,使用edi指向目标字符串,使用esi指向源字符串。您可以使用repnz scasb查找第一个字符串的结尾,使用lodsb/stosb将字节从源复制到目标(实际上还有一个movsb来复制字节,但它不允许您在复制时检查字符串的结尾)。

请注意,这取决于方向标志为0,但我所知道的每个编译器都是这样的(也取决于您对它的保留方式)。

代码语言:javascript
复制
    xor eax, eax     ; set al to value we're looking for, 0 in this case.
    lea ecx, [eax-1] ; set ecx to 0ffffffffh

    mov edi, [esp+4] ; find end of dest string
    repnz scasb

    mov esi, [esp+8] ; we've found the end of the destination, now do the copy
    dec edi

copy_loop:
    lodsb
    stosb
    test al, al
    jnz  copy_loop
    ret

这有点短。在正常情况下,我们可以预期任何一个处理器都是内存绑定的,因此速度可能不是特别重要(但如果我们确实关心,它将取决于所使用的确切处理器-这在旧处理器上可能工作得更好,但在较新的处理器上可能会慢一点)。

就可读性而言,这两种代码都相当容易阅读,但我认为我的代码更符合为x86编写代码的人希望看到寄存器被使用的方式。就缩进而言,对于是否缩进汇编语言似乎没有多少一致意见。您的缩进当然不会伤害任何东西,但是它不像高级语言,在这种语言中,没有缩进的代码显然是一个问题。我写上面的代码的方式(指令缩进一个停止,标签(S)在左边边缘)几乎肯定是更常见的。

我应该补充一点,我在发表这个话题时有点犹豫--虽然我认为我曾经很好地写过汇编语言,但我已经多年没有写到足够多的东西了。我确实期待@Edward在这方面发表评论(虽然我不确定他是否还会编写更多的程序集代码)。

票数 5
EN

Code Review用户

发布于 2016-04-23 10:18:29

密码看起来没问题。但是有一些说明,movsbrep,它们基本上可以代替您的第二步。您可以在第一步之后使用[esp + 8],然后执行以下操作:

代码语言:javascript
复制
sub eax, dword ptr [esp + 8]   ;get the length of the string
mov ecx, eax                   ;move the length of the string into ecx
mov esi, dword ptr [esp + 8]   ;move the source address into esi

您将再次使用原始步骤1获取目标的空终止符,并执行以下操作:

代码语言:javascript
复制
mov edi, eax                   ;move address of the destination null-terminator to edi, you could of course also replace eax with edi in your step 1
inc ecx                            ;don't forget to copy the null-terminator
rep movsb                      ;movsb: move the value at esi to the address at edi, increment esi and edi
                               ;rep:   decrement ecx and do the following instruction again if nonzero

对于文档,查找调用约定。您可以注意所使用的调用约定,但除此之外,您不需要显式地注意使用eaxecxedx,因为它们在大多数约定中都是参数。除此之外,我认为除了最后的评论之外,文档不是太冗长,也不是太少。

还请注意,您使用[esp + 4][esp + 8]作为参数,最好遵循约定,使用ecxedx或堆栈。此外,您应该将指向堆栈的数据指针push,而不是在固定地址中返回数据。

缩进更多的是个人的事情,除了一些东西,比如不要使用硬标签(而且你没有使用它们,所以这很好)。大多数情况下,使用带有4个空格的缩进。唯一的一件事是,我个人离开跳转指令在循环结束,以及缩进。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/126495

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档