.text section address range of position independent executable - c

I want the address of the .text section of a position independent executable. Using readelf -S:
Name Type Address Offset
Size EntSize Flags Link Info Align
.text PROGBITS 0000000000002700 00002700
0000000000001672 0000000000000000 AX 0 0 16
I learn that it will begin 0x2700 bytes past where library was loaded into memory. But how can I get the load address of the executable?
Is there any other way to get the .text section address range during runtime (from the running program)?

Is there any other way to get the .text section address range during runtime (from the running program)?
Yes: you need to use dl_iterate_phdr and use info->dlpi_addr to locate the PIE binary in memory at runtime. The very first call to your callback will be for the main executable.

Related

Linker Inserts Unnecessary Opcode Padding

I've recently come across a minor issue when linking multiple object files for a Motorola 68000 based system (SEGA Mega Drive). The problem is, when an input section for one object file ends and the next one begins, the linker fills memory addresses with zeros so that the next object file begins aligned on a four byte boundary. The text below is a memory map output by the linker. As you can see, the .text output section contains three object files. The first two (main.o, swap.o), were written in C compiled and assembled using m68k-elf-gcc. The third one (swap_asm.o) was hand written in 68000 assembly and assembled using the vasm. The function at the beginning of swap.o would normally start at address 0x0000001E. But, the linker is *fill*ing the beginning of the swap.o file with two bytes, specifically 0x0000. So, swap.o starts at 0x00000020. But, swap_asm.o is not getting aligned and begins at a non-four-byte-aligned address, 0x00000036. Is there a way to make the linker not add any padding and just start the swap.o right away? I understand there are a few work arounds like filling the space with a NOP, but I was wondering if there is a way to just not do a *fill*?
.text 0x00000000 0x4c
main.o(.text)
.text 0x00000000 0x1e main.o
0x00000000 main
swap.o(.text)
*fill* 0x0000001e 0x2
.text 0x00000020 0x16 swap.o
0x00000020 swap
swap_asm.o(.text)
.text 0x00000036 0x16 swap_asm.o
0x00000036 swap_asm
The 68000 processor requires instructions to be aligned (and this requirement holds also for data). Despite of the CPU requirements (which are unskipable) the linker also uses a script in which the segments are required to have some alignment (normally to provide for this cpu requirements)
While the linker script can be tweakable, It can be the case that changing the alignment makes the linker to produce incorrect code (because of what is said in the above paragraph) but anycase, that's something you can try and test.
Motorola 68000 (and more the 16 bit version of the MegaDrive) triggers a bus error trap when a 16bit transfer is requested on an odd address. The same happens if a 32bit (but this happens also up to the 68030, the 68040 I think already handles this making several bus accesses, like the Intel processors)
So I found my answer. When the assembler detects long (32-bits) data is being dealt with in an assembly file, it automatically aligns the input section along a 4 byte boundary. You can actually override this using SUBALIGN in a linker script. Here's my linker script aligning input sections along a 2 byte boundary.
MEMORY
{
rom : ORIGIN = 0x00000000, LENGTH = 0x00400000
}
SECTIONS
{
.text : SUBALIGN(0x2) {
*(.header)
*(.boot)
obj/main.o(.text)
*(.text)
*(.isr)
*(.vdp)
} > rom
.data : { *(.data) } > rom
.bss : { *(.bss) } > rom
}
New linker map:
.text 0x00000000 0x4a
main.o(.text)
.text 0x00000000 0x1e main.o
0x00000000 main
swap.o(.text)
.text 0x0000001e 0x14 swap.o
0x0000001e swap
swap_asm.o(.text)
.text 0x00000034 0x16 swap_asm.o
0x00000034 swap_asm

How is text segment made read-only?

