> For the complete documentation index, see [llms.txt](https://l0mb4rd.gitbook.io/pwn/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://l0mb4rd.gitbook.io/pwn/pwn-notes/intro-to-pwn-notes/computer-architecture-and-low-level-programming/x86-64-architecture.md).

# x86-64 Architecture

**x86-64** (also called **x64** or **AMD64**) is a **64-bit processor architecture** used in most modern computers. It is an extension of the older **x86 (32-bit) architecture**, allowing CPUs to handle more memory and perform faster computations.

#### **Key Features of x86-64:**

✅ **64-bit registers and memory addressing** – Can access way more RAM (up to 16 exabytes, compared to 4GB in 32-bit systems).\
✅ **More general-purpose registers (GPRs)** – Improves performance by allowing more efficient data handling.\
✅ **Backward compatibility** – Can still run 32-bit x86 programs.\
✅ **Used in most modern desktops, laptops, and servers** – Found in Intel and AMD processors.

In simple terms, **x86-64 is just a bigger, faster, and more powerful version of x86, designed to handle modern computing needs.**&#x20;

## Getting to know x86-64: Registers.&#x20;

Registers are the storage that CPUs use to store and load data quickly, and they serve a variety of purposes - such as storing data needed for arithmetic operations or addresses to be referenced. In x64 architecture, there are **General Register**, **Instruction Pointer Register (IP)**, and **Flag Register**.

### General-Purpose Registers&#x20;

General-purpose registers are registers that have a main purpose, but can be used for many other purposes. On x86-64, each general-purpose register can hold 8 bytes and can represent integers up to 264−1, given the integer is unsigned.

Commonly-used general-purpose registers are listed below. In addition to these, x64 has more general-purpose registers: r8, r9, ... , r15.

| **Name**                   | **Primary use**                                                          |
| -------------------------- | ------------------------------------------------------------------------ |
| rax (accumulator register) | Return value of a function                                               |
| rbx (base register)        | No primary use on x64                                                    |
| rcx (counter register)     | Loop count for loop statement, or execution count for various operations |
| rdx (data register)        | No primary use on x64                                                    |
| rsi (source index)         | Pointer to the source when moving data                                   |
| rdi (destination index)    | Pointer to the destination when moving data                              |
| rsp (stack pointer)        | Pointer to the location of the stack in use                              |
| rbp (stack base pointer)   | Pointer to the bottom of the stack                                       |

### Instruction Pointer Register

A program is a sequence of machine language code. The role of the instruction pointer register is to indicate which part of the code CPU should execute next. In x64 architecture, the instruction pointer register is called *RIP* and is 8 bytes in size.

### Flag Registers&#x20;

Flag registers are registers that store the current state of the processor. In the x64 architecture, there is a 64-bit sized flag register called RFLAGS, which is an extension of the 16-bit flag register of the past. Just as a flag is signaled by the act of raising and lowering it, the flag register represents the current state of CPU with different bits in it.

RFLAGS is a 64-bit register, allowing the CPU to use up to 64 flags. However, in practice, only 20 of these bits are used, as shown in the figure below. Particularly, the flags in the table below are the ones you are most likely to encounter in the future.

![](https://dreamhack-lecture.s3.amazonaws.com/media/2a6bf077170efd67c530d3583387433ff9eb1fbb8cc92c7ba9908c68d6729495.png)

|                   |                                                                            |
| ----------------- | -------------------------------------------------------------------------- |
| **flags**         | **Meaning**                                                                |
| CF(Carry Flag)    | Set when the result of an unsigned number operation exceeds the bit range. |
| ZF(Zero Flag)     | Set if the result of the operation is zero.                                |
| SF(Sign Flag)     | Set if the result of the operation is negative.                            |
| OF(Overflow Flag) | Set when the result of a signed number operation exceeds the bit range.    |

As a simple example of using the flag, suppose you have `a` with a value of 3 and `b` with a value of 5. Then, you perform an operation to subtract `b` from `a`. The result of the operation is negative, therefore *SF* is set. CPU then knows from *SF* that `a` is smaller than `b`.

## Getting to know x86-64: Program Memory Segmentation

In Linux, a process’s memory is divided into **five main segments**, each serving a specific purpose. These segments are: **code segment, data segment, BSS segment, heap segment, and stack segment**.

Memory in a process spans from `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF` and is structured into these segments based on how the data is used.

One key benefit of this division is that the **operating system assigns different permissions to each segment** based on its intended function. These permissions include **read, write, and execute**, ensuring that the CPU can only perform authorized actions on memory.

For example, the **data segment** stores variables and data used during program execution. Since the program needs to access and modify this data, **read and write permissions** are granted. However, since this segment isn’t meant to contain executable code, **execute permission is denied** to prevent unintended behavior.

Let’s explore each memory segment in more detail.

### **Code Segment (Text Segment)**&#x20;

#### **What is it?**

* Holds the **actual machine code** (compiled program instructions).
* CPU **executes** code directly from this section.

🔒 **Permissions:** **Read & Execute (RX)** – The program can **run** code from here but **can’t modify it** (to prevent corruption or exploits).

**Example:**

```c
int main() { printf("Hello, world!"); return 0; }
```

The compiled instructions for `printf("Hello, world!")` are stored in the **code segment**.

### **Data Segment (Initialized Global & Static Variables)**&#x20;

#### **What is it?**

* Stores **global and static variables that have been initialized** (given a value before the program runs).
* Unlike the BSS segment, variables here already have a defined value.

🔒 **Permissions:** **Read & Write (RW)** – The program can **modify data** but **cannot execute code** from this section.

**Example:**

```c
int initialized_var = 42;  // Stored in the data segment
```

Since `initialized_var` has been **assigned a value (42)**, it lands in the **data segment** instead of BSS.

### **BSS Segment (Uninitialized Global & Static Variables)**&#x20;

#### **What is it?**

* Stores **global and static variables that are declared but not initialized**.
* The OS automatically sets all values in this section to **zero**.

🔒 **Permissions:** **Read & Write (RW)** – Can store data, but **no execution allowed**.

**Example:**

```c
int global_var;  // Stored in BSS (because it's uninitialized)
```

Since `global_var` wasn’t given a value, it goes to the **BSS segment**, unlike `initialized_var` in the **data segment**.

### **Stack Segment (Local Variables & Function Calls)**&#x20;

#### **What is it?**

* Stores **function calls, local variables, and return addresses**.
* **Grows downwards** (towards lower memory addresses).
* Each function call creates a **stack frame** that holds parameters and local variables.

🔒 **Permissions:** **Read & Write (RW)** – but **no execute permission** (prevents stack overflow exploits).

**Example:**

```c
void func() {
    int x = 10;  // 'x' is stored in the stack
}
```

When `func()` runs, the integer `x` is pushed onto the **stack**, and when the function returns, `x` is popped off.

### **Heap Segment (Dynamic Memory)**&#x20;

#### **What is it?**

* Used for **dynamic memory allocation** (memory that grows/shrinks during runtime).
* When you use **malloc()** in C or `new` in C++, memory is taken from the **heap**.
* **Grows upwards** (towards higher memory addresses).

🔒 **Permissions:** **Read & Write (RW)** – Programs can modify heap memory but **cannot execute code** from here.

**Example:**

```c
int *ptr = malloc(10 * sizeof(int));  // Memory allocated on the heap
```

The pointer `ptr` is stored in the stack, but the **actual memory for 10 integers** is allocated in the **heap**.

<table data-header-hidden><thead><tr><th></th><th></th><th width="156"></th><th></th></tr></thead><tbody><tr><td><strong>Segments</strong></td><td><strong>Roles</strong></td><td><strong>Common permissions</strong></td><td><strong>Usage examples</strong></td></tr><tr><td>Code segment</td><td>Segment where executable code is stored</td><td>read, execute</td><td>Function code, such as <code>main()</code></td></tr><tr><td>Data segment</td><td>Segment where initialized global variables or constants are located</td><td>read, write / read-only</td><td>Initialized global variables, global constants</td></tr><tr><td>BSS segment</td><td>Segment where uninitialized data is located</td><td>read, write</td><td>Uninitialized global variables</td></tr><tr><td>Stack segment</td><td>Segment where temporary variables are stored</td><td>read, write</td><td>Local variables, function arguments, etc.</td></tr><tr><td>Heap segment</td><td>Segment which is dynamically used during execution</td><td>read, write</td><td>Memory allocated by <code>malloc()</code>, <code>calloc()</code>, etc.</td></tr></tbody></table>

## References

Big thanks to [Dreamhack](https://dreamhack.io) for the amazing material on x86-64 :)
