ARM程序集中插入排序中的指针问题无效

时间:2017-07-22 00:57:40

标签: c assembly arm

我正在尝试将简单的插入排序算法转换为汇编,但有关此特定配置的某些操作会导致程序出现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。

3 个答案:

答案 0 :(得分:1)

您在汇编转换中发现了问题。但请注意以下问题:

  • 外部循环应该一直运行到c < n而不是c < 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;
        }
    }
    

答案 1 :(得分:1)

每个人都有不同的写代码方法。我与你的不同,但我想分享我的想法。我会从尽可能简单的开始,从那里开始工作和构建。以下是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所建议的那样有用。 (有关详细信息,请参阅中间部分&#34;命令&#34;在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&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代码。这是一个有趣的项目,我不知道插入排序。

答案 2 :(得分:0)

我明白了。除了清理我的代码,我只需要翻译

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。