Почему у меня в программе сборки arm32 есть segfault?

Я пытаюсь написать программу на ассемблере, которая пишет весь char** argv контент. В C это будет так:

#include <stdio.h>

int main(int argc, char** argv) {
    for (unsigned int i = 0; argv[i] != 0; i++)
        puts(argv[i]);
    return 0;
}

и я написал это в сборке так:


.global main

.data

.bss

.text

.align 4

@r0 = argc
@r1 = argv

main:
        @prologue
        stmfd sp!, {fp, lr}
        add fp, sp, #4
        sub sp, sp, #8

        @i = 0
        mov r0, #0
        str r0, [fp, #-8]
        b loop_begin

loop_content:
        bl puts
        ldr r0, [fp, #-8]
        add r0, r0, #1
        str r0, [fp, #-8]

loop_begin:
        @r0 = i
        ldr r0, [fp, #-8]

        @i = i * 4
        lsl r0, r0, #2

        @r0 = argv + i
        add r0, r1, r0

        @r3 = argv[i]
        ldr r0, [r0]

        @argv[i] == 0?
        cmp r0, #0

        @if arvg[i] != 0 go to ...
        bne loop_content

        mov r0, #0
        sub sp, fp, #4
        ldmfd sp!, {fp, lr}
        bx lr

но это дает мне segfault в

        str r0, [fp, #-8]

Понятия не имею, почему, у вас есть идеи, почему это происходит?


person Mattern    schedule 10.12.2019    source источник
comment
@Afshin, я собираюсь использовать этот код и для char** envp, так что это условие более уникальное :)   -  person Mattern    schedule 10.12.2019
comment
Вы уверены, что именно здесь происходит segfault? В какой строчке, как у вас таких инструкций несколько?   -  person Jester    schedule 10.12.2019
comment
@Jester, да, я пытался отладить его с помощью gdb, так что это происходит именно в этой строке   -  person Mattern    schedule 10.12.2019
comment
Я попытался использовать r9 вместо локальной переменной, теперь проблема та же, но ошибка возникает в add r9, r9, #1, сразу после bl puts, из-за этого я думаю, что bl puts может вызвать ошибку, но нет очевидных причин, почему   -  person Mattern    schedule 10.12.2019
comment
Это add не может быть виновато. Скорее всего, вы неправильно использовали gdb. В вашем коде есть проблемы, например, полагаться на то, что значение в r1 сохраняется при вызовах. На самом деле, просто исправление заставляет меня работать.   -  person Jester    schedule 10.12.2019
comment
@Jester, пожалуйста, объясните, в чем проблема с r1?   -  person Mattern    schedule 10.12.2019
comment
Это реестр, сохраненный вызывающим абонентом. bl puts уничтожит его. Вам нужно будет сохранить его самостоятельно.   -  person Jester    schedule 10.12.2019
comment
@ Шут, святые регистры ... Я добавил mov r1, r8 перед звонком и mov r8, r1 сразу после, и это помогло! Большое спасибо! Вы можете ответить на вопрос, чтобы я мог отметить его как правильный ответ   -  person Mattern    schedule 10.12.2019
comment
Обратите внимание, что вы не можете просто злоупотреблять регистрами, сохраненными вызываемым пользователем, не сохраняя их, даже если это работает. По той же причине bl puts сохраняет r8 для вас, вы также должны сохранить его для вызывающего абонента (в данном случае код libc).   -  person Jester    schedule 10.12.2019
comment
@Jester, спасибо за совет :)   -  person Mattern    schedule 10.12.2019
comment
@Afshin: argv[] - массив указателей с завершающим нулем. (argv[argc] == NULL) Код C безопасен.   -  person Peter Cordes    schedule 11.12.2019