9k1l4oc8
Posts: 24
Joined: Sat Dec 29, 2018 6:22 pm

BX LR Seg Fault

Thu Dec 12, 2019 3:18 am

I am getting a segmentation fault. When I use gdb it seems to keep going between the instructions on lines 35 to 37 (last three lines of print_r2). I expect for it to pop the other two values back in and then go to the next instruction at line 14. Everything worked before I tried to print register 2. The output is as follows.

Any ideas?

Code: Select all

R2: 1
Segmentation fault

Code: Select all

print_r2:
   ...
        POP {R1} @ goes back to here
        POP {R0}  @ goes here
        BX LR @ issue starts here goes to POP {R1}... second time it goes back to POP {R1} ... to infinity

Code: Select all

.global _start

_start:
        PUSH {R11, LR}
        MOV R1, #0 @ A
        MOV R2, #1 @ B
        @ R3 just holds add temporarily
        MOV R4, #0 @ Total
        MOV R5, #10
        @LDR R5, =0x003D0900 @ 4 million, max
        BL print_r2

main_loop:
        BL fib
        ADD R4, R2
        CMP R2, R5 @ largest value with max
        BL print_r2
        BGT end
        B main_loop

end:
        LDR R0, =format
        MOV R1, R4 @ move total to print it
        BL printf
        MOV R7, #1
        SWI #0
        POP {R11, PC}

print_r2:
        PUSH {R0}
        PUSH {R1}
        MOV R1, R2
        LDR R0, =regval
        BL printf
        POP {R1}
        POP {R0}
        BX LR

fib:
        MOV R3, R2
        ADD R3, R1
        MOV R1, R2
        MOV R2, R3
        BX LR

.data
        format: .ascii "The result is: %d\n"
        regval: .ascii "R2: %d\n"

Schnoogle
Posts: 101
Joined: Sun Feb 11, 2018 4:47 pm

Re: BX LR Seg Fault

Thu Dec 12, 2019 8:38 am

Hi,

I'm definitely not an expert but from the code snippets you shared I assume you expect the link return address register (LR) to be "stacked". But it's not. So whenever you do a branch link call BL the contents of the LR register will be updated containing the last return address. But when you call BX LR this will not magically update LR to the previous return address. You need to store this value on the stack on your own.

Example:

Code: Select all

__main:
	bl __func1	@ this sets lr to point to the instruction after this branch
	mov r0, #0
	
__func1:
	push {lr}	@ store the current return address on the stack as we will other branch in this function
	bl __func2 @ lr is updated and points to the next instruction. if the previous lr was not preserved you will not be able to return from this
	                    @ function properly....
	mov r1, #2
	pop {lr}   @ restore the return address
	bx   lr	@and return -> this could be optimised with pop {pc} which will reduce the return to on instruction

__func2:
       mov x0, #1
       add  x1, x0, #2
       bx    lr	@ as this function does not contain any branch link call, lr is still the same as when entering this function, so using it is save to return
As a rule of thumb for handwritten assembly function I always push the content of LR register to the stack as the first instruction and retrieving it from the stack before returning...

Hope this helps.

Regards
Schnoogle

9k1l4oc8
Posts: 24
Joined: Sat Dec 29, 2018 6:22 pm

Re: BX LR Seg Fault

Thu Dec 12, 2019 9:22 am

Schnoogle wrote:
Thu Dec 12, 2019 8:38 am
Hi,

I'm definitely not an expert but from the code snippets you shared I assume you expect the link return address register (LR) to be "stacked". But it's not. So whenever you do a branch link call BL the contents of the LR register will be updated containing the last return address. But when you call BX LR this will not magically update LR to the previous return address. You need to store this value on the stack on your own.
You are exactly right, this is the issue. I obviously was not thinking. There were also some other issues, but I will post after I am done fixing everything to show you all what I did wrong.

Thank you!

hippy
Posts: 6523
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: BX LR Seg Fault

Thu Dec 12, 2019 2:29 pm

If you "push {lr}" at the start of your "print_r2:" routine you can probably replace your "bx lr" with "pop {pc}". That saves an instruction over 'pop {lr} / bx lr'.

User avatar
rpdom
Posts: 15900
Joined: Sun May 06, 2012 5:17 am
Location: Chelmsford, Essex, UK

Re: BX LR Seg Fault

Thu Dec 12, 2019 4:50 pm

hippy wrote:
Thu Dec 12, 2019 2:29 pm
If you "push {lr}" at the start of your "print_r2:" routine you can probably replace your "bx lr" with "pop {pc}". That saves an instruction over 'pop {lr} / bx lr'.
I'd use "push {r0,r1,lr}" and "pop { r0,r1,pc)" to save even more instructions.

LdB
Posts: 1370
Joined: Wed Dec 07, 2016 2:29 pm

Re: BX LR Seg Fault

Fri Dec 13, 2019 9:26 am

You are carrying values in the registers r0 to r3 which printf will trash because it is allowed to trash them and worse you expect the flags to be maintained. Search ARM 32Bit calling convention.

This for example is nonsense

Code: Select all

        CMP R2, R5 @ largest value with max
        BL print_r2
        BGT end
You do a compare, branch to print_r2 which calls printf and expect you will get the flags back unharmed for the BGT good luck with that :-)

Even in your first call to print_r2 after start it worries me because you seem to have stuff in r0-r3 they are scratch registers and you have to consider them trashed on any call to anything you haven't actually written yourself.

9k1l4oc8
Posts: 24
Joined: Sat Dec 29, 2018 6:22 pm

Re: BX LR Seg Fault

Sun Dec 15, 2019 3:09 am

Thanks for the suggestions, code is fixed now mostly. I am still getting a seg fault on line number 31. Any ideas? Do you see anything else that I am doing wrong?

Also I don't understand the align directive and research on it does not reveal much. How does it work? Does it affect all code after it or just the next line? Am I even remotely using it correctly?

Code: Select all

     1  .text
     2  .global _start
     3  .align 2
     4
     5  _start:
     6          PUSH {R11, LR}
     7          MOV R1, #0 @ A
     8          MOV R2, #1 @ B
     9          @ R3 just holds add temporarily
    10          MOV R4, #0 @ Total
    11          LDR R5, =0x003D0900 @ 4 million, max
    12
    13  main_loop:
    14          BL fib
    15          BL mod_r2_by_2
    16          CMP R3, #0
    17          BNE .skip_add
    18          ADD R4, R2
    19  .skip_add:
    20          CMP R2, R5 @ largest value with max
    21          BGT end
    22          BL print_r2
    23          B main_loop
    24
    25  end:
    26          LDR R0, =adr_format
    27          MOV R1, R4 @ move total to print it
    28          PUSH {LR}
    29          BL printf
    30          POP {LR}
    31          POP {R11, PC}
    32
    33  print_r2:
    34          PUSH {R0-R5}
    35          MOV R1, R2
    36          LDR R0, =adr_regval
    37          PUSH {LR}
    38          BL printf
    39          POP {LR}
    40          POP {R0-R5}
    41          BX LR
    42
    43  fib:
    44          MOV R3, R2
    45          ADD R3, R1
    46          MOV R1, R2
    47          MOV R2, R3
    48          BX LR
    49
    50  mod_r2_by_2: @ return value is r3
    51          PUSH {R0-R2}
    52          CMP R2, #2
    53          BLT .mod_end
    54          MOV R0, #2
    55          MOV R3, R2
    56          UDIV R3, R0
    57          MULS R3, R0
    58          SUB R3, R2, R3
    59          POP {R0-R2}
    60  .mod_end:
    61          BX LR
    62
    63  .align 4
    64  adr_format: .asciz "The result is: %d\n"
    65  adr_regval: .asciz "R2: %d\n"
In GDB I get the following for line 31.

Code: Select all

Warning:
Cannot insert breakpoint 0.
Cannot access memory at address 0x0

User avatar
Paeryn
Posts: 2782
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: BX LR Seg Fault

Sun Dec 15, 2019 4:58 am

9k1l4oc8 wrote:
Sun Dec 15, 2019 3:09 am
Thanks for the suggestions, code is fixed now mostly. I am still getting a seg fault on line number 31. Any ideas? Do you see anything else that I am doing wrong?
Your mod_r2_by_2 function fails to keep the stack in order. At the start you push {r0-r3} and then if r2 < 2 you jump to .mod_end which returns to lr but you didn't pop {r0-r2} before doing so. Try moving the label up a line to before the pop.

Code: Select all

mod_r2_by_2: @ return value is r3
        PUSH {R0-R2}
        CMP R2, #2
        BLT .mod_end
        MOV R0, #2
        MOV R3, R2
        UDIV R3, R0
        MULS R3, R0
        SUB R3, R2, R3
.mod_end:
        POP {R0-R2}
        BX LR
9k1l4oc8 wrote:
Sun Dec 15, 2019 3:09 am
Also I don't understand the align directive and research on it does not reveal much. How does it work? Does it affect all code after it or just the next line? Am I even remotely using it correctly?
The align directive makes sure the address of the next item is aligned to the given value. .align 4 says if the current address isn't divisible by 4 then add extra bytes until it is. So your initial .align 2 is wrong as only Thumb instructions can be half-word aligned (it happens to work as the first address is 0 which satisfies .align 4) .align 2 says if the lowest 2 bits of the address aren't 0 then add bytes until they are. <thanks rpdom for correcting me, I was thinking about a different processor>
9k1l4oc8 wrote:
Sun Dec 15, 2019 3:09 am
In GDB I get the following for line 31.

Code: Select all

Warning:
Cannot insert breakpoint 0.
Cannot access memory at address 0x0
What did you try doing when you got that error?

If you are running this in Linux then your program exit is wrong if you aren't using the gcc standard startup, you need to do a syscall to exit with the return code in r0 to terminate rather than branching to lr (and you don't need to preserve any registers).

Code: Select all

end:
        LDR R0, =adr_format
        MOV R1, R4 @ move total to print it
        PUSH {LR}
        BL printf
        POP {LR}
        POP {R11, lr} @ don't pop the original LR to PC
        mov r7, #1    @ syscall number for exit
        mov r0, #0    @ no error
        svc #0        @ make the syscall
Last edited by Paeryn on Sun Dec 15, 2019 5:43 am, edited 1 time in total.
She who travels light — forgot something.

User avatar
rpdom
Posts: 15900
Joined: Sun May 06, 2012 5:17 am
Location: Chelmsford, Essex, UK

Re: BX LR Seg Fault

Sun Dec 15, 2019 5:26 am

Paeryn wrote:
Sun Dec 15, 2019 4:58 am
The align directive makes sure the address of the next item is aligned to the given value. .align 4 says if the current address isn't divisible by 4 then add extra bytes until it is. So your initial .align 2 is wrong as only Thumb instructions can be half-word aligned (it happens to work as the first address is 0 which satisfies .align 4)
Has .align changed then? It used to be powers of two. align 2 meant 2 to the power 2 i.e. 4-byte aligned. align 4 would be 2 to the power of 4 which is 16 byte aligned.
Paeryn wrote:
Sun Dec 15, 2019 4:58 am
Your mod_r2_by_2 function fails to keep the stack in order.
I didn't spot that one :oops:

The mod_r2_by_2 function seems over complex for what should be a simple operation, unless I have misunderstood what it is doing.

Code: Select all

mod_r2_by_2@ @ return value is r3
        AND R3, R2, #1
        BX LR

User avatar
Paeryn
Posts: 2782
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: BX LR Seg Fault

Sun Dec 15, 2019 5:37 am

rpdom wrote:
Sun Dec 15, 2019 5:26 am
Paeryn wrote:
Sun Dec 15, 2019 4:58 am
The align directive makes sure the address of the next item is aligned to the given value. .align 4 says if the current address isn't divisible by 4 then add extra bytes until it is. So your initial .align 2 is wrong as only Thumb instructions can be half-word aligned (it happens to work as the first address is 0 which satisfies .align 4)
Has .align changed then? It used to be powers of two. align 2 meant 2 to the power 2 i.e. 4-byte aligned. align 4 would be 2 to the power of 4 which is 16 byte aligned.
Oops, sorry, yes it is powers of 2 on ARM, so .align 2 is divisible by 4.
She who travels light — forgot something.

9k1l4oc8
Posts: 24
Joined: Sat Dec 29, 2018 6:22 pm

Re: BX LR Seg Fault

Sun Dec 15, 2019 6:24 am

Thank you both so much! Paeryn and rpdom that was so helpful!

I think I have my code all fixed up thanks to you two. I did not realize there was an AND instruction, not that I have really taken the time to find every last instruction. That was much better. Also I couldn't figure out how to exit properly. I thought I was doing that based on how I saw some C code compiled.

I feel like I am missing so much. I am getting a book on ARM and hoping to fill in all the gaps. I will have to post more coding projects and please inform me on what I am missing.

Thanks again! If you see any more errors please point them out. Code will be below.

Also can someone please explain when I should be using .align? What are the signs I will have an issue if I don't use it? And for ARM should I always be using '.align 2' when I need it?

Another question I have is should I use LDR, like how I have it in _start in the code blow, or should I be using MOV and MOVT?

Code: Select all

.text
.global _start

_start:
        PUSH {FP, LR}
        MOV R1, #0 @ A
        MOV R2, #1 @ B
        @ R3 just holds add temporarily
        MOV R4, #0 @ Total
        @LDR R5, =0x003D0900 @ 4 million, max
        MOV R5, #0x0900
        MOVT R5, #0x003D

main_loop:
        BL fib
        BL mod_r2_by_2
        CMP R3, #0
        BNE .skip_add
        ADD R4, R2
.skip_add:
        CMP R2, R5 @ largest value with max
        BGT end
        BL print_r2
        B main_loop

end:
        LDR R0, =adr_format
        MOV R1, R4 @ move total to print it
        PUSH {LR}
        BL printf
        POP {LR}
        POP {FP, LR}
        MOV R7, #1
        MOV R0, #0
        SVC #0

print_r2:
        PUSH {R0-R5}
        MOV R1, R2
        LDR R0, =adr_regval
        PUSH {LR}
        BL printf
        POP {LR}
        POP {R0-R5}
        BX LR

fib:
        MOV R3, R2
        ADD R3, R1
        MOV R1, R2
        MOV R2, R3
        BX LR

mod_r2_by_2: @ return value is r3
        AND R3, R2, #1
        BX LR

.align 2
adr_format: .asciz "The result is: %d\n"
adr_regval: .asciz "R2: %d\n"

njh
Posts: 44
Joined: Wed Aug 09, 2017 2:07 pm

Re: BX LR Seg Fault

Sun Dec 15, 2019 9:52 am

The imortant thing to read up on is the ARM C Function Calling Convention. This is, as the name suggests, not an intrinsic part of assembly language but a convention for calling 'C' functions, that printf() uses, for instance. It makes sense to use it even for your own private assembler functions.

Since you are calling printf() you need a working C runtime, so I would start with main() rather than _start. You exit from main by returning 0, that is
MOV r0, #0
BX LR ; <-if you used STM or PUSH you may need to do something different

The .align 2 is needed when swapping between code and data and especially after strings, to ensure the following code or data are placed at a 4-byte aligned address.

LDR= is a bit of assembler magic that will generate some combination of MOV, MOVT or LDR instructions to get a value into a register. Sometimes it generates a word of data which will typically go at the end of the current section (file). It's up to you if you want to use it.

User avatar
Paeryn
Posts: 2782
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: BX LR Seg Fault

Sun Dec 15, 2019 7:43 pm

njh wrote:
Sun Dec 15, 2019 9:52 am
LDR= is a bit of assembler magic that will generate some combination of MOV, MOVT or LDR instructions to get a value into a register. Sometimes it generates a word of data which will typically go at the end of the current section (file). It's up to you if you want to use it.
It won't generate a combination of instructions, it will generate one instruction. If possible it will generate a mov, mvn or movw instruction depending on the value, mov can handle 0 to 4095, mvn can handle -1 to -4096 (logical not of what mov accepts) and movw can handle 0 to 65535. Movt won't be generated as that only affect the upper 16 bits of the register leaving the lower 16 bits alone. If one of the mov instructions can't handle the value then the value is stored in a literal pool and the instruction used is of the form ldr r0, [pc, #offset]. The literal pool needs to be within 4096 bytes of the instruction (as that is the limit of the offset).
She who travels light — forgot something.

njh
Posts: 44
Joined: Wed Aug 09, 2017 2:07 pm

Re: BX LR Seg Fault

Sun Dec 15, 2019 10:00 pm

Paeryn wrote:
Sun Dec 15, 2019 7:43 pm
It won't generate a combination of instructions, it will generate one instruction. If possible it will generate a mov, mvn or movw instruction depending on the value, mov can handle 0 to 4095, mvn can handle -1 to -4096 (logical not of what mov accepts) and movw can handle 0 to 65535. Movt won't be generated as that only affect the upper 16 bits of the register leaving the lower 16 bits alone. If one of the mov instructions can't handle the value then the value is stored in a literal pool and the instruction used is of the form ldr r0, [pc, #offset]. The literal pool needs to be within 4096 bytes of the instruction (as that is the limit of the offset).
Oops. You're right of course. I'd mixed it up with a half-remembered MOV32, which we don't even have in this toolchain.

LdB
Posts: 1370
Joined: Wed Dec 07, 2016 2:29 pm

Re: BX LR Seg Fault

Mon Dec 16, 2019 2:18 am

If the literal pool isn't within 4096 bytes it is allowed to add an extra opcode(s), that is up to compiler

9k1l4oc8
Posts: 24
Joined: Sat Dec 29, 2018 6:22 pm

Re: BX LR Seg Fault

Mon Dec 16, 2019 4:20 am

njh wrote:
Sun Dec 15, 2019 9:52 am
The imortant thing to read up on is the ARM C Function Calling Convention. This is, as the name suggests, not an intrinsic part of assembly language but a convention for calling 'C' functions, that printf() uses, for instance. It makes sense to use it even for your own private assembler functions.

Since you are calling printf() you need a working C runtime, so I would start with main() rather than _start. You exit from main by returning 0, that is
MOV r0, #0
BX LR ; <-if you used STM or PUSH you may need to do something different

The .align 2 is needed when swapping between code and data and especially after strings, to ensure the following code or data are placed at a 4-byte aligned address.

LDR= is a bit of assembler magic that will generate some combination of MOV, MOVT or LDR instructions to get a value into a register. Sometimes it generates a word of data which will typically go at the end of the current section (file). It's up to you if you want to use it.
Thank you for all the tips, they work great.

The only issue I have is that if I only use GCC then I get the following error.

Code: Select all

Assembler messages:
Error: selected processor does not support `movt R5,#0x003D' in ARM mode
In fact if I use some prior instructions I was using I get the following error as well. If I use 'as' instead of 'gcc' then everything works fine. Should I not use gcc to make the '.o' files?

Code: Select all

Assembler messages:
Error: selected processor does not support `udiv R2,#1' in ARM mode

User avatar
Paeryn
Posts: 2782
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: BX LR Seg Fault

Mon Dec 16, 2019 5:01 am

9k1l4oc8 wrote:
Mon Dec 16, 2019 4:20 am
The only issue I have is that if I only use GCC then I get the following error.

Code: Select all

Assembler messages:
Error: selected processor does not support `movt R5,#0x003D' in ARM mode
That's because Raspbian's gcc defaults to generating code for armv6 to be compatible with the RPi0 and instructions like movt weren't introduced until armv7 (which all RPis from 2 up can use). Either tell gcc that you are compiling for a different processor e.g.

Code: Select all

gcc program.s -o program -nostartfiles -mcpu=cortex-a53
or put a cpu directive at the top of your code e.g.

Code: Select all

        .cpu cortex-a53
LdB wrote:
Mon Dec 16, 2019 2:18 am
If the literal pool isn't within 4096 bytes it is allowed to add an extra opcode(s), that is up to compiler
Are you sure about that? I've always known the LDR= pseudo-instruction to assemble into exactly one instruction and if that can't be done then it should generate an error.
She who travels light — forgot something.

9k1l4oc8
Posts: 24
Joined: Sat Dec 29, 2018 6:22 pm

Re: BX LR Seg Fault

Mon Dec 16, 2019 5:21 am

Paeryn wrote:
Mon Dec 16, 2019 5:01 am
9k1l4oc8 wrote:
Mon Dec 16, 2019 4:20 am
The only issue I have is that if I only use GCC then I get the following error.

Code: Select all

Assembler messages:
Error: selected processor does not support `movt R5,#0x003D' in ARM mode
That's because Raspbian's gcc defaults to generating code for armv6 to be compatible with the RPi0 and instructions like movt weren't introduced until armv7 (which all RPis from 2 up can use). Either tell gcc that you are compiling for a different processor e.g.

Code: Select all

gcc program.s -o program -nostartfiles -mcpu=cortex-a53
or put a cpu directive at the top of your code e.g.

Code: Select all

        .cpu cortex-a53
Thank you!

LdB
Posts: 1370
Joined: Wed Dec 07, 2016 2:29 pm

Re: BX LR Seg Fault

Mon Dec 16, 2019 7:00 am

Pseudo-Instructions are allowed to provide small number of substitute instructions it is called veneer. I know the BL instruction does it on Keil DS5 for example. The whole point to pseudo instructions is to let the assembler work it out. Whether XYZ compiler does it is up to them but there is nothing to stop it happening. So I am not doubting some compilers limit LDR to a single instruction but that is not set in stone. I can see a problem with LDR that the compiler would have to "find free registers" or reserve them to work a practical veneer.

User avatar
rpdom
Posts: 15900
Joined: Sun May 06, 2012 5:17 am
Location: Chelmsford, Essex, UK

Re: BX LR Seg Fault

Mon Dec 16, 2019 11:06 am

In this particular case

Code: Select all

        MOV R5, #0x0900
        MOVT R5, #0x003D
to allow the code to work on ARMv6 and later 32 bit ARM systems, I would use something like

Code: Select all

        MOV R5, #0x0900
        ADD R5, R5, #0x003D0000
I believe the assembler should translate the ADD instruction to

Code: Select all

        ADD R5, R5, #0x3D LSL 16
if not, I would code that in manually.

LdB
Posts: 1370
Joined: Wed Dec 07, 2016 2:29 pm

Re: BX LR Seg Fault

Mon Dec 16, 2019 1:44 pm

Nice example rpdom that is indeed a very practical example which does not even involve another register.

Return to “Bare metal, Assembly language”