Skip to content

Dyrun/Memory-cpp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 

Repository files navigation

Memory sections workshop

The role of memory in programming

During compilation, the source code, written in a high-level language like C++, is translated into machine code, which is the low-level language directly understood by the computer's processor. The resulting executable file contains these machine code instructions.
When you start the program - loading -, the operating system reads the executable file from the hard drive and loads it into the computer's main memory (RAM). This process is necessary because the CPU can only directly read and execute instructions from the RAM.
The central processing unit (CPU) fetches and executes the instructions from memory in a sequential manner. As the program runs, it manipulates the data stored in the computer's memory.

Memory regions

When an executable file is loaded into memory, the operating system subdivides the allocated memory region into discrete segments. These segments typically include the .text segment for executable code, the .data segment for initialized data, and the .bss segment for uninitialized data.

What is the significance of these sections?

  • Organization: The memory segmentation helps the system to keep track of different types of data, such as code, initialized data, uninitialized data, and the stack. This organization makes it easier for the operating system to allocate and deallocate memory.
  • Protection: The separation of sections provides a level of memory protection, preventing one part of a program from interfering with another. This is crucial for multi-tasking operating systems where multiple programs may be running concurrently.
  • Optimization: By understanding the layout of memory, compilers and linkers can generate more efficient code. For example, they can place frequently used code and data in the same cache line, reducing cache misses. Additionally, by separating code and data, the system can implement techniques like paging and swapping, which improve memory management.

The high-level overview of section creation

  • Compilation: The compiler breaks down the human-readable source code into machine code, which is a sequence of instructions that the computer can directly execute. The resulting object file contains these instructions, along with information about data and other sections.
  • Linking: The linker takes multiple object files and resolves references between them, creating a single executable file. It assigns absolute memory addresses to each section, ensuring that the program's code and data are located in the correct places in memory.
  • Loading: The operating system loads the executable file into memory, allocating space for each section. The exact memory addresses assigned to each section may vary depending on the system's memory layout and other running programs.

Important note: The absolute addresses assigned by the linker do not represent specific physical addresses in RAM. Instead, they provide a relative addressing scheme within the executable file. This means that the linker determines the relative positions of the sections within the file, but the actual physical addresses are only determined at load time.

Memory sections

  • .text: This segment contains the executable code of the program. It stores the machine instructions that the CPU will execute.
  • .data: This segment holds initialized global and static variables. These variables have their values assigned at compile time.
  • .bss: This segment contains uninitialized global and static variables. These variables are allocated space, but their initial values are all set to zero.
  • .rdata: This segment holds read-only data, such as constant strings and literal values. It is often used to store data that is not modified during program execution.
  • .xdata: This segment is specific to certain embedded systems and typically holds variables that are too large to fit in the .data or .bss segments.
  • .pdata: This segment is used for exception handling in some programming languages and environments. It contains information about the program's data, such as the addresses of variables and functions, which is used to unwind the stack in case of an exception.

Assignments

Preparations

To make our jobs easier and avoid tool-related issues, let's use a common environment where everything is already set up. Here's how to do it.
Within the .devcontainer folder, you'll find a file that describes a C++ development environment. By utilizing this file in Visual Studio Code, you can establish a connection to a Linux-based development environment. The steps involved are as follows:

  • Make sure you have Docker Desktop installed and running on your computer.
  • Install the Remote Development extension pack in Visual Studio Code. Once the installations are complete, follow these steps:
  • Open the Command Palette (usually by pressing Ctrl+Shift+P) and search for Dev Containers: Clone Repository in Container Volume
  • You may need to authorize the extension for GitHub authentication. You will be redirected to your web browser to complete this process.
  • A development container will be created and started in the background. You may see a terminal window open while the container is being created.

Assignment: Uncovering Variables and Functions in Memory Sections

The source file for the next task is located in the ./sections directory.
Hint: you may find the following instructions useful for completing this task:

  • compile and create object file:
g++ -c sections.cpp -o sections.o
  • check the section contents:
objdump -s --section=<section_name> ./sections.o
  • check the section's size:
size sections.o

.data Section Exploration

Create a variable in your program that is placed in the .data section. Verify its placement!

.bss Section Exploration

Create a variable in your program that is placed in the .bss section. Verify its placement!

.text Section Exploration

Create a function getFact that calculates the factorial of a number in your program. Determine the size of the resulting function.

.text area Function Playground I.

Experiment by calling the getFact function multiple times and observe how the size of the compiled code changes.
For instance, create a variable on the stack and assign the function's return value to it.
Additionally, allocate memory on the heap and store the result there.

.text area Function Playground II.

Experiment by converting the getFact function into an inline function. Call it multiple times and measure the change in the compiled code size after each invocation.

Compile-Time Function Evaluation I.

Call the getFact function to calculate the factorial of 5. Store the factorial of the previous number in a non-constant variable. Can you make the calculation happen at compile time? In which code section will the calculated value be placed?

Compile-Time Function Evaluation II.

Call the getFact function to calculate the factorial of 5. Store the factorial of the previous number in a constant variable. Can you make the calculation happen at compile time? In which code section will the calculated value be placed?

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors