Skip to content

Commit 6439769

Browse files
authored
Introduce Virtual Address (#2226)
In the current ongoing effort for sourcemod to fully support 64 bits, we are introducing "virtual address". # Explanation Because SourcePawn does not yet support a 64 bits-wide type it's been impossible for any plugins to hold addresses in regular 32-bits wide variable. A first attempt at solving this issue was made in commit ce1a4dc therein dubbed "PseudoAddress", however this turned out to be an unsatisfactory solution, as any 'high' address if offsetted could turn invalid (or outright be impossible to map). This leaves us with three alternatives : - New type - Convert Address into a handle - Virtual Address A new type is the most destructive solution, as it entails breaking every single Address related method. While that solution is still not off the table, we're reserving it as the last attempt should this commit fail. Converting into a handle type is a good compromise between a brand new type whilst also preserving the Address methods. However, this comes with two issues: the first being that you can no longer offset Address, the second is that we would require authors to free the handle type which will be very confusing. This will likely not be implemented. # Virtual address Under a reasonable assumption, we've noted that the average plugin is unlikely to play with more than 4 GB of memory; this shouldn't be too surprising as all valve games were once 32bits and therefore limited to 4GB. Assuming this stays mostly true and a plugin isn't interested with the mapped memory of lesser known modules (like soundlib or matlib), it is fair to assume plugins are unlikely to access more than 4GB of mapped memory. Working with this in mind, we map the memory the plugins are likely to access to our custom virtual address ranges (from 0 to 4Gb, the values of which can fit on 32bits variable). If any memory was missed and plugins were to try an access it later those ranges will be late-mapped to our virtual address ranges until we run out of them. In order to use virtual addressing, whether on 32 bits or 64 bits. Plugins must now "#include <virtual_address>", as well as use the new SDKCall_VirtualAddress, SDKType_VirtualAddress, LoadAddressFromAddress & StoreAddressToAddress where it's appropriate to.
1 parent 0c900be commit 6439769

20 files changed

+368
-189
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@
1212
[submodule "public/safetyhook"]
1313
path = public/safetyhook
1414
url = https://github.com/alliedmodders/safetyhook
15+
[submodule "core/logic/libaddrz"]
16+
path = core/logic/libaddrz
17+
url = https://github.com/dvander/libaddrz.git

core/logic/AMBuilder

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,15 @@ for cxx in builder.targets:
8686
'DatabaseConfBuilder.cpp',
8787
'LumpManager.cpp',
8888
'smn_entitylump.cpp',
89+
'libaddrz/addrz.cpp',
90+
'libaddrz/mapping.cpp',
91+
'libaddrz/platform.cpp',
92+
'libaddrz/proc_maps.cpp',
93+
'PseudoAddrManager.cpp',
8994
]
90-
91-
if binary.compiler.target.arch == 'x86_64':
92-
binary.sources += ['PseudoAddrManager.cpp']
95+
if binary.compiler.target.platform == 'linux':
96+
binary.sources += ['libaddrz/platform_linux.cpp']
97+
elif binary.compiler.target.platform == 'windows':
98+
binary.sources += ['libaddrz/platform_windows.cpp']
9399

94100
SM.binaries += [builder.Add(binary)]

core/logic/PluginSys.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,13 @@ bool CPlugin::ReadInfo()
330330
else
331331
m_MaxClientsVar = nullptr;
332332

333+
if (base->FindPubvarByName("PointerSize", &idx) == SP_ERROR_NONE) {
334+
sp_pubvar_t* var = nullptr;
335+
if (base->GetPubvarByIndex(idx, &var) == SP_ERROR_NONE && var) {
336+
*var->offs = sizeof(void*);
337+
}
338+
}
339+
333340
return true;
334341
}
335342

core/logic/PseudoAddrManager.cpp

Lines changed: 69 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -28,142 +28,96 @@
2828
*/
2929

