【问题标题】:Invalid pointer issue in insertion sort in ARM assemblyARM 程序集中插入排序中的指针无效问题
【发布时间】:2017-07-22 00:57:40
【问题描述】:

我正在尝试将一个简单的插入排序算法转换为程序集,但有关此特定配置的某些内容导致程序出现 invalid pointer 错误。

这是我正在使用的 C 版本:

int n, array[100], c, d, t;
for (c = 1; c < n - 1; c++) {
    d = c;
    while (d > 0 && array[d] < array[d - 1]) {
        t = array[d];
        array[d] = array[d - 1];
        array[d - 1] = t;
        d--;
    }
}

这是一个正在使用的 C 结构:

typedef struct { 
    int *list;
    int size;
    int maxSize;
} list; 

这是我的汇编文件:

.syntax unified

.text

.align 8
.global insert_ARM
.func insert_ARM, insert_ARM
.type insert_ARM, %function

insert_ARM:
    push {r4-r11, ip, lr}

@ setup
    ldr r4, [r0, #4]
    sub r4, r4, 1    @ r4 = n-1
    mov r5, #1       @ c=1
    mov r6, #16      @ d=0, which starts at #16
    mov r7, #0       @ t=0

for:

   @ d = c ; needs these lines to do the assembly equivalent, which is * 4.
    mov r6, r5      @ d = c
    LSL r6, #2      @ uses logical shift left: multiplies r6 by 4 to get the correct index
    add r6, r6, 16  @ add 16 because that's where the array starts

while:

    @ condition 1: d > 0
    cmp r6, #0      @ if d <= 0, get out of there
    ble forLoopStatements

    @ condition 2: array[d] < array[d-1]
    @ first, I need to define array[d] and array[d-1]
    @ r8 = array[d] and r9 = array[d-1]
    sub r10, r6, #4 @ r10 = d-1
    ldr r9, [r0, r10]   @ r9 = array[d-1]
    ldr r8, [r0, r6]    @ r8 = array[d]
    cmp r9, r8      @ comparing array[d-1] with array[d]
    bge forLoopStatements   @ if array[d] >= array[d-1], get out of there

    @ while effects
    @ note that r8 should still be array[d] here.
    str r9, [r0, r6]    @ array[d] = array[d-1]
    str r8, [r0, r10]   @ array[d-1] = t @ BUG HERE.

    sub r6, r6, #4  @ d--; // does -4 for ARM
    bal while       @ repeat loop


forLoopStatements:
    @ (c<n-1; c++)
    add r5, r5, #1  @ c++
    cmp r5, r4      @ compares c with n-1
    blt for     @ if c < n-1, loop again


end:
    mov r0, r10

    pop {r4-r11, ip, lr}

    BX lr
.endfunc

.end

好像是

str r8, [r0, r10]   @ array[d-1] = t

这会在某个时候导致旅行。

编辑:我发现这条指令中 r8 的数字在某种程度上是不正确的,因为立即使用类似的东西

mov r8, #4

在商店防止错误之前(但当然会使结果不正确)。

在检查 r0 的内容时,碰巧更新超出了范围,因为在此过程中正在修改结构的其他成员。数组索引 0 位于 +16。

【问题讨论】:

  • 使用调试器检查 r0r8 的值,然后确定哪个是错误的。然后弄清楚它是如何得到这个值的。目前尚不清楚您的结构是什么以及r0 指向的位置。你说它包含一个指向数组的指针,但它有一些索引?
  • typedef struct { int *list;整数大小;诠释最大尺寸; } 列表;所以 *list 在 r0 中。我正在尝试使用排序算法对其进行修改。
  • 那么您缺少list 成员的负载。另外,sortedList_index_0 是什么,为什么它的偏移量是16
  • 它必须是 16,因为那是列表的第一个元素所在的位置。 Size 为 +4,maxSize 为 +8,+12 为某种空白,list[0] 为 +16,list[1] 为 +20,等等。我将用我最近的尝试更新我的汇编代码,这应该更直接,但具有相同的算法和相同的问题。
  • 鉴于您在+16 找到list[0] 可能只是偶然的struct 定义...

标签: c assembly arm


【解决方案1】:

您在汇编的翻译中发现了问题。但请注意以下问题:

  • 外部循环应该一直运行到c &lt; n 而不是c &lt; n - 1。按照编码,数组的最后一个元素永远不会移动。

  • 使用 2 个嵌套的 for 循环会更具可读性:

    int n, array[100], c, d, t;
    for (c = 1; c < n; c++) {
        for (d = c; d > 0 && array[d] < array[d - 1]; d--) {
            t = array[d];
            array[d] = array[d - 1];
            array[d - 1] = t;
        }
    }
    

【讨论】:

    【解决方案2】:

    每个人都有不同的编写代码的方法。我的与你的不同,但我想分享我的想法。我会从尽可能简单的开始,让一些东西工作并从那里开始构建。这是 forloop 的示例代码。

    /* forloop.s */
    
    /* int n, array[100], c, d, t;
       for (c=1; c<n-1; c++)
       address of array = r0 = .word ( Raspbian Jessie = 32 bits )
       n = r4 = array size
       c = r5 = 1word = 4memory_bytes = index into array
       d = r6 = c = address in array
       array[d] = r10 = data
    */
    
    .data
    
    .balign 4
    array:
        .word 6, 3, 7, 8, 5, 2, 1, 9, 4
    size:
        .word (size - array)
    
    .text
    
    .global main
    
    main:
        push  {r4-r12, lr}        @ save registers for OS
        ldr   r0, =array          @ load address of array in r0
        ldr   r4, =size           @ load address of size in r4
        ldr   r4, [r4]            @ load size in r4
        sub   r4, #4              @ substract 1 word from r4 (n=n-1)
        mov   r5, #4              @ move 4 in r5 (c=1word=4memory_bytes)
    
    for:                          @ (c=1; c<n-1; c++)
    
        add   r6, r0, r5          @ d (r6) = array address (r0) + (c=4)
    @ while:                      @ while loop would go here
        ldr   r10, [r6], #-4      @ r10 = array[d], d=d-4
        ldr   r11, [r6]           @ r11 = array[d-1]
        @...  @ while code
        cmp   r0,  r6             @ is d > 0 ...
        @...  @continue while loop code
    
        @ back to forloop code
        cmp   r5, r4              @ compare (subtract) r5 (c) from r4 (n)
        add   r5, #4              @ add 1 word to r5 (c++)
        blt   for                 @ end of for loop (c<n-1)
    
    end:
        mov   r0, #0              @ set exit code
        pop   {r4-r12, lr}        @ restore enviroment for return to OS
        bx    lr                  @ return to OS
    

    组装和链接代码并运行它并检查结束状态。

    as -o forloop.o forloop.s
    gcc -o forloop forloop.o
    ./forloop; echo $?
    

    它适用于我在 Raspberry Pi 上。我对gdb 知之甚少,但正如 Jester 所建议的那样,这可能会有所帮助。 (有关更多信息,请参阅http://cs107e.github.io/guides/gdb/ 的中间部分“命令”。)

    pi@RPi0:~/pgm/Asm $ gdb -tui forloop  # Text User Interface
    ---Type <return> to continue, or q <return> to quit--- [Enter]
    (gdb) layout asm
    (gdb) start        @ start is required
    (gdb) layout reg
    (gdb) Ctl-x o      @ Selects registers as Up & Down arrow to see all
    (gdb) si           @ single step
    (gdb) [Enter]      @ repeat single step
    (gdb) run          @ run program to end
    (gdb) q            @ quit gdb
    

    移动向下箭头以查看cpsr 寄存器。最左边的数字是标志8=Negative, 6=Zero&amp;Carry, 4=Zero, 2=Carry, 1=oVerflow

    在 arm 上调试汇编程序的另一种方法是使用 linux printf 命令。这是 myprint.s。

    /* myprint.s */
    
    .data
    
    .balign 4
    format:
        .asciz " %2d  %2d  %2d  %2d  %2d  %2d  %2d  %2d  %2d\n"
    
    .balign 4
    array:
        .word 6, 3, 7, 8, 5, 2, 1, 9, 4
    size:
        .word (size - array)
    
    .text
    
    .global main
    
    print:    @ --- a printf function to print the value in the array ---
        push  {r0-r12, lr}        @ save registers for OS
        mrs   r10,  cpsr          @ save flag settings
        ldr   r11,  =array        @ To print the array[0-8], the array
        ldm   r11,  {r1-r9}       @  address is loaded in r11 and stored
        push  {r4-r10}            @  in reg r1-r9, printf gets args# from
        ldr   r0,   =format       @  format, 3 print from r1-r3, rest from
        bl    printf              @  stack.
        pop   {r4-r10}            @ adjust stack, restore r10 (flags)
        msr   cpsr_f, r10         @ restore saved flags
        pop   {r0-r12, pc}        @ restore reg and return
    
    main:
        push  {r4-r12, lr}        @ save registers for OS
        bl    print               @ --- can be placed anywhere in code ---
        ldr   r0, =array          @ load address of array in r0
        ldr   r4, =size           @ load address of size in r4
        ldr   r4, [r4]            @ load size in r4
        sub   r4, #4              @ substract 1word from r4 (n=n-1)
        mov   r5, #4              @ move 4 in r5 (c=1word=4memory_bytes)
    
    for:                          @ (c=1; c<n-1; c++)
        add   r6, r0, r5          @ d=r6 = array address (r0) + (c=4)
    
    while:                        @ while loop would go here
        ldr   r10, [r6], #-4      @ r10 = array[d], d=d-4
        ldr   r11, [r6]           @ r11 = array[d-1]
        cmp   r10, r11            @ is array[d] < array[d-1]
        bge   forloop_code        @ if not, continue forloop code
        mov   r7,  r11            @ move array[d-1] into t (r7)
        str   r10, [r6], #4       @ store array[d] into array[d-1], (d-1)+4=d
        str   r7,  [r6], #-4      @ store t-array[d-1] into array[d], d-4=(d-1)
        cmp   r6,  r0             @ is d>0 (addr(array[d-1]) > addr(array[0]))?
        bgt   while               @ yes, check if array[d-1] < array[d-2]
    
    forloop_code:                 @ back to forloop code
        bl    print               @ --- can be placed anywhere in code ---
        cmp   r5, r4              @ compare (subtract) r5 (c) from r4 (n)
        add   r5, #4              @ add 1 word to r5 (c++)
        blt   for                 @ end of for loop (c<n-1)
    
    end:
        pop   {r4-r12, lr}        @ restore registers for OS
        mov   r0, #0              @ set exit code
        bx    lr                  @ return to OS
    
    
    as -o myprint.o myprint.s
    gcc -o myprint myprint.o
    ./myprint; echo $?
      6   3   7   8   5   2   1   9   4
      3   6   7   8   5   2   1   9   4
      3   6   7   8   5   2   1   9   4
      3   6   7   8   5   2   1   9   4
      3   5   6   7   8   2   1   9   4
      2   3   5   6   7   8   1   9   4
      1   2   3   5   6   7   8   9   4
      1   2   3   5   6   7   8   9   4
      1   2   3   4   5   6   7   8   9
    0
    

    另一个想法是汇编您的C 代码并使用gdb 来查看C 代码如何在汇编中。这是一个有趣的项目,我不知道插入排序。

    【讨论】:

      【解决方案3】:

      我想通了。除了清理我的代码,我只需要翻译

      while ( d > 0 )
      

      作为

      cmp r6, #16      @ if d <= 0, get out of there
      ble forLoopStatements
      

      而不是

      cmp r6, #0      @ if d <= 0, get out of there
      ble forLoopStatements
      

      保持最小索引为 0。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-11-28
        • 2018-11-03
        • 1970-01-01
        • 2021-05-16
        • 2016-03-16
        • 1970-01-01
        • 2022-01-11
        • 2010-11-20
        相关资源
        最近更新 更多