2

I'm trying to write a firmware mod (to existing firmware, for which i don't have source code) All Thumb code.

does anybody have any idea how to do this, in gcc as (GAS) assembler: Use BL without having to manually calculate offsets, when BL'ing to some existing function (not in my code.. but i know its address)

Currently, if i want to use BL ...i have to : -go back in my code -figure out and add all the bytes that would result from assembling all the previous instructions in the function i'm writing -add the begining address of my function to that (i specify the starting address of what i'm writing, in the linker script) -and then substract the address of the firmfunc function i want to call

All this... just to calculate the offset... to be able to write abl offset... to call an existing firmware function? And if i change any code before that BL, i have to do it all over again manually !
See.. this is why i want to learn to use BX right... instead of BL

Also, i don't quite understand the BX. If i use BX to jump to an absolute address, do i have to increase the actual address by 1, when caling Thumb code from Thumb code (to keep the lsb byte 1)... and the CPU will know it's thumb code ?

vmanta
  • 346
  • 2
  • 3
  • 12

2 Answers2

3

BIG EDIT:

Changing the answer based on what I have learned recently and a better understanding of the question

First off I dont know how to tell the linker to generate a bl to an address that is a hardcoded address and not actually in this code. You might try to rig up an elf file that has labels and such but dummy or no code, dont know if that will fool the linker or not. You would have to modify the linker script as well. not worth it.

your other question that was spawned from this one:

Arm/Thumb: using BX in Thumb code, to call a Thumb function, or to jump to a Thumb instruction in another function

For branching this works just fine:

LDR R6, =0x24000
ADD R6, #1       @ (set lsb to 1)
BX R6

or save an instruction and just do this

LDR R6, =0x24001
BX R6

if you want to branch link and you know the address and you are in thumb mode and want to get to thumb code then

  ldr r6,=0x24001
  bl thumb_trampoline
  ;@returns here
  ...
.thumb_func
thumb_trampoline:
  bx r6

And almost the exact same if you are starting in arm mode, and want to get to thumb code at an address you already know.

  ldr r6,=0x24001
  bl arm_trampoline
  ;@returns here
  ...
arm_trampoline:
  bx r6

You have to know that you can trash r6 in this way (make sure r6 isnt saving some value being used by some code that called this code).

Very sorry misleading you with the other answer, I could swear that mov lr,pc pulled in the lsbit as a mode, but it doesnt.

Community
  • 1
  • 1
old_timer
  • 69,149
  • 8
  • 89
  • 168
  • now if you look at the encoding for the instruction bl is relative not absolute, you can only branch to addresses relative to the program counter. This is a fixed word length instruction set, you will want to either use bx which allows mode switching but is limited to registers, use it as I have shown above or if possible based on the mode (maybe only works in arm mode) ldr pc,=function_name, which is a long branch and uses another word (like a variable word length instruction) but like bx is a branch not a branch link. – old_timer Feb 19 '12 at 15:03
  • Thanks, for nice explanation. But i would like to be able to BL to existing thumb functions in the firmware, for which i know the address. This way i don't have to mess with LR, but i don't know how to put this in assembler... or linker script... like if i do BL LABEL, i have to tell the assembler what address that LABEL is at... because it's not in my code... and don't know how to tell it that – vmanta Feb 19 '12 at 16:27
  • i'll accept the answer, it's good info ... still if anyone knows.. pls reply. There has to be a way. How does the linker link to functions you import (external functions your code calls) - what directive to use for that in asm or linker script? – vmanta Feb 19 '12 at 16:35
  • ahh, I see what you are saying. yeah some how you would need to have the linker know about a label with a specific address. dont know how to do that, you can uncheck my answer and let someone else try, no sweat... – old_timer Feb 19 '12 at 21:20
  • in your example above, won't ldr r2,put32add // mov lr,pc // bx r2 make it return to the mov lr, pc line ? as that's where you set lr to... – vmanta Feb 20 '12 at 00:39
  • the program counter, from a programmers perspective is two instructions ahead. So if you are at an instruction at address 0x100 in memory and in thumb mode then a mov lr,pc will put 0x104 in the lr. if in arm mode then mov lr,pc would put 0x108 in the lr. – old_timer Feb 20 '12 at 03:02
  • I edited the question to hopefully clarify the question regarding BL. As for BX i have more questions, so i posted another thread – vmanta Feb 20 '12 at 20:58
  • 1
    MY THUMB TO ARM CODE IS BROKEN! it does no work this way, see this question instead http://stackoverflow.com/questions/9368360/arm-thumb-using-bx-in-thumb-code-to-call-a-thumb-function-or-to-jump-to-a-thu – old_timer Feb 21 '12 at 00:24
  • The trampoline is excellent solution - I can call that using a BL, by name in my code, and I don't have to calculate offsets manually, and don't have to worry about messing with LR manually, either ! Very good ! As far as i've seen R4-R7 are preserved by any function (at least in this firmware i have dissasembled) This is Armv4T, and was compiled with ADSv1.2 – vmanta Feb 21 '12 at 03:50
0

The accepted answer achieves the desired goal, but to address the answer exactly as asked you can use the .equ directive to associate a constant vale with a symbol, that can then be used as an operand to instructions. This has the assembler synthesise the trampoline if/when necessary:

equ myFirmwareFunction, 0x12346570
.globl _start
        mov r0, #42
        b   myFirmwareFunction

Which generates the following assembly[1]

01000000 <_start>:
 1000000:   e3a0002a    mov r0, #42 ; 0x2a
 1000004:   eaffffff    b   1000008 <__*ABS*0x12346570_veneer>

01000008 <__*ABS*0x12346570_veneer>:
__*ABS*0x12346570_veneer():
 1000008:   e51ff004    ldr pc, [pc, #-4]   ; 100000c <__*ABS*0x12346570_veneer+0x4>
 100000c:   12346570    data: #0x12345670

If the immediate value is close enough to PC that the offset will fit in the immediate field, then the verneer (trampoline) is skipped and you will get a single branch instruction to the specified constant address.

[1] using the codesorcery (2009q1) toolchain with: arm-none-eabi-gcc -march=armv7-a -x assembler test.spp -o test.elf -Ttext=0x1000000 -nostdlib

David Mirabito
  • 455
  • 5
  • 13