Memory Layout of C Programs
I) What is memory managerment? Memory management is the process of controlling and coordinating computer memory, assigning portions called blocks to various running programs to optimize overall system performance. Memory management resides in hardware, in the OS (operating system), and in ...
I) What is memory managerment?
Memory management is the process of controlling and coordinating computer memory, assigning portions called blocks to various running programs to optimize overall system performance. Memory management resides in hardware, in the OS (operating system), and in programs and applications.
In hardware, memory management involves components that physically store data, such as RAM (random access memory) chips, memory caches, and flash-based SSDs (solid-state drives). In the OS, memory management involves the allocation (and constant reallocation) of specific memory blocks to individual programs as user demands change. At the application level, memory management ensures the availability of adequate memory for the objects and data structures of each running program at all times. Application memory management combines two related tasks, known as allocation and recycling.
- When the program requests a block of memory, a part of the memory manager called the allocator assigns that block to the program.
- When a program no longer needs the data in previously allocated memory blocks, those blocks become available for reassignment. This task can be done manually (by the programmer) or automatically (by the memory manager).
II- Complier driver
-
That invokes the language preprocessor, compiler, assembler, and linker, as needed on behalf of the user
-
Can generate three types of object files depending upon the options supplied to the compiler driver. Technically an object file is a sequence of bytes stored on disk in a file. These object files are as follows:
- Relocatable object file : These are static library files. Static linkers such as the Unix ld program take collection of relocatable object files and command line arguments as input and generate a fully linked executable object file as output that can be loaded into memory and run. Relocatable object files contain binary code and data in a form that can be combined with other relocatable object files at compile time to create an executable object file
- Executable object file: These are executable files contain binary code and data in a form that can be copied directly into memory and executed.
- Shared object file: These special type of relocatable object files are loaded into memory and linked dynamically, at either load time or run time.
III- Code and Data segments
When we run a program, its executable image is loaded into memory area that normally called a process address space in an organized manner. It is organized into following areas of memory, called segments:
- Text Or Code segment
- Initialized Data Segments
- Uninitialized Data Segments
- Stack Segment
- Heap Segment
1. Text Or Code segments
- contains machine code of complied program. The text segments of an executable object file is often read-only segment that prevents a program from being accidentally modified.
- This is the area where the compiled code of the program itself resides. This is the machine language representation of the program steps to be carried out, including all functions making up the program, both user defined and system.
- The portion of the executable file containing the text segment is the text section.
2. Data segments
- Stores program data. This data could be initialized or uninitialized variables and it could be local or global variables.
- Data segment is further into 4 sub-data segments
- Initialized data segment
- Uninitialized data segment
- Stack
- Heap
2.1. initialized data
- Stores all global, static, constant, external variables that are initialized with non-zero values.
- Each process running the same program has its own data segment. This segment can be further classified into initialized read-only area and initialized read-write area
2.2. Uninitialized data - bss(block storage start) segment
- Uninitialized data starts at the end of the data segment
- Stores all uninitialized global, static, and external variables (declared with extern keyword). Global, external, and static variable are by default initialized to zero.
- This section occupies no actual space in the object file
- Each process running the same program has its own BSS area.
- When running, the BSS data are placed in the data segment.
2.3. Heap segment
- It is part of RAM where dynamically allocated variables are stored.
- In C program dynamic memory allocation is done by using malloc / calloc functions.
- When some more memory need to be allocated using malloc and calloc function, heap grows upward.
- Free memory (free or delete) goes back to the heap. Heap memory doesn't returned in the same order in which it was acquired.
- The end of the heap is marked by a pointer known as the "break". Your programs will "break" if they reference past the break.
2.4. Stack segment
- Used to stores all local variables.
- Used for passing arguments to the functions along with the return address of the instruction which is to be executed after the function call is over.
- Local variables have a scope to the block which they are defined in; they are created when control enters into the block. Local variables do not appear in data or bss segment. Also all recursive function calls are added to stack. Data is added or removed in a last-in-first-out manner to stack.
Example.
int main() { return 0; }
$$gcc test.c
$$size a.out
text data bss dec hex filename 836 260 8 1104 450 a.out
//2. Now add a global variable as shown below
int global; /* Uninitialized variable stored in bss*/ int main() { return 0; }
$$gcc test.c
$$size a.out
text data bss dec hex filename 836 260 12 1108 454 a.out
As you can see BSS is incremented by 4 bytes.
//3. Let us include a static variable as shown below.
int global; /* Uninitialized variable stored in bss*/ int main() { static int i; /* Uninitialized static variable stored in bss */ return 0; }
$$gcc test.c
$$size a.out
text data bss dec hex filename 836 260 16 1112 458 a.out
As you can see BSS is incremented by 4 bytes.
//4. Now let us initialize the static variable so that it is saved in the initialized data segment.
int global; /* Uninitialized variable stored in bss*/ int main() { static int i = 10; /* Initialized static variable stored in DS*/ return 0; }
$$gcc test.c
$$size a.out
text data bss dec hex filename 836 264 12 1112 458 a.out
Now you can see that data section is incremented by 4 bytes and BSS is decremented by 4 bytes.
//5. Let us even initialize the Global variable which makes it part of Data Segment.
int global = 10; /* Initialized variable stored in DS*/ int main() { static int i = 10; /* Initialized static variable stored in DS*/ return 0; }
$$gcc test.c
$$size a.out
text data bss dec hex filename 836 268 8 1112 458 a.out
We can see that data section is incremented by 4 bytes and BSS is decremented by 4 bytes.
//6. Let us see what happens when we add a const variable.
const int i = 1; int global=10; int main() { static int i=10; return 0; }
$$gcc test.c
$$size a.out
text data bss dec hex filename 840 268 8 1116 45c a.out
Now we can see that TEXT segment is incremented by 4 bytes.