Why is there no stack segment in an object (ELF) files? - c

I am beginner of OS. This question is given by my professor and confused me a lot. I hope I can get some hints and helps here.
In my memory, ELF file can link each other. Is that a reason that ELF file cannot have stack segment?
Thanks a lot for any helps!

This is because stack is not something need to be saved in a file format, it is completely related to runtime execution. Just like you don't need to have a "heap segment" in your file.
On the other hand, stack belongs to an executing thread, not data or functions, and it doesn't has a fixed size. Consider a recursive function:
int foo() {
printf("Stack Overflow!\n");
return foo();
}
every recursion has its own frame in stack, there is no stack belong to foo() itself, only for its execution.
Of course you can reserve a stack segment in your file, just as a big block of static memory, and let the %rsp pointer(x64) pointing to it. But OS has already done for you, so it's not necessary.

Most GNU/Linux ELF programs do have stack segments because on all but very recent architectures, a stack segment in the program header is used to mark the stack as non-executable (which is a form of security hardening).
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001f8 0x00000000000001f8 R E 0x8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000ffd0c 0x00000000000ffd0c R E 0x200000
LOAD 0x0000000000100548 0x0000000000700548 0x0000000000700548
0x000000000000b6fc 0x0000000000015440 RW 0x200000
…
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x0000000000100548 0x0000000000700548 0x0000000000700548
0x0000000000002ab8 0x0000000000002ab8 R 0x1
On certain GNU/Linux architectures (FDPIC), the size of the stack segment is even used by the kernel to set the stack size of the main thread.
(There are different kinds of program segments. Not all of them load bits from the file image.)

Related

Why are instructions addresses on the top of the memory user space contrary to the linux process memory layout?

#include <stdio.h>
void func() {}
int main() {
printf("%p", &func);
return 0;
}
This program outputted 0x55c4cda9464a
Supposing that func will be stored in the .text section, and according to this figure, from CS:APP:
I suppose that the address of func would be somewhere near the starting address of the .text section, but this address is somewhere in the middle. Why is this the case? Local variables stored on the stack have addresses near 2^48 - 1, but I tried to disassemble different C codes and the instructions were always located somewhere around that 0x55... address.
gcc, when configured with --enable-default-pie1 (which is the default), produces Position Independent Executables(PIE). Which means the load address isn't same as what linker specified at compile-time (0x400000 for x86_64). This is a security mechanism so that Address Space Layout Randomization (ASLR) 2 can be enabled. That is, gcc compiles with -pie option by default.
If you compile with -no-pie option (gcc -no-pie file.c), then you can see the address of func is closer to 0x400000.
On my system, I get:
$ gcc -no-pie t.c
$ ./a.out
0x401132
You can also check the load address with readelf:
$ readelf -Wl a.out | grep LOAD
LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x000478 0x000478 R 0x1000
LOAD 0x001000 0x0000000000401000 0x0000000000401000 0x0001f5 0x0001f5 R E 0x1000
LOAD 0x002000 0x0000000000402000 0x0000000000402000 0x000158 0x000158 R 0x1000
LOAD 0x002e10 0x0000000000403e10 0x0000000000403e10 0x000228 0x000230 RW 0x1000
1 you can check this with gcc --verbose.
2 You may also notice that address printed by your program is different in each run. That's because of ASLR. You can disable it with:
$ echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
ASLR is enabled on Linux by default.

Where in object file does the code of function "main" starts?

