Relocation in assembly

会有一股神秘感。 提交于 2019-12-17 14:13:32

问题


I have a boot-up code for a bare-metal ARM written in assembly and I'm trying to understand how it works. The binary is written in some external Flash, and is copying parts of itself in RAM at boot-up. I still didn't exactly get the concept of relocation in this context, even though I read this wikipedia entry. The RAM is mapped to a low address window, and the flash in a high address window. Can someone explain to me why we test the value of the link register here?

/* Test if we are running from an address, we are not linked at */
       bl check_position
 check_position:
        mov     r0, lr                  
        ldr     r1, =check_position
        cmp     r0, r1                  /* ; don't relocate during debug */
        beq     relocated_entry 

回答1:


My guess is the application runs from ram, and when debugging the application this author is perhaps using some sort of bootloader and or jtag to load the test app directly into ram, thus no reason to copy and run (which could cause a crash).

Another reason you would do something like this is to avoid an infinite loop. If for example you want to boot from flash (have to usually) but execute from ram, the simplest way to do that is to just copy the whole flash or whole some chunk of flash to ram and just branch to the start of ram. Which when you do that means you hit the "copy the app to ram and branch" loop again, to avoid it the second time (which might crash you), you have some sort of am I running this loop from flash or not test.




回答2:


Can anyone explain to me why we test the value of the link register here?

The bl check_position will place the value of PC+4 in the link register and transfer control to the check_position also PC relative.bl at ARM So far everything is PC relative.

The ldr r1,=check_position gets a value from the literal pool.Ref1 The actual code looks like,

  ldr r1,[pc, #offset]
...
  offset:
    .long check_position   # absolute address from assemble/link. 

So the R0 contains a PC relative version and the R1 contains the absolute assembled version. Here, they are compared. You could also use arithmetic to calculate the difference and then branch to it if non-zero; or possibly copy the code to it's absolute destination.Ref2 If the code is running at the linked address, then R0 and R1 are the same. This is some pseudo code for bl.

 mov lr,pc               ; pc is actually two instruction ahead.
 add pc,pc,#branch_offset-8

The key is that BL does everything based on the PC including the update of lr. Instead of using this trick, we could use mov R0,PC, except the PC is 8 bytes ahead. Another alternative would be to use adr R0,check_position, which would get the assembler to do all the address math for us.

 /* Test if we are running from an address, we are not linked at */
 check_position:
    adr    r0, check_position
    ldr    r1, =check_position
    cmp    r0, r1                  /* ; don't relocate during debug */
    beq    relocated_entry 

Ref1: See Arm op-codes and .ltorg in gnu-assembler manual
Ref2: This is exactly what the Linux head.S is doing for the ARM.

Edit: I checked the ARM ARM and the PC is apparently the current instruction +8, which goes to show why the code was like this. I think the adr version is more direct and readable, but the adr pseudo-op is not used as often so people may not be familiar with it.



来源:https://stackoverflow.com/questions/15671717/relocation-in-assembly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!