3

When I try to use % in C code in my ARM bare-metal program, it needs a wrapper from libgcc. No problem, I can link that in. When I do, the linker stops complaining, but then the program hangs (if I watch the registers they actually start cycling in inexplicable ways) at the mod use. Commenting out the line with the % makes the program not hang in this way, so that's definitely the problem.

I have constructed a trivial example of the problem at https://gist.github.com/1724746

I run the test using:

qemu-system-arm -M versatilepb -cpu arm1176 -nographic -kernel kernel.elf | xxd

Then ^a-x to quit it, and with the % line commented out I get the bytes I expect of output, but with the line in there I do not get any such output.

Any idea what's going on here?

Edit: the cross compiler I'm using is the default on on Ubuntu: https://launchpad.net/gcc-linaro

singpolyma
  • 10,999
  • 5
  • 47
  • 71
  • you need to examine the disassembly, can you please post the relevant disassembly for the program you created? Have you run this on real hardware and not qemu? – old_timer Feb 02 '12 at 17:55
  • I have posted my disassembled code at https://singpolyma.net/test.asm (warning: very large!) -- I do not have good access to any real hardware. My target is QEMU. – singpolyma Feb 02 '12 at 18:59

1 Answers1

2

So it loads r0 and r1 with a 2 (well it does the plus one thing on current task+1

10648:  e1a00003    mov r0, r3
1064c:  e51b1010    ldr r1, [fp, #-16]
10650:  eb00fd36    bl  4fb30 <____aeabi_uidivmod_veneer>

I guess they used _veneer to switch from arm to thumb, it is a trampoline to switch modes:

0004fb30 <____aeabi_uidivmod_veneer>:
   4fb30:   e51ff004    ldr pc, [pc, #-4]   ; 4fb34 <____aeabi_uidivmod_veneer+0x4>
   4fb34:   00010905    .word   0x00010905

That takes you here, the actual modulo operation, this is thumb code, thumb mode

00010904 <__aeabi_uidivmod>:
   10904:   2900        cmp r1, #0
   10906:   d0f8        beq.n   108fa <__aeabi_uidiv+0x252>
   10908:   e92d 4003   stmdb   sp!, {r0, r1, lr}
   1090c:   f7ff fecc   bl  106a8 <__aeabi_uidiv>
   10910:   e8bd 4006   ldmia.w sp!, {r1, r2, lr}
   10914:   fb02 f300   mul.w   r3, r2, r0
   10918:   eba1 0103   sub.w   r1, r1, r3
   1091c:   4770        bx  lr
   1091e:   bf00        nop

So it looks to do a normal divide, then multiplies the result and subtracts for example

12345 % 100 = 12345 - ((12345/100)*100) = 12345 - (123*100) = 12345 - 12300 = 45

I wonder if the problem is thumb mode. The arm1176 definitely has thumb mode, the real one, and I qemu can do thumb.

You could try an experiment though to find out assemble and link:

.thumb
.thumb_func
.globl thumb_test
thumb_test:
   add r0,#1
   bx lr

arm-none-linux-gnueabi-as thumb_test.s -o thumb_test.o

or whatever your toolchain prefix is if any, and link the .o file in with everything else.

in the C code declare it as

unsigned int thumb_test ( unsigned int );

and whatever you pass it you should get that value plus one back...try this instead of the modulo.

Another thing to try is just a straight divide, see if divide works but not modulo, maybe the problem is the multiply instruction? Who knows.

Hmmmm, I think I see the problem:

0004fb30 <____aeabi_uidivmod_veneer>:
   4fb30:   e51ff004    ldr pc, [pc, #-4]   ; 4fb34 <____aeabi_uidivmod_veneer+0x4>
   4fb34:   00010905    .word   0x00010905

You cant switch modes with an ldr it has to be a bx or blx, it is probably going to the undefined handler when it tries to execute thumb code in arm mode.

I have some examples of how switching modes needs to work, in particular written for qemu. Now this is a low level example, no printf for example, I do have uart output with a way to see stuff on the uart. If this is the problem (switching to thumb mode) then you need to examine how you are compiling and linking your program. You might need to specify interwork or might need to understand how your toolchain was built if you are not using the formerly code sourcery (now consumed by mentor graphics) toolchain. that is assuming you are using something gnu/gcc based.

If you have a way to put an undefined handler in there or if you are able to watch the trace and see the pc drop to something near address 0x0000000 that is probably what is happening. If you build with my little thumb_test thing and it uses an ldr instead of bx to get there then that should not work either it should still crash...

old_timer
  • 69,149
  • 8
  • 89
  • 168
  • Linking in your thumb_test.s code and calling it works. Adding -mthumb-interwork to CFLAGS does not work. Hmm, removing the ".thumb_func" from your test then causes the same problem. So it's not realising this other code is thumb, probably, somehow. So I need to find a way to convince the compiler that it's calling into thumb code so it'll generate the right trampoline? – singpolyma Feb 03 '12 at 21:36
  • It's strange, though, changing the main code to compile in thumb mode does not fix it. Dissasembly: http://singpolyma.net/test2.asm – singpolyma Feb 03 '12 at 21:42
  • there is a lot of thumb2 code in there the arm11 does not support thumb2. If you remove the modulo but compile with this thumb version what happens? – old_timer Feb 03 '12 at 22:10
  • when you remove .thumb_func the assembler does not know this is thumb code, assume arm and makes an arm call to that address, you have to use .thumb_func or some other equivalent (if any) to tell the assembler this is a thumb target (set the msbit of the address when you call it). basically you have to use bx or blx to switch modes and the lsbit of the address indicates the mode (it is not used as an address, just a mode bit) 1 is thumb 0 is arm. – old_timer Feb 03 '12 at 22:12
  • because this is a simulator and not real you might be getting away with some instructions not supported in hardware, so be aware of that. – old_timer Feb 03 '12 at 22:20
  • I said that wrong, if you dont put the .thumb_func in there the assembler does not mark it as a thumb address, which is pretty stupid because the assembler DOES know that this section of code is thumb code. the linker will assume it is an arm address and not encode the right address for the bx. if you are in thumb mode and do an ldr pc, of a thumb address you should be okay. i suspect you may also be dealing with thumb2 instructions that are not supported either in the divide/modulo or now everywhere. – old_timer Feb 03 '12 at 22:23
  • you may find it an ugly solution but I often do something like this http://github.com/dwelch67/stm32f4d in the adventure directory, then gcclib contains the files from gcc. Granted this stm32f4d is thumb2 and you want arm or thumb, the same approach works just fine. put a syntax error in the gcc lib source, then build a cross compiler when it fails on the syntax error steal the command line and tweak as necessary. Normally I just never use divide or modulo. – old_timer Feb 03 '12 at 22:26