I have an object file of a C program which prints hello world, just for the question.
I am trying to understand using readelf utility or gdb or hexedit(I can't figure which tool is a correct one) where in the file does the code of function "main" starts.
I know using readelf that symbol _start & main occurs and the address where it is mapped in a virtual memory. Moreover, I also know what the size of .text section and the of coruse where entry point specified, i.e the address which the same of text section.
The question is - Where in the file does the code of function "main" starts? I tought that is the entry point and the offset of the text section but how I understand it the sections data, bss, rodata should be ran before main and it appears after section text in readelf.
Also I tought we should sum the size all the lines till main in symbol table, but I am not sure at all if it is correct.
Additional question which follow up this one is if I want to replace main function with NOP instrcutres or plant one ret instruction in my object file. how can I know the offset where I can do it using hexedit.
So, let's go through it step by step.
Start with this C file:
#include <stdio.h>
void printit()
{
puts("Hello world!");
}
int main(void)
{
printit();
return 0;
}
As the comments look like you are on x86, compile it as 32-bit non-PIE executable like this:
$ gcc -m32 -no-pie -o test test.c
The -m32 option is needed, because I am working at a x86-64 machine. As you already know, you can get the virtual memory address of main using readelf, objdump or nm, for example like this:
$ nm test | grep -w main
0804918d T main
Obviously, 804918d can not be an offset in the file that is just 15 kB big. You need to find the mapping between virtual memory addresses and file offsets. In a typical ELF file, the mapping is included twice. Once in a detailed form for linkers (as object files are also ELF files) and debuggers, and a second time in a condensed form that is used by the kernel for loading programs. The detailed form is the list of sections, consisting of section headers, and you can view it like this (the output is shortened a bit, to make the answer more readable):
$ readelf --section-headers test
There are 29 section headers, starting at offset 0x3748:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[...]
[11] .init PROGBITS 08049000 001000 000020 00 AX 0 0 4
[12] .plt PROGBITS 08049020 001020 000030 04 AX 0 0 16
[13] .text PROGBITS 08049050 001050 0001c1 00 AX 0 0 16
[14] .fini PROGBITS 08049214 001214 000014 00 AX 0 0 4
[15] .rodata PROGBITS 0804a000 002000 000015 00 A 0 0 4
[...]
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
p (processor specific)
Here you find that the .text section starts at (virtual) address 08049050 and has a size of 1c1 bytes, so it ends at address 08049211. The address of main, 804918d is in this range, so you know main is a member of the text section. If you subtract the base of the text section from the address of main, you find that main is 13d bytes into the text section. The section listing also contains the file offset where the data for the text section starts. It's 1050, so the first byte of main is at offset 0x1050 + 0x13d == 0x118d.
You can do the same calculation using program headers:
$ readelf --program-headers test
[...]
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x08048034 0x08048034 0x00160 0x00160 R 0x4
INTERP 0x000194 0x08048194 0x08048194 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
LOAD 0x000000 0x08048000 0x08048000 0x002e8 0x002e8 R 0x1000
LOAD 0x001000 0x08049000 0x08049000 0x00228 0x00228 R E 0x1000
LOAD 0x002000 0x0804a000 0x0804a000 0x0019c 0x0019c R 0x1000
LOAD 0x002f0c 0x0804bf0c 0x0804bf0c 0x00110 0x00114 RW 0x1000
[...]
The second load line tells you that the area 08049000 (VirtAddr) to 08049228 (VirtAddr + MemSiz) is readable and executable, and loaded from offset 1000 in the file. So again you can calculate that the address of main is 18d bytes into this load area, so it has to reside at offset 0x118d inside the executable. Let's test that:
$ ./test
Hello world!
$ echo -ne '\xc3' | dd of=test conv=notrunc bs=1 count=1 seek=$((0x118d))
1+0 records in
1+0 records out
1 byte copied, 0.0116672 s, 0.1 kB/s
$ ./test
$
Overwriting the first byte of main with 0xc3, the opcode for return (near) on x86, causes the program to not output anything anymore.
_start normally belongs to a module ( a *.o file) that is fixed (it is called differently on different systems, but a common name is crt0.o which is written in assembler.) That fixed code prepares the stack (normally the arguments and the environment are stored in the initial stack segment by the execve(2) system call) the mission of crt0.s is to prepare the initial C stack frame and call main(). Once main() ends, it is responsible of getting the return value from main and calling all the atexit() handlers to finish calling the _exit(2) system call.
The linking of crt0.o is normally transparent due to the fact that you always call the compiler to do the linking itself, so you normally don't have to add crt0.o as the first object module, but the compiler knows (lately, all this stuff has grown considerably, since we depend on architecture and ABIs to pass parameters between functions)
If you execute the compiler with the -v option, you'll get the exact command line it uses to call the linker and you'll get the secrets of the final memory map your program has on its first stages.

Finding the rendezvous (struct r_debug) structure of a process?

I'm trying to access the "rendezvous structure" (struct r_debug *)
in order to find the link map of a process.
But I keep running into invalid adresses and I really can't figure out
what's going on.
Here's how I go on trying to find it:
1. Get the AT_PHDR value from the auxiliary vector
2. Go through the program headers until I find the PT_DYNAMIC segment
3. Try to access the vaddr of that segment (PT_DYNAMIC) to get the dynamic tags
4. Iterate through the dynamic tags until I find DT_DEBUG. If I get here I should be done
The issue is I can't get past step 3 because the vaddr of the PT_DYNAMIC
segment always points to an invalid address.
What am I doing wrong ? Do I need to find the relocation of the vaddr ?
I have looked at the LLDB sources but I can't figure out how they got the address.
UPDATE: #EmployedRussian was right, I was looking at a position-independent executable.
His solution to calculate the relocation worked wonderfully.
What am I doing wrong ?
Most likely you are looking at position-independent executable. If your readelf -Wl a.out looks like this:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000040 0x0000000000000040 0x0000000000000040 0x0001f8 0x0001f8 R 0x8
INTERP 0x000238 0x0000000000000238 0x0000000000000238 0x00001c 0x00001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x016d28 0x016d28 R E 0x200000
LOAD 0x017250 0x0000000000217250 0x0000000000217250 0x0010d0 0x001290 RW 0x200000
DYNAMIC 0x017df8 0x0000000000217df8 0x0000000000217df8 0x0001e0 0x0001e0 RW 0x8
then you need to adjust Phdr_pt_dynamic.p_vaddr by the executable relocation address (the key is that the first Phdr_pt_load.p_vaddr == 0).
You can find this relocation address as the delta between AT_PHDR value in the aux vector and Phdr_pt_phdr.p_vaddr.
(Above I use Phdr_xxx as a shorthand for Phdr[j] with .p_type == xxx).
You are also doing it in much more complicated way than you have to: the address of the dynamic array is trivially available as _DYNAMIC[]. See this answer.

readelf 'Align' column unit

I have the following simple "hello world" program:
#include <stdio.h>
int main() {
printf("Hello world.\n");
return 0;
}
I compile it as
gcc -static -O0 -g -std=gnu11 -o test_helloworld test_helloworld.c -lpthread
Now, I want check its memory segment by issuing readelf -l and obtain the following output:
Elf file type is EXEC (Executable file)
Entry point 0x400890
There are 6 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000c96cf 0x00000000000c96cf R E 200000
LOAD 0x00000000000c9eb8 0x00000000006c9eb8 0x00000000006c9eb8
0x0000000000001c98 0x00000000000035b0 RW 200000
NOTE 0x0000000000000190 0x0000000000400190 0x0000000000400190
0x0000000000000044 0x0000000000000044 R 4
TLS 0x00000000000c9eb8 0x00000000006c9eb8 0x00000000006c9eb8
0x0000000000000020 0x0000000000000050 R 8
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 10
GNU_RELRO 0x00000000000c9eb8 0x00000000006c9eb8 0x00000000006c9eb8
0x0000000000000148 0x0000000000000148 R 1
I got confused by Align column: are those number in bytes or kilobytes? I thought align should be equal to the page size. Why am I getting those number?
Thanks!
This is what System V Application Binary Interface says this about p_align:
Values 0 and 1 mean no alignment is required. Otherwise, p_align should be a positive, integral power of 2, and p_vaddr should equal p_offset, modulo p_align.
So p_align doesn't need to be equal to the page size.

Static Address vs. Execution Address In An Elf Loader

I am writing an Elf Loader for ARM/ARM64. While processing the dynamic relocations I became a bit confused by some of the terms/symbols in the documentation I am following.
On Pg.14 it is stated,
"S (when used on its own) is the address of the symbol."
"P is the address of the place being relocated (derived from r_offset)."
"Delta(S) if S is a normal symbol, resolves to the difference between the static link address of S and the
execution address of S. If S is the null symbol (ELF symbol index 0), resolves to the difference between the
static link address of P and the execution address of P."
From what I gather, I believe the "execution address" of S (or P) to be the address of the symbol in the process's memory space but am unsure what is meant by "static link address".
If someone can clarify the terminology that would be great, thank you.
what is meant by "static link address".
A non-PIE executable is linked to load at a particular address. For example, on x86_64 Linux default static link address is 0x400000:
echo "int main() { return 0; }" | gcc -xc - -no-pie
readelf -Wl a.out | grep LOAD
LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x000618 0x000618 R E 0x200000
LOAD 0x000e50 0x0000000000600e50 0x0000000000600e50 0x0001d8 0x0001e0 RW 0x200000
This binary is linked with static link address of 0x400000, and symbols in it reflect that:
nm a.out | grep ' main'
0000000000400487 T main
This executable must be loaded at 0x400000, and will not work correctly if loaded anywhere else.
Note that default non-PIE static link address
is different for different architectures (i386 default is 0x8048000), and
can be changed at static link time via linker script and/or linker flags.
Contrast this with a PIE executable, which is typically linked at static link address 0:
echo "int main() { return 0; }" | gcc -xc - -fPIE -pie
readelf -Wl a.out | grep LOAD
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x0007d8 0x0007d8 R E 0x200000
LOAD 0x000e18 0x0000000000200e18 0x0000000000200e18 0x000210 0x000218 RW 0x200000
nm a.out | grep ' main'
00000000000005fa T main
So the static link address of main is 0x400487 in the non-PIE case, and 0x5fa in the PIE case.

Resources