I know that text segment is a read-only segment and trying to write to it results in "Bus error".
I am curious about how this segment is made read-only.
Since the physical memory is not readonly, this must be done during paging.
Is there a bit for each page of memory for read-only pages which is set for text segment?
An ELF file (a Unix executable or a shared-object) has two main concepts:
Section: An area inside the executable file with a specific role. There may be different sections inside an ELF file (can be seen in man elf). Common sections in an ELF file are:
.text (SHT_PROGBITS): The actual executable code in the ELF file.
.dynsym (SHT_DYNSYM): Holds information about symbols that should be dynamically retrieved.
.rela.dyn and .rela.plt (SHT_RELA): Hold relocation information for the dynamic linker to use when loading the ELF file into memory.
.dynamic (SHT_DYNAMIC): Holds information for the dynamic linker, such as other dependencies, offsets for different sections in run-time, etc.
.symtab (SHT_SYMTAB): Holds a symbol table.
.strtab (SHT_STRTAB): Holds a string table.
There are more sections and the above are just a few common ones.
Using readelf one can see all the sections in an ELF file:
readelf --sections -W <file>
Running this command on a shared-object in my computer results in the following output (simplified):
There are 29 section headers, starting at offset 0x1898:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
...
[ 3] .dynsym DYNSYM 0000000000000230 000230 000168 18 A 4 2 8
[ 4] .dynstr STRTAB 0000000000000398 000398 0000b0 00 A 0 0 1
...
[ 7] .rela.dyn RELA 0000000000000488 000488 0000c0 18 A 3 0 8
[ 8] .rela.plt RELA 0000000000000548 000548 000030 18 AI 3 22 8
...
[12] .text PROGBITS 00000000000005e0 0005e0 000121 00 AX 0 0 16
...
[20] .dynamic DYNAMIC 0000000000200e18 000e18 0001c0 10 WA 4 0 8
...
[23] .data PROGBITS 0000000000201028 001028 000008 00 WA 0 0 8
...
[27] .symtab SYMTAB 0000000000000000 001068 000570 18 28 45 8
[28] .strtab STRTAB 0000000000000000 0015d8 0001c6 00 0 0 1
Segment: An area inside the executable with load instructions for the dynamic linker. Meaning, a segment is just an area inside the ELF file that should be loaded into a preferred address memory, and have specific permissions, alignment, etc.
Every section (which is an area in the ELF file with a logical role) should be part of a segment, with the correct permissions and characteristics. A segment can have more than a single section within, and a section is inside a single segment (one-to-many relation).
Using readelf one can see all segments in an ELF file:
readelf --segments -W <file>
Running this command on a shared-object in my computer results in the following output:
There are 7 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x00079c 0x00079c R E 0x200000
LOAD 0x000e00 0x0000000000200e00 0x0000000000200e00 0x000230 0x000238 RW 0x200000
DYNAMIC 0x000e18 0x0000000000200e18 0x0000000000200e18 0x0001c0 0x0001c0 RW 0x8
NOTE 0x0001c8 0x00000000000001c8 0x00000000000001c8 0x000024 0x000024 R 0x4
GNU_EH_FRAME 0x000718 0x0000000000000718 0x0000000000000718 0x00001c 0x00001c R 0x4
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
GNU_RELRO 0x000e00 0x0000000000200e00 0x0000000000200e00 0x000200 0x000200 R 0x1
Section to Segment mapping:
Segment Sections...
00 .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame
01 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
02 .dynamic
03 .note.gnu.build-id
04 .eh_frame_hdr
05
06 .init_array .fini_array .jcr .dynamic .got
Here, we can see that all sections related to the executable code, as well as many sections related to the dynamic loading of the file are in segment 00 (PT_LOAD), which has read and executable permissions (R E). Sections that should be modified by the loader are in segment 01 (PT_LOAD) which has read and write permissions (RW). Segment 02 is of type PT_DYNAMIC and holds dynamic linking information - the .dynamic section.
The dynamic linker takes all this information into account when it loads the ELF file into memory. It loads the different segments of the ELF file from disk into memory and protects their pages with the correct permissions. Then, it iterates through the different sections and uses them according to their roles (relocations, resolving dynamic symbols, etc...).
The memory protection itself is being made by the OS and the hardware itself. It is similar to using the Linux methods mprotect(). More information about memory protection can be found here.
Generally, one way this happens is the program loader allocates memory for the program text, reads program text into it from the executable file, and asks the operating system to make that memory read-only. The operating system records this in its databases and conveys the information to the hardware to tell it not to allow writes to that memory.
Another possibility is that the loader does not actually read the program text into memory but merely issues a request to the operating system to mark that the appropriate portion of the executable file is “mapped” into memory, as read-only. When the process actually tries to execute the code in a memory page, the operating system will read it into memory (with write access for itself, so it can do this) and mark it read-only (for the process).
This latter case is an optimization because it allows your program to start without loading all of it first. And parts of it that are never used, such as code to handle rare errors, might never be loaded.
It doesn't have to be. You can make the text segment of a program read write. But you have to instruct the linker about that. The operating system generates pages that generate a page fault when you try to write on them, but not when you try to read. This is part of the multiuser protection system (also called the memory protection system).
The reason of the text segment being read only is a legacy from ancient time. The multiple instances of a single program called by different processes (e.g. different users doing a ls to show their own directory contents) use the same segment as text, so why not share that segment and avoid to load it each time for each instance? That's the reason, but if you want to modify your program's text segment at runtime, you can designate it to be COW ((C)opy (o)n (w)rite) and the pages will be individually copied when you try to write on them, so you have the illussion it is read/write.

