You're talking about memory virtualization, which, unless you're developing an operating system, is nothing you have to care about, since it's transparent to user processes.
It's true that the address your program sees, notepad.0073053, is not the same as the one that is physically on the hardware address pins on the processor. But, when the processor executes the call instruction, it doesn't translate the destination address by some magic and puts this translated addres into the program counter - the program counter will. after the call, hold 0x007353.
The virtualization is done in the memory management unit (MMU), which you can think of as a separate piece of hardware between the actual processor and the ram. (Of course, they are on the same chip in modern processors, but there used to be separate MMUs long ago). Think of it this way:
+-----------------+ +----------------------+ +-----------------+
| CPU | | MMU lookup table | | RAM |
+-----------------+ +----------------------+ +-----------------+
| call 73053 | 73053 | virtual | phys. | 63053 |00000 |
| access 73053 on |---------->| 10000-20000 | a0000 |--------->|10000 |
| the address bus | | 40000-50000 | 30000 | |20000 |
+-----------------+ | 70000-80000 | 60000 | |30000 |
+----------------------+ |40000 |
|50000 |
|60000 X |
|70000 |
|80000 |
+-----------------+
The MMU contains a lookup table - which virtual address range maps to which physical addres range. Whenever the processor accesses memory, it tells the mmu which virtual address to access; the MMU uses its lookup table to determine the actual physical address, and that's what it puts on the address bus. But, the processor doesn't care about this translation. What you see in Ollydbg is always the virtual address, never the physical one.
The MMU entries are handled inside the operating system, which may rearrange them as it sees fit. For example, the OS may decide to need the RAM block at 60000 for something else, copy the block at 60000 to, for example, 20000, and update the MMU table. Your program won't notice anything of that - it still accesses the same virtual memory location, which is now at a different place in physical memory.
+-----------------+ +----------------------+ +-----------------+
| CPU | | MMU lookup table | | RAM |
+-----------------+ +----------------------+ +-----------------+
| call 73053 | 73053 | virtual | phys. | 23053 |00000 |
| access 73053 on |---------->| 10000-20000 | a0000 |--------->|10000 |
| the address bus | | 40000-50000 | 30000 | |20000 X |
+-----------------+ | 70000-80000 | 20000 | |30000 |
+----------------------+ |40000 |
|50000 |
|60000 |
|70000 |
|80000 |
+-----------------+
If the operating system decides to page out a memory block to disk, it will clear the corresponding MMU entry. Now, when the processor tries to access that virtual memory, the MMU will generate a page fault
, which tells the processor it can't access the memory.
+-----------------+ +----------------------+ +-----------------+
| CPU | | MMU lookup table | | RAM |
+-----------------+ 73053 +----------------------+ +-----------------+
| call 73053 |---------->| virtual | phys. | |00000 |
| access 73053 on | | 10000-20000 | a0000 | |10000 |
| the address bus | fault! | 40000-50000 | 30000 | |20000 |
+-----------------+<----------| 90000-a0000 | 40000 | |30000 |
+----------------------+ |40000 |
|50000 |
|60000 |
|70000 |
|80000 |
+-----------------+
This page fault will make the processor call the page fault handler within the operating system. The OS keeps a list of which pages it has written to disk, finds a memory location that's currently unused, reads the required page from disk to that location, updates the MMU accordingly, then returns to the user program and re-executes the instruction that generated the page fault. The user program won't know anything about that (unless it tries hard to find out, for example by measuring the real time needed and comparing that to the expected time). In Windows, perfmon
s Ram/Page faults per second
counter will tell you how often that happened.
(Actually, there are different kinds of page faults. Space in the MMU tables is quite limited, it normally doesn't map all of a user program's virtual addresses. When a page fault occurs, the OS first checks "Is that block of memory in RAM somewhere, with only the MMU entry missing?". If yes, the OS just generates the MMU entry and allows the program to continue. This is called a minor page fault, which is quite fast to handle; page faults that actually access the disk are called major page faults, and impact performance much more).