【问题标题】:Assembly language : try to understand a small function汇编语言:尝试理解一个小函数
【发布时间】:2011-01-21 15:50:55
【问题描述】:

对于我的工作,我需要反转这部分代码 (ARM9) 正在执行的操作。我是一名 java 开发人员,我真的不明白这部分与单个函数相关的代码。

我当然是在寻求帮助,因为原始代码不再可用。任何人都可以帮助我了解这段代码用任何高级语言的小算法做什么?这会很好。我试了好几个小时都没有结果。

sub_FFFF7B38
    PUSH    {LR}
    ADDS    R2, R0, #0
    LDRB    R3, [R2]
    CMP     R3, #0
    BEQ     loc_FFFF7B52
    SUBS    R1, #1
    BCC     loc_FFFF7B52

loc_FFFF7B46:
    ADDS    R0, #1
    LDRB    R3, [R0]
    CMP     R3, #0
    BEQ     loc_FFFF7B52
    SUBS    R1, #1
    BCS     loc_FFFF7B46

loc_FFFF7B52:
    SUBS    R0, R0, R2
    POP     {R1}

【问题讨论】:

    标签: assembly arm


    【解决方案1】:

    这个怎么样:Instruction set for ARM

    一些提示/简化的asm

    • 推送 - 将某些内容放在“堆栈”/内存中
    • 添加 - 通常在 + 中“添加”
    • Pop 从“堆栈”/内存中检索某些内容
    • CMP - 是比较的缩写,它将某事物与其他事物进行比较。

    X: 或:Whatever: 表示下面是一个“子程序”。曾经在 Java 中使用过“goto”吗?其实和那个差不多。

    如果您有以下情况(如果它是正确的 arm-asm 则忽略它只是 pseduo):

    PUSH 1
    x:     
        POP %eax
    

    首先它会将 1 放入堆栈,然后将其弹出回 eax(扩展 ax 的缩写,这是一个可以放入 32 位数据量的寄存器)

    现在,x: 会做什么呢?那么我们假设在这之前还有 100 行 asm,那么您可以使用“跳转”指令导航到 x:

    这是对 asm 的一点介绍。简化。

    尝试理解上面的代码并检查指令集。

    【讨论】:

    • 我了解 push、add、cmp、jmp 但我仍然了解代码的用途。
    【解决方案2】:

    Filip 提供了一些指针,您还需要阅读 ARM 调用约定。 (也就是说,哪些寄存器包含入口的函数参数以及它的返回值。)

    通过快速阅读,我认为这段代码是 strnlen 或与之密切相关的东西。

    【讨论】:

    • 代码完全是 strnlen,我很惊讶只有你这么说而且你的回答没有人赞成。
    【解决方案3】:

    除了最后两行之外,它可能类似于以下内容。
    如果我不是 100% 正确,请不要打我。

    如果
    R0p0p 并且
    R1n 并且
    R2 是临时值(已编辑;首先我认为:ip0[i]的地址)
    R3是临时值

    .

    sub_FFFF7B38
              PUSH {LR}           ; save return address
              ADDS R2, R0, #0     ; move R0 to R2
              LDRB R3, [R2]       ; load *p0
              CMP R3, #0          ; if *p0==0 
              BEQ loc_FFFF7B52    ; then jump to loc_FFFF7B52 
              SUBS R1, #1         ; decrement n
              BCC loc_FFFF7B52    ; if there was a borrow (i.e. n was 0): jump to loc_FFFF7B52
    
    
    loc_FFFF7B46:
              ADDS R0, #1         ; increment p
              LDRB R3, [R0]       ; load *p
              CMP R3, #0          ; if *p==0
              BEQ loc_FFFF7B52    ; jump to loc_FFFF7B52
              SUBS R1, #1         ; decrement n
              BCS loc_FFFF7B46    ; if there was no borrow (i.e. n was not 0): jump to loc_FFFF7B46
    
    
    loc_FFFF7B52:
              SUBS R0, R0, R2     ; calculate p - p0
              POP {R1}            ; ??? I don't understand the purpose of this
                                  ; isn't there missing something?
    

    或在 C 中:

    int f(char *p0, unsigned int n)
    {
      char *p;
    
      if (*p0==0 || n--==0)
        return 0;
    
      for(p=p0; *++p && n>0; n--)
      {
      }
      return p - p0;
    }
    

    【讨论】:

    • @Curd - 我认为你比我想的要近得多,我认为我不能超过你的答案。 +1
    • R2 也是一个临时寄存器。 ADDS R2, R0, #0 在读取之前覆盖 R2R2 只是保存了R0 的原始值,它不是索引所以不应该是p0 吗?另外CC 是“carry clear”条件,不是carry set,(见CS 稍后)。
    • @Charley Bailey:(1)关于 R2,你是对的。我将相应地编辑我的答案。 (2) 是的,CC 是“Carry Clear”,CS 是“Carry Set”,但是 ARM 使用进位标志进行减法/比较,正如人们所期望的那样(其他架构也是如此)。所以当我在评论中谈论“进位”时,它反映的不是实际的ARM标志位,而是是否有借位。感谢您的更正。
    • @Curd:是的,你是对的。我从来不需要手动使用带有减法的进位位,所以我没有意识到这一点。不过我应该知道,因为我以前使用过 SBC,这需要它正常工作。
    • 我认为POP 之后必须有MOV PC, R1 或类似名称。 TBH 我不知道 PUSHPOP 是 ARM 指令,但我认为它们执行类似于 STMFD R13!, {R14}LDM 等效的操作。
    【解决方案4】:

    我的 ASM 有点生锈,所以请不要烂番茄。假设这从 sub_FFFF7B38 开始:

    命令PUSH {LR} 保留链接寄存器,这是一个特殊寄存器,在子程序调用期间保存返回地址。

    ADDS 设置标志(就像CMN 一样)。此外,ADDS R2, R0, #0R0 添加到 0 并存储在 R2 中。 (来自 Charles 在 cmets 中的更正)

    LDRB R3, [R2] 正在将R2 的内容加载到主存中,而不是由R3 引用的寄存器。 LDRB 只加载一个字节。字中三个未使用的字节在加载时清零。基本上,将R2 从寄存器中取出并妥善保管(也许)。

    CMP R3, #0 执行两个操作数之间的减法并设置寄存器标志,但不存储结果。这些标志导致...

    BEQ loc_FFFF7B521,表示“如果之前的比较相等,则转到loc_FFFF7B521”或if(R3 == 0) {goto loc_FFFF7B521;}

    所以如果R3 不为零,则SUBS R1, #1 命令从R1 中减去一个并设置一个标志。

    如果设置了进位标志,BCC loc_FFFF7B52 将导致执行跳转到loc_FFFF7B52

    (剪断)

    最后,POP {LR} 恢复了在执行此代码之前保存在链接寄存器中的先前返回地址。

    编辑 - 当我在车里时,Curd 说出了我在试图写出答案时所想的内容,但时间不多了。

    【讨论】:

    • ADDS 设置标志(就像CMN 一样)。同样ADDS R2, R0, #0R0添加到0并存储在R2中,它不添加R0R2
    • @Charles - 我在我自己的任何文档(全部为 x86)中都找不到 ADDS 命令,也没有在 Fillip 提供的网站上看到它。感谢您的指正。 :)
    • S 只是一个后缀,ADDS 不是单独的指令。您可以使用S 为任何算术指令添加后缀以设置标志。
    【解决方案5】:

    这里是逐行注释的说明

    sub_FFFF7B38
        PUSH    {LR}          ; save LR (link register) on the stack
        ADDS    R2, R0, #0    ; R2 = R0 + 0 and set flags (could just have been MOV?)
        LDRB    R3, [R2]      ; Load R3 with a single byte from the address at R2
        CMP     R3, #0        ; Compare R3 against 0...
        BEQ     loc_FFFF7B52  ; ...branch to end if equal
        SUBS    R1, #1        ; R1 = R1 - 1 and set flags
        BCC     loc_FFFF7B52  ; branch to end if carry was clear which for subtraction is
                              ; if the result is not positive
    
    loc_FFFF7B46:
        ADDS    R0, #1        ; R0 = R0 + 1 and set flags
        LDRB    R3, [R0]      ; Load R3 with byte from address at R0
        CMP     R3, #0        ; Compare R3 against 0...
        BEQ     loc_FFFF7B52  ; ...branch to end if equal
        SUBS    R1, #1        ; R1 = R1 - 1 and set flags
        BCS     loc_FFFF7B46  ; loop if carry set  which for subtraction is
                              ; if the result is positive
    
    loc_FFFF7B52:
        SUBS    R0, R0, R2    ; R0 = R0 - R2
        POP     {R1}          ; Load what the previously saved value of LR into R1
                              ; Presumably the missing next line is MOV PC, R1 to
                              ; return from the function.
    

    所以在非常基本的 C 代码中:

    void unknown(const char* r0, int r1)
    {
        const char* r2 = r0;
        char r3 = *r2;
        if (r3 == '\0')
            goto end;
        if (--r1 <= 0)
            goto end;
    
    loop:
        r3 = *++r0;
        if (r3 == '\0')
            goto end;
        if (--r1 > 0)
            goto loop;
    
    end:
        return r0 - r2;
    }
    

    添加一些控制结构以摆脱gotos:

    void unknown(const char* r0, int r1)
    {
        const char* r2 = r0;
        char r3 = *r2;
    
        if (r3 != '\0')
        {
            if (--r1 >= 0)
            do
            {
                 if (*++r0 == '\0')
                     break;
            } while (--r1 >= 0);
        }
    
        return r0 - r2;
    }
    

    编辑:现在我对进位位和SUBS 的困惑已经消除,这更有意义。

    简化:

    void unknown(const char* r0, int r1)
    {
        const char* r2 = r0;
    
        while (*r0 != '\0' && --r1 >= 0)
            r0++;
    
        return r0 - r2;
    }
    

    换句话说,这是在r0指向的字符串指针的第一个r1字符中找到第一个NUL的索引,如果没有,则返回r1

    【讨论】:

    • 在第二个示例中,不是while (r1-- ==0),而是while (r1-- != 0)。所以你的最后一个例子实际上是它真正的作用,换句话说,它是一个长度有界的strlen
    • @Andrew McGregor:我切换 if !=, goto end to while ==。我相当确定这是一个正确的转换。我确实花了一些时间对此感到困惑,因为它似乎非常错误。你完全确定我搞砸了吗?
    • ARM 的进位标志反转;这是一个不进位,所以 subs 后跟 bcc 表示如果减法不为零则分支
    • @Andrew McGregor:这就解释了。是否在某个时候进行了更改,因为我当然不记得在 ARM2 上打过这个 - 尽管那是前一段时间了。
    • @Andrew McGregor:好的,我找到了一些文档;进位集是我对加法的预期,但对于减法,它是在减法的结果为正时设置的,考虑到 SBC 的工作原理,这是有道理的。
    猜你喜欢
    • 1970-01-01
    • 2021-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-08
    • 1970-01-01
    • 2013-02-01
    • 2018-10-21
    相关资源
    最近更新 更多