SimplifyC++ Article
#12 Mastering GAS: A Complete Guide to the GNU Assembler
#12 Mastering GAS: A Complete Guide to the GNU Assembler
Series for explaining and teaching GNU GAS Assembler using AT&T syntax – all codes are reviewed and tested daily on
Fedora Linux 42
GNU Assembler version 2.44-6
----------------------------------
Memory Sections in GAS
.data Section: Storing Pre-Initialized Data
In assembly language programming, the .data section plays a vital role in the structure of a program, serving as the region where pre-initialized data is stored. This section is typically used for defining variables and constants that have predefined values before the program starts running. In this section, we will explore the intricacies of the .data section, its role in the assembly process, its usage, and its relationship with other sections of a program.
Role and Importance of the .data Section
The .data section is one of the most fundamental aspects of a program's structure, serving as a storage area for data that is known at compile-time. This data may consist of global variables, constants, strings, and other types of information that the program will access throughout its execution. Understanding the function and usage of this section is crucial for efficient program development.
What Goes in the .data Section?
Global Variables: These are variables that are defined once and are accessible across the entire program, including all functions and procedures. They are initialized in the
.datasection.Constants: Constants are values that do not change during the program’s execution, such as fixed numerical values or string literals. These are stored in the
.datasection for easy access during runtime.Strings: Strings, especially null-terminated strings, are typically stored in the
.datasection. This is essential for programs that need to work with textual data.Pre-Initialized Arrays and Buffers: Arrays or buffers that need to hold specific values when the program starts can be stored in the
.datasection.
Why Is It Important?
The .data section ensures that the necessary information is available to the program as soon as it begins execution. For example, strings that will be output to the screen, configuration settings, lookup tables, and any other values needed at runtime are made accessible from this section. By keeping these values organized in one part of the program’s memory layout, the program’s execution becomes more predictable and efficient.
The .data Section in the Memory Layout
When a program is executed, the operating system loads it into memory. The program’s memory space is divided into several segments, each of which serves a distinct purpose. The .data section resides in the data segment of the program’s memory space. Understanding this segmentation helps clarify how data is organized and managed.
Text Segment: Contains executable code (machine instructions).
Data Segment: Stores global and static variables, including those defined in the
.datasection.BSS Segment: Contains uninitialized global variables, which are allocated but not assigned a value.
Heap: Used for dynamic memory allocation during runtime.
Stack: Used for storing local variables and function call information.
The .data section is typically loaded into memory with the program’s executable, ensuring that data is ready for use by the time the program begins execution. The data in this section is read-write, which allows the program to modify these values during execution.
Defining Data in the .data Section
In GAS (GNU Assembler), data can be defined in the .data section using various directives to specify the type and value of the data. These directives tell the assembler how to allocate memory for variables and initialize them with specific values.
Common Data Types and Directives
Integer Data: To define integer data in the
.datasection, the.intor.longdirective is used. These directives allocate a specific amount of memory for integer values and initialize them with the given value..section .datamy_int: .int 42 # Integer variable initialized to 42my_long: .long 1234567890 # Long integer initialized with a large valueFloating-Point Data: You can also store floating-point numbers using the
.floatand.doubledirectives..section .datapi_value: .float 3.14159 # Storing a floating-point numberCharacter and String Data: Strings and characters are commonly defined using
.asciz(for null-terminated strings). This is particularly useful for output operations where you need to print a string to the console or use it in text-based computations..section .datamsg: .asciz "Hello, World!" # Null-terminated stringchar: .byte 'A' # Single characterArrays and Buffers: Arrays are sequences of data elements, and you can define them in the
.datasection by using the appropriate data type directives, such as.byte,.word, or.int..section .datanumbers: .byte 1, 2, 3, 4, 5 # An array of bytesBooleans: Boolean values, often represented as
0(false) and1(true), can be stored as integers or bytes..section .dataflag: .byte 1 # Boolean value stored as a byte
Accessing Data in the .data Section
Once data is defined in the .data section, it is available for use by the program's instructions. To access data, we typically use the load and store instructions. These operations allow the program to read from and write to the memory locations defined in the .data section.
Loading Data into Registers
In GAS, data can be accessed by loading values from the .data section into CPU registers. Registers are used to hold values temporarily while performing operations.
For example, to load the integer value stored in my_int into the eax register, you can use the mov instruction as follows:
movl my_int, %eax # Load the value at memory location 'my_int' into eaxIn this example, the value at the memory address of my_int is loaded into the eax register. The square brackets indicate that the value at that memory address should be loaded, not the memory address itself.
Storing Data from Registers
Once data is loaded into a register and processed (e.g., performing a calculation), the result can be stored back into the .data section.
movl %eax, my_int # store %eax into memory at 'my_int'In this case, the value in the eax register is stored back in the memory address of my_int.
Dereferencing Pointers
In some cases, data in the .data section may be stored as a pointer to another memory location. Dereferencing these pointers allows the program to follow the chain of pointers to access the actual data. This process is done by referencing the pointer and using the mov instruction to load or store data from that location.
Access Modifiers in the .data Section
In assembly, data defined in the .data section is typically read-write, meaning that it can be modified by the program during execution. However, it is possible to specify read-only data in the .data section by using appropriate directives, such as .rodata, in some assemblers.
Read-Only Data:
Sometimes, you may want to define data that should not be modified during execution. For example, strings or constants that should remain unchanged can be marked as read-only. This is typically achieved by using a special section like .rodata (read-only data) in other assemblers, but in GAS, you would avoid marking these as writable.
Example of a Complete Program Using the .data Section
Below is an example of how a program can use the .data section to store pre-initialized data, which is then used during the program’s execution:
.section .datamsg: .asciz "Welcome to GAS!"num: .int 42buffer: .space 128
.section .text .global _start
_start: # Writing a message to the console movl $4, %eax # syscall number for sys_write movl $1, %ebx # file descriptor for stdout movl $msg, %ecx # address of the message to print movl $18, %edx # length of the string int $0x80 # invoke system call
# Performing a computation using the pre-initialized integer movl num, %eax # load the integer value into eax addl $5, %eax # add 5 to the value movl %eax, num # store the result back into num
# Exit the program movl $1, %eax # syscall number for sys_exit xorl %ebx, %ebx # return code 0 int $0x80 # invoke system callExplanation:
msg: The message
"Welcome to GAS!"is stored as a null-terminated string in the.datasection. It is printed to the console using thesys_writesystem call.num: The integer
42is stored in the.datasection, and after performing an addition operation, it is updated with the result (47).buffer: A buffer of 128 bytes is reserved for future use, but no initial value is assigned.
This simple program demonstrates how data is stored in the .data section, accessed via registers, and manipulated throughout the execution of the program.
Conclusion
The .data section is a crucial part of a program's memory structure, designed to hold pre-initialized data such as global variables, constants, and strings. By placing this data in the .data section, the program ensures that these values are available for use during execution. The .data section provides a flexible, organized way to handle static data, making it easy to access and modify as needed during the program's runtime. Through efficient use of this section, programmers can manage data in an orderly fashion and ensure that the program's operations remain smooth and efficient.