Why is the ELF header loaded into memory along with the text segment?

I compiled this program with -m32 -nostdlib into an ELF executable:
void _start() {}
And when I did readelf -l I was surprised to see that the offset on the LOAD segment was 0x000000, since that would mean that the executable header would get loaded into memory at the same time as the text segment. So I checked with GDB, and that is indeed the case:
(gdb) b _start
Breakpoint 1 at 0x8048083
(gdb) r
Starting program: /home/tbodt/ish/build/a.out
Breakpoint 1, 0x08048083 in _start ()
(gdb) x/4c 0x08048000
0x8048000: 127 '\177' 69 'E' 76 'L' 70 'F'
Why is this useful?
I was surprised to see that the offset on the LOAD segment was 0x000000
Why were you surprised?
since that would mean that the executable header would get loaded into memory at the same time as the text segment.
Correct. Why is that a problem?
As this answer explains, the executable is mmaped and mmap works on entire pages; you can't mmap part of the page starting at offset 0x34.
You could build an executable in which .text starts at offset 4096 (leaving a large hole between the ELF header and program headers and the text), and then such executable can have the first PT_LOAD segment with offset 4096. This isn't commonly done: the wasted space in the file usually is not worth saving 52 bytes of memory.

How to read the relocation records of an object file

I'm trying to understand the linking stage of C toolchain. I wrote a sample program and dissected the resulting object file. While this helped me to get a better understanding of the processes involved, there are some things which remain unclear to me.
Here are:
My (blazingly simple) sample program
Relevant parts of the object disassembly
The objects symbol table
The objects relocation table
Part 1: Handling of initialized variables.
Is it correct, that theses relocation table entries...
RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
0000002b dir32 .data
00000035 dir32 .data
0000003f dir32 .data
... are basically telling the linker, that the addresses stored at offset 2b, 35 and 3f from .text are not absolute adresses, but relative adresses (= offsets) in relation to .data? It is my understanding that this enables the linker to
either convert these relative adresses to absolute adresses for creation of a non-relocatable object file,
or just adjust them accordingly in case the object file gets linked with some other object file.
Part 2: Handling of uninitialized variables.
I don't understand why uninitalized variables are handled so differently to initialized variables. Why are the register adresses stored in the opcode,
equal for all the uninitialized variables (0x0, 0x0 and 0x0), while being
different for all the initialized variables (0x0, 0x4 and 0x8)?
Also the value field of their relocation table entries is entirely unclear to me. I would have expected the .bss section to be referenced there.
RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
0000000d dir32 _var1_zeroed-0x00000004
00000017 dir32 _var2_zeroed-0x00000004
00000021 dir32 _var3_zeroed-0x00000004
... are basically telling the linker, that the addresses stored at offset ...
No, the linker is no longer involved with this. The relocation tables tell the loader, the part of the operating system that's responsible for loading the executable image into memory about the addresses.
The linker builds the executable image based on the assumption that everything is ideal and the image can be loaded at the intended address. If that's the case then everything is hunky-dory, nothing needs to be done. If there's a conflict however, the virtual address space is already in use by something else, then the image needs to be relocated at a different address.
That requires addresses to be patched, the offset between the ideal and the actual load address needs to be added. So if the .data section ends up at another address then addresses .text+0x2b, .text+0x35, etcetera, must be changed. No different for the uninitialized variables, the linker already picked an address for them but when _var1_zeroed-0x00000004 ends up at another address then .text+0x0d, .text+0x17, etcetera, need to be changed.

