Memory Map in
Memory Map in
Text segment
Initialized data segment
Uninitialized data segment
Stack
Heap
The size(1) command reports the sizes (in bytes) of the text, data, and bss segments. ( for
more details please refer man page of size(1) )
1. Check the following simple C program
#include <stdio.h>int main(void){return 0;}
[narendra@CentOS]$ gcc memory-layout.c -o memory-layout
[narendra@CentOS]$ size memory-layout
text
data
bss
960
248
dec
hex
filename
1216
4c0
memory-layout
2. Let us add one global variable in program, now check the size of bss (highlighted in red
color).
#include <stdio.h>int global; /* Uninitialized variable stored in bss*/int main(void){return
0;}
[narendra@CentOS]$ gcc memory-layout.c -o memory-layout
[narendra@CentOS]$ size memory-layout
text
data
bss
dec
hex
filename
960
248
12
1220
4c4 memory-layout
3. Let us add one static variable which is also stored in bss.
#include <stdio.h>int global; /* Uninitialized variable stored in bss*/int main(void){static
int i; /* Uninitialized static variable stored in bss */return 0;}
[narendra@CentOS]$ gcc memory-layout.c -o memory-layout
[narendra@CentOS]$ size memory-layout
text
data
bss
dec
hex
filename
960
248
16
1224
4c8 memory-layout
4. Let us initialize the static variable which will then be stored in Data Segment (DS)
#include <stdio.h>int global; /* Uninitialized variable stored in bss*/int main(void){static
int i = 100; /* Initialized static variable stored in DS*/return 0;}
[narendra@CentOS]$ gcc memory-layout.c -o memory-layout
data
bss
dec
hex
filename
960
252
12
1224
4c8 memory-layout
5. Let us initialize the global variable which will then be stored in Data Segment (DS)
#include <stdio.h>int global = 10; /* initialized global variable stored in DS*/int
main(void){static int i = 100; /* Initialized static variable stored in DS*/return 0;}
[narendra@CentOS]$ gcc memory-layout.c -o memory-layout
[narendra@CentOS]$ size memory-layout
text
960
1.
data
bss
dec
hex
filename
256
8
1224
4c8 memory-layout
Where are global local static extern variables stored?
Local Variables are stored in Stack. Register variables are stored in Register. Global &
static variables are stored in data segment (BSS). The memory created dynamically
are stored in Heap and the C program instructions get stored in code segment and the
extern variables also stored in data segment
2.
What does BSS Segment store?
BSS segment stores the uninitialized global and static variables and initializes them to
3.
zero. I read that BSS segment doesnt consume memory, then where those it store
these variables? You probably read that the BSS segment doesnt consume space in
the executable file on disk. When the executable loaded, the BSS segment
certainly does consume space in memory. Space is allocated and initialized to zero by
the OS loader
Global variable and Local variableGlobal variables once declared they can be used anywhere in the program i.e. even in
many functions. If possible u can use the global variables in the different user defined
header files as like in packages in java. On the other hand global variables values can
be changed programmatically local variables are local to a functional and cant be
used beyond that function.
4.
Figure w.1: Compile, link & execute stages for running program
W.2 OBJECT FILES and EXECUTABLE
After the source code has been assembled, it will produce an Object files
(e.g..o, .obj) and then linked, producing an executable files.
An object and executable come in several formats such as ELF (Executable and
Linking Format) and COFF (Common Object-File Format). For example, ELF is
used on Linux systems, while COFF is used on Windows systems.
Other object file formats are listed in the following Table.
Object
File
Format
Description
The a.out format is the original file format for Unix. It consists of three
sections: text, data, and bss, which are for program code, initialized data,
and uninitialized data, respectively. This format is so simple that it
doesnt have any reserved place for debugging information. The only
debugging format for a.out is stabs, which is encoded as a set of normal
a.out
symbols with distinctive attributes.
The COFF (Common Object File Format) format was introduced with
System V Release 3 (SVR3) Unix. COFF files may have multiple sections,
each prefixed by a header. The number of sections is limited. The COFF
specification includes support for debugging but the debugging information
COFF
was limited. There is no file extension for this format.
A variant of COFF. ECOFF is an Extended COFF originally introduced for
ECOFF
Mips and Alpha workstations.
The IBM RS/6000 running AIX uses an object file format called XCOFF
(eXtended COFF). The COFF sections, symbols, and line numbers are
used, but debugging symbols are dbx-style stabs whose strings are
located in the .debug section (rather than the string table). The default
XCOFF
name for an XCOFF executable file is a.out.
Windows 9x and NT use the PE (Portable Executable) format for
their executables. PE is basically COFF with additional headers. The
PE
extension normally .exe.
The ELF (Executable and Linking Format) format came with System V
Release 4 (SVR4) Unix. ELF is similar to COFF in being organized into a
number of sections, but it removes many of COFFs limitations. ELF used
on most modern Unix systems, including GNU/Linux, Solaris and Irix. Also
ELF
used on many embedded systems.
SOM (System Object Module) and ESOM (Extended SOM) is HPs object
SOM/ESO file and debug format (not to be confused with IBMs SOM, which is a
M
cross-language Application Binary Interface ABI).
Table w.2
When we examine the content of these object files there are areas called sections.
Sections can hold executable code, data, dynamic linking information, debugging
data, symbol tables, relocation information, comments, string tables, and notes.
Some sections are loaded into the process image and some provide information
needed in the building of a process image while still others are used only in linking
object files.
There are several sections that are common to all executable formats (may be
named differently, depending on the compiler/linker) as listed below:
Section
Description
This section contains the executable instruction codes and is shared
among every process running the same binary. This section usually has
READ and EXECUTE permissions only. This section is the one most
.text
affected by optimization.
BSS stands for Block Started by Symbol. It holds un-initialized global
and static variables. Since the BSS only holds variables that dont have
any values yet, it doesnt actually need to store the image of these
variables. The size that BSS will require at runtime is recorded in the
object file, but the BSS (unlike the data section) doesnt take up any
.bss
actual space in the object file.
.data
Contains the initialized global and static variables and their values. It is
usually the largest part of the executable. It usually has READ/WRITE
permissions.
Also known as .rodata (read-only data) section. This contains
.rdata
constants and string literals.
.reloc
Stores the information required for relocating the image while loading.
A symbol is basically a name and an address. Symbol table holds
information needed to locate and relocate a programs symbolic
definitions and references. A symbol table index is a subscript into this
array. Index 0 both designates the first entry in the table and serves as
the undefined symbol index. The symbol table contains an array of
Symbol table symbol entries.
Relocation is the process of connecting symbolic references with
symbolic definitions. For example, when a program calls a function, the
associated call instruction must transfer control to the proper destination
address at execution. Re-locatable files must have relocation entries
which are necessary because they contain information that describes
how to modify their section contents, thus allowing executable and
shared object files to hold the right information for a processs program
Relocation
image. Simply said relocation records are information used by the linker
records
to adjust section contents.
Table w.3: Segments in executable file
The following is an example of the object file content dumping using readelf
program. Other utility can be used is objdump.
/* testprog1.c */
#include <stdio.h>
static void display(int i, int *ptr);
int main(void)
{
int x = 5;
int *xptr = &x;
printf(In main() program:\n);
printf(x value is %d and is stored at address %p.\n, x, &x);
printf(xptr pointer points to address %p which holds a value of %d.\n,
xptr, *xptr);
display(x, xptr);
return 0;
}
void display(int y, int *yptr)
{
char var[7] = ABCDEF;
printf(In display() function:\n);
printf(y value is %d and is stored at address %p.\n, y, &y);
z, 4, 4
;define a constant
;allocate 4 bytes for x with;4-byte
alignment
leave
return
W.3 RELOCATION RECORDS
Because the various object files will include references to each others code and/or
data, so various locations, these shall need to be combined during the link time.
For example in Figure w.2, the object file that has main() includes calls to
functions funct() and printf().
After linking all of the object files together, the linker uses the relocation records to
find all of the addresses that need to be filled in.
W.4 SYMBOL TABLE
Since assembling to machine code removes all traces of labels from the code, the
object file format has to keep these around in different places.
It is accomplished by the symbol table that contains a list of names and their
corresponding offsets in the text and data segments.
A disassembler provides support for translating back from an object file or
executable.
Programs that are linked dynamically are linked against shared objects that have
the extension .so. An example of such an object is the shared object version of the
standard C library, libc.so.
The advantageous to defer some of the objects/modules during the static linking
step until they are finally needed (during the run time) includes:
1. Program files (on disk) become much smaller because they need not hold all
necessary text and data segments information. It is very useful for portability.
2. Standard libraries may be upgraded or patched without every one program
need to be re-linked. This clearly requires some agreed module-naming
convention that enables the dynamic linker to find the newest, installed
module such as some version specification. Furthermore the distribution of
the libraries is in binary form (no source), including dynamically linked libraries
(DLLs) and when you change your program you only have to recompile the
file that was changed.
3. Software vendors need only provide the related libraries module required.
Additional runtime linking functions allow such programs to programmaticallylink the required modules only.
4. In combination with virtual memory, dynamic linking permits two or more
processes to share read-only executable modules such as standard C
libraries. Using this technique, only one copy of a module needs be resident
in memory at any time, and multiple processes, each can executes this
shared code (read only). This results in a considerable memory saving,
although demands an efficient swapping policy.
W.6 HOW SHARED OBJECTS ARE USED
To understand how a program makes use of shared objects, lets first examine the
format of an executable and the steps that occur when the program starts.
W.6.1 SOME ELF FORMAT DETAILS
Executable and Linking Format (ELF) is binary format, which is used in SVR4 Unix
and Linux systems.
It is a format for storing programs or fragments of programs on disk, created as a
result of compiling and linking.
ELF not only simplifies the task of making shared libraries, but also enhances
dynamic loading of modules at runtime.
W.6.2 ELF SECTIONS
The Executable and Linking Format used by GNU/Linux and other operating
systems, defines a number of sections in an executable program.
These sections are used to provide instruction to the binary file and allowing
inspection. Important function sections include the Global Offset Table (GOT),
which stores addresses of system functions, the Procedure Linking
Table(PLT), which stores indirect links to the GOT, .init/.fini, for internal
initialization and shutdown, .ctors/.dtors, for constructors and destructors.
The data sections are .rodata, for read only data, .data for initialized data,
and.bss for uninitialized data.
Partial list of the ELF sections are organized as follows (from low to high):
1. .init
Startup
2. .text
String
3. .fini
Shutdown
4. .rodata
Read Only
5. .data
Initialized Data
6. .tdata
Initialized Thread Data
7. .tbss
Uninitialized Thread Data
8. .ctors
Constructors
9. .dtors
Destructors
10.
.got
Global Offset Table
11.
.bss
Uninitialized Data
You can use the readelf or objdump program against the object or executable files
in order to view the sections.
In the following Figure, two views of an ELF file are shown: the linking view and the
execution view.
Figure w.4: Simplified object file format: linking view and execution view.
Keep in mind that the full format of the ELF contains many more items. As
explained previously, the linking view, which is used when the program or library is
linked, deals with sections within an object file.
Sections contain the bulk of the object file information: data, instructions, relocation
information, symbols, debugging information, etc.
The execution view, which is used when the program runs, deals with segments.
Segments are a way of grouping related sections.
For example, the text segment groups executable code, the data segment groups
the program data, and the dynamic segment groups information relevant to dynamic
loading.
Each segment consists of one or more sections. A process image is created by
loading and interpreting segments.
The operating system logically copies a files segment to a virtual memory segment
according to the information provided in the program header table. The OS can also
use segments to create a shared memory resource.
At link time, the program or library is built by merging together sections with similar
attributes into segments.
W.7
Typically, all the executable and read-only data sections are combined into a single
text segment, while the data and BSS are combined into the data segment.
These segments are normally called load segments, because they need to be
loaded in memory at process creation. Other sections such as symbol information
and debugging sections are merged into other, non-load segments.
PROCESS LOADING
In Linux processes loaded from a file system (using either
the execve() or spawn()system calls) are in ELF format.
If the file system is on a block-oriented device, the code and data are loaded into
main memory.
If the file system is memory mapped (e.g. ROM/Flash image), the code neednt be
loaded into RAM, but may be executed in place.
This approach makes all RAM available for data and stack, leaving the code in ROM
or Flash. In all cases, if the same process is loaded more than once, its code will be
shared.
Before we can run an executable, firstly we have to load it into memory.
This is done by the loader, which is generally part of the operating system. The
loader does the following things (from other things):
1. Memory and access validation Firstly, the OS system kernel reads in the
program files header information and does the validation for type, access
permissions, memory requirement and its ability to run its instructions. It
confirms that file is an executable image and calculates memory
requirements.
2. Process setup includes:
1. Allocates primary memory for the programs execution.
2. Copies address space from secondary to primary memory.
3. Copies the .text and .data sections from the executable into primary
memory.
4. Copies program arguments (e.g., command line arguments) onto the
stack.
5. Initializes registers: sets the esp (stack pointer) to point to top of stack,
clears the rest.
6. Jumps to start routine, which: copies main()s arguments off of the
stack, and jumps to main().
Address space is memory space that contains program code, stack, and data
segments or in other word, all data the program uses as it runs.
The memory layout, consists of three segments (text, data, and stack), in
simplified form is shown in Figure w.5.
The dynamic data segment is also referred to as the heap, the place dynamically
allocated memory (such as from malloc() and new) comes from. Dynamically
allocated memory is memory allocated at run time instead of compile/link time.
Stack
When a program is running, the initialized data, BSS and heap areas are usually
placed into a single contiguous area called a data segment.
The stack segment and code segment are separate from the data segment and
from each other as illustrated in Figure w.4.
Although it is theoretically possible for the stack and heap to grow into each other,
the operating system prevents that event.
The relationship among the different sections/segments is summarized in Table w.6,
executable program segments and their locations.
Executable file
section
Address space
Program memory
(disk file)
segment
segment
.text
Text
Code
.data
Data
Initialized data
.bss
Data
BSS
Data
Heap
Stack
Stack
Table w.6
W.9 THE PROCESS (IMAGE)
The diagram below shows the memory layout of a typical Cs process. The process
load segments (corresponding to text and data in the diagram) at the processs
base address.
The main stack is located just below and grows downwards. Any additional threads
or function calls that are created will have their own stacks, located below the main
stack.
Each of the stack frames is separated by a guard page to detect stack overflows
among stacks frame. The heap is located above the process and grows upwards.
In the middle of the processs address space, there is a region is reserved for
shared objects. When a new process is created, the process manager first maps
the two segments from the executable into memory.
It then decodes the programs ELF header. If the program header indicates that the
executable was linked against a shared library, the process manager will extract the
name of the dynamic interpreter from the program header.
The dynamic interpreter points to a shared library that contains the runtime linker
code. The process manager will load this shared library in memory and will then
pass control to the runtime linker code in this library.
other required shared libraries (which may themselves reference other shared
libraries).
It will then process the relocations for each library. Some of these relocations are
local to the library, while others require the runtime linker to resolve a global symbol.
In the latter case, the runtime linker will search through the list of libraries for this
symbol. In ELF files, hash tables are used for the symbol lookup, so theyre very
fast.
Once all relocations have been applied, any initialization functions that have been
registered in the shared librarys init section are called. This is used in some
implementations of C++ to call global constructors.
W.11 SYMBOL NAME RESOLUTION
When the runtime linker loads a shared library, the symbols within that library have
to be resolved. Here, the order and the scope of the symbol resolution are
important.
If a shared library calls a function that happens to exist by the same name in several
libraries that the program has loaded, the order in which these libraries are
searched for this symbol is critical. This is why the OS defines several options that
can be used when loading libraries.
All the objects (executables and libraries) that have global scope are stored on an
internal list (the global list).
Any global-scope object, by default, makes available all of its symbols to any shared
library that gets loaded.
The global list initially contains the executable and any libraries that are loaded at
the programs startup.
W.12 DYNAMIC ADDRESS TRANSLATION
In the view of the memory management, modern OS with multitasking, normally
implement dynamic relocation instead of static.
All the program layout in the address space is virtually same. This dynamic
relocation (in processor term it is called dynamic address translation) provides the
illusion that:
1. Each process can use addresses starting at 0, even if other processes are
running, or even if the same program is running more than one time.
2. Address spaces are protected.
3. Can fool process further into thinking it has memory thats much larger than
available physical memory (virtual memory).
In dynamic relocation the address changed dynamically during every
reference. Virtual address is generated by a process (also called logical
address) and the physical address is the actual address in physical memory
at the run-time.
The address translation normally done by Memory Management
Unit (MMU) that incorporated in the processor itself.
Virtual addresses are relative to the process. Each process believes that its virtual
addresses start from 0. The process does not even know where it is located in
physical memory; the code executes entirely in terms of virtual addresses.
MMU can refuse to translate virtual addresses that are outside the range of memory
for the process for example by generating the segmentation faults. This provides
the protection for each process.
During translation, one can even move parts of the address space of a process
between disk and memory as needed (normally called swapping or paging).
This allows the virtual address space of the process to be much larger than the
physical memory available to it.
Graphically, this dynamic relocation for a process is shown in Figure w.6