3030
#include "PseudoAddrManager.h"
31+
#include <bridge/include/CoreProvider.h>
3132
#ifdef PLATFORM_APPLE
3233
#include <mach/mach.h>
3334
#include <mach/vm_region.h>
3435
#endif
3536
#ifdef PLATFORM_LINUX
3637
#include <inttypes.h>
3738
#endif
39+
#ifdef PLATFORM_WINDOWS
40+
#include <Psapi.h>
41+
#endif
3842

39-
PseudoAddressManager::PseudoAddressManager() : m_NumEntries(0)
43+
PseudoAddressManager::PseudoAddressManager() : m_dictionary(am::IPlatform::GetDefault())
4044
{
4145
}
4246

43-
// A pseudo address consists of a table index in the upper 6 bits and an offset in the
44-
// lower 26 bits. The table consists of memory allocation base addresses.
45-
void *PseudoAddressManager::FromPseudoAddress(uint32_t paddr)
46-
{
47-
#ifdef KE_ARCH_X64
48-
uint8_t index = paddr >> PSEUDO_OFFSET_BITS;
49-
uint32_t offset = paddr & ((1 << PSEUDO_OFFSET_BITS) - 1);
50-
51-
if (index >= m_NumEntries)
52-
return nullptr;
53-
54-
return reinterpret_cast<void *>(uintptr_t(m_AllocBases[index]) + offset);
55-
#else
56-
return nullptr;
47+
void PseudoAddressManager::Initialize() {
48+
#ifdef PLATFORM_WINDOWS
49+
auto process = GetCurrentProcess();
50+
auto get_module_details = [process](const char* name, void*& baseAddress, size_t& moduleSize) {
51+
if (process == NULL) {
52+
return false;
53+
}
54+
auto hndl = GetModuleHandle(name);
55+
if (hndl == NULL) {
56+
return false;
57+
}
58+
MODULEINFO info;
59+
if (!GetModuleInformation(process, hndl, &info, sizeof(info))) {
60+
return false;
61+
}
62+
moduleSize = info.SizeOfImage;
63+
baseAddress = info.lpBaseOfDll;
64+
return true;
65+
};
5766
#endif
58-
}
59-
60-
uint32_t PseudoAddressManager::ToPseudoAddress(void *addr)
61-
{
62-
#ifdef KE_ARCH_X64
63-
uint8_t index = 0;
64-
uint32_t offset = 0;
65-
bool hasEntry = false;
66-
void *base = GetAllocationBase(addr);
67-
68-
if (base) {
69-
for (int i = 0; i < m_NumEntries; i++) {
70-
if (m_AllocBases[i] == base) {
71-
index = i;
72-
hasEntry = true;
73-
break;
74-
}
67+
#ifdef PLATFORM_LINUX
68+
auto get_module_details = [](const char* name, void* baseAddress, size_t& moduleSize) {
69+
auto hndl = dlopen(name, RTLD_NOLOAD);
70+
if (hndl == NULL) {
71+
return false;
7572
}
76-
} else {
77-
return 0;
78-
}
73+
void* addr = dlsym(hndl, "CreateInterface");
74+
dlclose(hndl);
7975

80-
if (!hasEntry) {
81-
index = m_NumEntries;
82-
if (m_NumEntries < SM_ARRAYSIZE(m_AllocBases))
83-
m_AllocBases[m_NumEntries++] = base;
84-
else
85-
return 0; // Table is full
86-
}
87-
88-
ptrdiff_t diff = uintptr_t(addr) - uintptr_t(base);
76+
if (!addr) {
77+
return false;
78+
}
8979

90-
// Ensure difference fits in 26 bits
91-
if (diff > (UINT32_MAX >> PSEUDO_INDEX_BITS))
92-
return 0;
80+
Dl_info info;
81+
if (dladdr(addr, &info) == 0) {
82+
return false;
83+
}
9384

94-
return (index << PSEUDO_OFFSET_BITS) | diff;
95-
#else
96-
return 0;
85+
baseAddress = info.dli_fbase;
86+
// It doesn't matter much if we figure out the module size
87+
// libaddrz coalesce maps on linux
88+
moduleSize = 0;
89+
return true;
90+
};
9791
#endif
92+
93+
// Early map commonly used modules, it's okay if not all of them are here
94+
// Everything else will be caught by "ToPseudoAddress" but you risk running out of ranges by then
95+
const char* libs[] = { "engine", "server", "tier0", "vstdlib" };
96+
97+
char formattedName[64];
98+
for (int i = 0; i < sizeof(libs) / sizeof(const char*); i++) {
99+
bridge->FormatSourceBinaryName(libs[i], formattedName, sizeof(formattedName));
100+
void* base_addr = nullptr;
101+
size_t module_size = 0;
102+
if (get_module_details(formattedName, base_addr, module_size)) {
103+
// Create the mapping (hopefully)
104+
m_dictionary.Make32bitAddress(base_addr, module_size);
105+
}
106+
}
98107
}
99108

100-
void *PseudoAddressManager::GetAllocationBase(void *ptr)
109+
void *PseudoAddressManager::FromPseudoAddress(uint32_t paddr)
101110
{
102-
#if defined PLATFORM_WINDOWS
103-
104-
MEMORY_BASIC_INFORMATION info;
105-
if (!VirtualQuery(ptr, &info, sizeof(MEMORY_BASIC_INFORMATION)))
111+
if (paddr == 0) {
106112
return nullptr;
107-
return info.AllocationBase;
108-
109-
#elif defined PLATFORM_APPLE
110-
111-
#ifdef KE_ARCH_X86
112-
typedef vm_region_info_t mach_vm_region_info_t;
113-
typedef vm_region_basic_info_data_t mach_vm_region_basic_info_data_t;
114-
const vm_region_flavor_t MACH_VM_REGION_BASIC_INFO = VM_REGION_BASIC_INFO;
115-
const mach_msg_type_number_t MACH_VM_REGION_BASIC_INFO_COUNT = VM_REGION_BASIC_INFO_COUNT;
116-
#define mach_vm_region vm_region
117-
#elif defined KE_ARCH_X64
118-
typedef vm_region_info_64_t mach_vm_region_info_t ;
119-
typedef vm_region_basic_info_data_64_t mach_vm_region_basic_info_data_t;
120-
const vm_region_flavor_t MACH_VM_REGION_BASIC_INFO = VM_REGION_BASIC_INFO_64;
121-
const mach_msg_type_number_t MACH_VM_REGION_BASIC_INFO_COUNT = VM_REGION_BASIC_INFO_COUNT_64;
122-
#define mach_vm_region vm_region_64
123-
#endif
124-
vm_size_t size;
125-
vm_address_t vmaddr = reinterpret_cast<vm_address_t>(ptr);
126-
mach_vm_region_basic_info_data_t info;
127-
memory_object_name_t obj;
128-
vm_region_flavor_t flavor = MACH_VM_REGION_BASIC_INFO;
129-
mach_msg_type_number_t count = MACH_VM_REGION_BASIC_INFO_COUNT;
130-
131-
kern_return_t kr = mach_vm_region(mach_task_self(), &vmaddr, &size, flavor,
132-
reinterpret_cast<mach_vm_region_info_t>(&info),
133-
&count, &obj);
134-
135-
if (kr != KERN_SUCCESS)
136-
return nullptr;
137-
138-
return reinterpret_cast<void *>(vmaddr);
139-
140-
#elif defined PLATFORM_LINUX
141-
142-
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
143-
144-
// Format:
145-
// lower upper prot stuff path
146-
// 08048000-0804c000 r-xp 00000000 03:03 1010107 /bin/cat
147-
FILE *fp = fopen("/proc/self/maps", "r");
148-
if (fp) {
149-
uintptr_t lower, upper;
150-
while (fscanf(fp, "%" PRIxPTR "-%" PRIxPTR, &lower, &upper) != EOF) {
151-
if (addr >= lower && addr <= upper) {
152-
fclose(fp);
153-
return reinterpret_cast<void *>(lower);
154-
}
113+
}
114+
return m_dictionary.RecoverAddress(paddr).value_or(nullptr);
115+
}
155116

156-
// Read to end of line
157-
int c;
158-
while ((c = fgetc(fp)) != '\n') {
159-
if (c == EOF)
160-
break;
161-
}
162-
if (c == EOF)
163-
break;
164-
}
165-
fclose(fp);
117+
uint32_t PseudoAddressManager::ToPseudoAddress(void *addr)
118+
{
119+
if (addr == nullptr) {
120+
return 0;
166121
}
167-
return nullptr;
168-
#endif
122+
return m_dictionary.Make32bitAddress(addr).value_or(0);
169123
}

core/logic/PseudoAddrManager.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#define _INCLUDE_SOURCEMOD_PSEUDOADDRESSMANAGER_H_
3232

3333
#include "common_logic.h"
34+
#include "libaddrz/addrz.h"
3435

3536
class PseudoAddressManager
3637
{
@@ -39,13 +40,9 @@ class PseudoAddressManager
3940
public:
4041
void *FromPseudoAddress(uint32_t paddr);
4142
uint32_t ToPseudoAddress(void *addr);
43+
void Initialize();
4244
private:
43-
void *GetAllocationBase(void *ptr);
44-
private:
45-
static constexpr uint8_t PSEUDO_OFFSET_BITS = 26;
46-
static constexpr uint8_t PSEUDO_INDEX_BITS = sizeof(uint32_t) * 8 - PSEUDO_OFFSET_BITS;
47-
void *m_AllocBases[1 << PSEUDO_INDEX_BITS];
48-
uint8_t m_NumEntries;
45+
am::AddressDict m_dictionary;
4946
};
5047

5148
#endif // _INCLUDE_SOURCEMOD_PSEUDOADDRESSMANAGER_H_

core/logic/common_logic.cpp

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
#include "RootConsoleMenu.h"
5858
#include "CellArray.h"
5959
#include "smn_entitylump.h"
60+
#include "PseudoAddrManager.h"
6061
#include <bridge/include/BridgeAPI.h>
6162
#include <bridge/include/IProviderCallbacks.h>
6263

@@ -86,9 +87,7 @@ IScriptManager *scripts = &g_PluginSys;
8687
IExtensionSys *extsys = &g_Extensions;
8788
ILogger *logger = &g_Logger;
8889
CNativeOwner g_CoreNatives;
89-
#ifdef KE_ARCH_X64
9090
PseudoAddressManager pseudoAddr;
91-
#endif
9291

9392
EntityLumpParseResult lastParseResult;
9493

@@ -122,20 +121,12 @@ static void RegisterProfiler(IProfilingTool *tool)
122121

123122
static void *FromPseudoAddress(uint32_t paddr)
124123
{
125-
#ifdef KE_ARCH_X64
126124
return pseudoAddr.FromPseudoAddress(paddr);
127-
#else
128-
return nullptr;
129-
#endif
130125
}
131126

132127
static uint32_t ToPseudoAddress(void *addr)
133128
{
134-
#ifdef KE_ARCH_X64
135129
return pseudoAddr.ToPseudoAddress(addr);
136-
#else
137-
return 0;
138-
#endif
139130
}
140131

141132
static void SetEntityLumpWritable(bool writable)
@@ -236,6 +227,7 @@ static void logic_init(CoreProvider* core, sm_logic_t* _logic)
236227
g_pSourcePawn2 = *core->spe2;
237228
SMGlobalClass::head = core->listeners;
238229

230+
pseudoAddr.Initialize();
239231
g_ShareSys.Initialize();
240232
g_pCoreIdent = g_ShareSys.CreateCoreIdentity();
241233

core/logic/libaddrz

Submodule libaddrz added at 661cd31

0 commit comments

Comments
 (0)