Last active
August 29, 2015 14:26
-
-
Save Zoxc/338baa18a631cd730a36 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
All code is shared between processes. Processes can not read or write to the code segment. | |
The global code segment is 2 TB. It is split into 1MB blocks, giving 2M blocks in total. | |
A module is a executable or shared library. | |
Each process has an bitmap where each bit represents a block of code of the code segment. | |
With 1 bit per block means the size of the array is 256KB. | |
If a bit is 0, the process does not have access to the block in the code segment. | |
There is a global array of function tables each associated with a block in the code segment. | |
We require indirect function targets to be 16 byte aligned. | |
The table contains 16-bit offsets each representing a valid code target of the form: block_offset + table[index] * 16 | |
If a table entry is invalid, it points to an address which contains a halting instruction. | |
The number of legal function targets in a block is 1MB / 16B = 64K. | |
The size of the function table for a block would be 64K * 16-bit = 128KB. A bitmap of legal targets would be 8 KB. | |
The global array of function tables would be 128KB * 2M = 256 GB. A bitmap would be 64 GB. | |
Since the number of possible function targets are way larger than actual function targets we can require the linker | |
to limit the number of indirect function targets per block. Say we limit each block to have 1K targets. | |
The function table size would now be 1K * 16-bit = 2KB. The global array 2KB * 2M = 4 GB. | |
The format of code pointers is this: | |
64-bits 32 0 | |
mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmbbbbbbbbbbbbbbbbbbbbbiiiiiiiiiiu | |
The m's form a 32-bit integer referring to the data segment of the callee. | |
The b's form a 21-bit integer referring to the block index. | |
The i's form a 10-bit integer referring to the function table index for the block. | |
The u is an unused bit. | |
We multiply the data segment integer with 0x1000 to get a pointer to the actual data segments. | |
This limits the data segments to a 4 TB address space. | |
When we are making an indirect function call: | |
mov r15, function_pointer | |
call verify_call | |
... | |
verify_call: | |
mov ecx, r15d | |
shr ecx, 11 ; access the block index | |
mov eax, 1 | |
mov r11, rcx | |
shl eax, cl | |
mov rcx, r11 | |
shr rcx, 6 | |
test qword ptr gs:[rcx], rax ; check if the correct bit is set in the process bitmap; it is located at gs:0 | |
je failure | |
mov rax, r15 | |
and rax, 2046 ; access the function table index * 2 | |
movzx eax, word ptr [rax] ; rax = global_array[index * 2] ; The global array is at offset 0 | |
shl r11, 20 ; multiply by block size | |
shr r15, 32 | |
shl r15, 12 ; extract and align the data segment | |
shl rax, 4 ; 16-bit multiply of offset due to alignment | |
add rax, r11 ; add block base and code offset | |
jmp rax | |
failure: | |
jmp bad_function_call | |
r15 allows functions to access their data segment using [r15 + offset]. | |
This is needed since the address space is shared between multiple processes. | |
In the common case of modules directly calling libraries we simply load the callee's data segment from the caller's data segment. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment