Page 1 of 1
BX LR Seg Fault
Posted: Thu Dec 12, 2019 3:18 am
by 9k1l4oc8
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
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"
Re: BX LR Seg Fault
Posted: Thu Dec 12, 2019 8:38 am
by Schnoogle
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
Re: BX LR Seg Fault
Posted: Thu Dec 12, 2019 9:22 am
by 9k1l4oc8
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!
Re: BX LR Seg Fault
Posted: Thu Dec 12, 2019 2:29 pm
by hippy
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'.
Re: BX LR Seg Fault
Posted: Thu Dec 12, 2019 4:50 pm
by rpdom
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.
Re: BX LR Seg Fault
Posted: Fri Dec 13, 2019 9:26 am
by LdB
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.
Re: BX LR Seg Fault
Posted: Sun Dec 15, 2019 3:09 am
by 9k1l4oc8
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
Re: BX LR Seg Fault
Posted: Sun Dec 15, 2019 4:58 am
by Paeryn
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
Re: BX LR Seg Fault
Posted: Sun Dec 15, 2019 5:26 am
by rpdom
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
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
Re: BX LR Seg Fault
Posted: Sun Dec 15, 2019 5:37 am
by Paeryn
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.
Re: BX LR Seg Fault
Posted: Sun Dec 15, 2019 6:24 am
by 9k1l4oc8
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"
Re: BX LR Seg Fault
Posted: Sun Dec 15, 2019 9:52 am
by njh
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.
Re: BX LR Seg Fault
Posted: Sun Dec 15, 2019 7:43 pm
by Paeryn
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).
Re: BX LR Seg Fault
Posted: Sun Dec 15, 2019 10:00 pm
by njh
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.
Re: BX LR Seg Fault
Posted: Mon Dec 16, 2019 2:18 am
by LdB
If the literal pool isn't within 4096 bytes it is allowed to add an extra opcode(s), that is up to compiler
Re: BX LR Seg Fault
Posted: Mon Dec 16, 2019 4:20 am
by 9k1l4oc8
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
Re: BX LR Seg Fault
Posted: Mon Dec 16, 2019 5:01 am
by Paeryn
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.
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.
Re: BX LR Seg Fault
Posted: Mon Dec 16, 2019 5:21 am
by 9k1l4oc8
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.
Thank you!
Re: BX LR Seg Fault
Posted: Mon Dec 16, 2019 7:00 am
by LdB
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.
Re: BX LR Seg Fault
Posted: Mon Dec 16, 2019 11:06 am
by rpdom
In this particular case
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
if not, I would code that in manually.
Re: BX LR Seg Fault
Posted: Mon Dec 16, 2019 1:44 pm
by LdB
Nice example rpdom that is indeed a very practical example which does not even involve another register.