How Convert Address in Elf to Physical Address

Calculate the entry point of an ELF file as a physical address (offset from 0)

My question is: what is the actual "formula" of how exactly you get the entry point address of the _start procedure as an offset from byte 0?

First, forget about sections. Only segments matter at runtime.

Second, use readelf -Wl to look at segments. They tell you exactly which chunk of file ([.p_offset, .p_offset + .p_filesz)) goes into which in-memory region ([.p_vaddr, .p_vaddr + .p_memsz)).

The exact calculation of "at which offset in the file does _start reside" is:

  1. Find Elf32_Phdr which "covers" the address contained in Elf32_Ehdr.e_entry.
  2. Using that phdr, file offset of _start is: ehdr->e_entry - phdr->p_vaddr + phdr->p_offset.

Update:

So, am I always looking for the 1st program header?

No.

Also by "covers" you mean that the 1st phdr->p_vaddr is always equal to e_entry?

No.

You are looking for a the program header (describing relationship between in-memory and on-file data) which overlaps the ehdr->e_entry in memory. That is, you are looking for the segment for which phdr->p_vaddr <= ehdr->e_entry && ehdr->e_entry < phdr->p_vaddr + phdr->p_memsz. This segment is often the first, but that is in no way guaranteed. See also this answer.

Why can I get the elf entry without the help of a base address?

The linker, as a rule, doesn't really care where in memory it places anything. It's primary job is to make sure that all memory references are consistent, no matter how memory is laid out. The purpose of a linker script is to tell the linker how to lay out the memory. If you don't provide a linker script, it will use its own defaults. In other words, the linker doesn't know or care whether you have something already loaded at 0x4000. It's your job to know how your memory is laid out, and to provide a linker script if you want it laid out in a specific way.

As for the -no-pie bit of the question, that comes down to how position-independent and non-position-independent executables are loaded. Your UEFI bootloader is, among other things, a loader. There is a flag in the executable telling the loader whether or not it's a PIE. If it's not, then the loader just has to use the exact addresses encoded in the file. In this case, the elf->entry pointer will be exactly correct. If it is a PIE, then the loader can place it at whatever memory address it likes, in which case the elf->entry pointer will be relative to the address at which the executable is loaded. That's why you need to use base + elf->entry when you don't provide the -no-pie flag.

Is changing default virtual address in elf header to 0 possible?

Can I change the default virtual address(ph_vaddr) in the elf to 0x0.

Yes, that is in fact how PIE (position independent) executables are usually linked.

echo "int main() { return 0; }" | gcc -xc - -fPIE -pie -o a.out
readelf -l a.out | grep LOAD | head -1

LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000

Note: above makes an executable that is of type ET_DYN.

will this allow access to null pointer?

No. When the kernel discovers that the .e_type == ET_DYN for the executable, it will relocate all of its segments elsewhere.

You can also make an executable of type ET_EXEC with .p_vaddr == 0, like so:

echo "int main() { return 0; }" | gcc -xc - -o a.out -Wl,-Ttext=0
readelf -l a.out | grep LOAD | head -1
LOAD 0x0000000000200000 0x0000000000000000 0x0000000000000000

The kernel will refuse to run it:

./a.out
Killed


Related Topics



Leave a reply



Submit