Why is the entry point address in my executable 0x8048330? (0x330 being the offset of .text section)

I wrote a small program to add two integers and on using readelf -a executable_name it showed the entry point address in elf header as:
Entry point address: 0x8048330
How does my executable know this address beforehand even before loader loads it in memory?
elf_format.pdf says this member gives the virtual address to which the system first transfers control, thus starting the process. Can anyone please explain what is the meaning of this statement and what is the meaning of virtual address here?
Also let me know, from where the executable file gets the value of 0x8048330 as entry point address. Just for cross check I compiled another program and for that also, the entry point address remains the same value 0x8048330 (offset of .text section being 0x330 in both the cases).
For first question:
the entry point you saw, 0x8048330, is a virtual memory address (in the opposite, is physical memory).
This means your executive doesn't have to know what physical address to map. (after it loads with a loader)
It doesn't even have the access to the physical memory.
To the process of your program, your .text section always starts from 0x8048330, your system (OS and hardware) will then map it (the virtual address) to the physical memory at run-time.
mapping and managing physical memory is a lot of things, you can check on Google for more information.
For the second question
I'm not sure which part confused you so I'll try to cover them all:
Could more than one program have same entry point?
Yes, there could be another program with the same entry point 0x8048330. because this address is virtual, the programs will be mapped to different physical memory at run-time when you try to run them at the same time.
Does the entry always 0x8048330?
Well, Linux executives are start from 0x8048000, but the offset of .text section is related to other sections length. So no, it could be 0x8048034 or anything else.
Why it always start from 0x8048000?
I think it's kind of history thing, the designer of Linux picked this one for some unknown or even random reason. you can refer this thread to see what under that area.
The entry address is set by the link editor, at the time when it creates the
executable. The loader maps the program file at the address(es) specified
by the ELF headers before transferring control to the entry address.
To use a concrete example, consider the following:
% file a.out
a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, \
for GNU/Linux 2.6.15, not stripped
% readelf -e a.out
... snip ...
Elf file type is EXEC (Executable file)
Entry point 0x8048170
There are 6 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x08048000 0x08048000 0x7cca6 0x7cca6 R E 0x1000
LOAD 0x07cf98 0x080c5f98 0x080c5f98 0x00788 0x022fc RW 0x1000
... snip ...
The first program header specifies that the contents of the file at
file offset 0 should be mapped to virtual address 0x08048000. The
file and memory sizes for this segment are 0x7cca6 bytes. This
segment is to be mapped in readable and executable but not writable
(it contains the program's code).
The entry point address specified in the ELF header is 0x8048170, which
falls inside the region containing program code.
The book "Linkers and Loaders" by John Levine is a good resource to consult on matters related to link editors and loaders.
About the virtual address question:
Normal userland applications work with virtual addresses which means they don't access directly the memory space. The OS (with the help of some microprocessor's special functions) maps this virtual addresses to physical addresses.
This way, the OS prevents applications from reading/writing into other applications memory or OS reserved memory. Also, this allows the paging of memory (use hard disk as memory) in a transparent way for the application.

Resources