Thursday, March 27, 2014

How to get to know the memory address at which my code/data is actually loaded at boot initialization stage?

In embedded software development, sometimes when you're writing a PIC (position independent code) boot code, you might need to know at which memory address my bootstrap code/data is loaded by a loader at runtime. This article discusses the solutions to this question with assembly language implementation (x86). 

In x86_64 environment, it is very easy because the x86-64 architecture adds an RIP (instruction pointer relative) addressing. This addressing mode is specified by using register `rip' as a base register and plus/minus an offset (Only constant offsets are valid). For example below, you want to get the address of stack, you can do it like this (with AT&T assembly format).

lea  stackbase(%rip), %rax  //after executing this instruction, 
                            //the rax hold the absolute memory 
                            //address of stack memory below. 

mov  %rax, %rsp             //switch to my new stack.

.align 8
    .fill 4096, 1, 0xCC

However, in a x86 32bit environment, we cannot use the same way because RIP-relative address mode is not supported by architecture. We can firstly calculate the delta between where we were compiled to run at and where we were actually loaded at. Fortunately this can only be done with a short local CALL (near call), nothing else will tell us what address we are running at.

This is because that when a CALL is executed, the processor will automatically save the return address (absolute EIP address) into the stack before branching to the target address. Hence, in order to do this, we need a memory space (any location with 4-byte available) assisted in. See the code below, for more details, you can refer to the Linux boot stage code head_32.S.

    leal (%esi), %esp  //set up a tmp stack with a 
                       //4-byte memory pointed by esi.
    call 1f            //nearly call to the next instruction. 
                       //the processor will push EIP 
                       //(here it is happened to be the next 
    popl %ebp          //pop the EIP value to ebp register. 
    subl $1b, %ebp     //after this, ebp is just the DELTA between 
                       //where we were compiled to run at (label 1)
                       //and where we were actually loaded at (ebp)
    //now with this DELTA, we can get any absolute 
    //memory address of our code/data.                       
<The End>

No comments:

Post a Comment