mirror of
https://github.com/decompals/wibo.git
synced 2025-12-14 15:46:11 +00:00
setup_linux: Improve segments init & add modify_ldt fallback
This commit is contained in:
@@ -110,7 +110,7 @@ void threadCleanup(void *param) {
|
|||||||
}
|
}
|
||||||
g_currentThreadObject = nullptr;
|
g_currentThreadObject = nullptr;
|
||||||
wibo::notifyDllThreadDetach();
|
wibo::notifyDllThreadDetach();
|
||||||
currentThreadTeb = nullptr;
|
wibo::uninstallTebForCurrentThread();
|
||||||
// TODO: mark mutexes owned by this thread as abandoned
|
// TODO: mark mutexes owned by this thread as abandoned
|
||||||
obj->cv.notify_all();
|
obj->cv.notify_all();
|
||||||
obj->notifyWaiters(false);
|
obj->notifyWaiters(false);
|
||||||
|
|||||||
29
src/macros.S
29
src/macros.S
@@ -34,31 +34,32 @@
|
|||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
.macro LJMP32 teb_reg
|
.macro LJMP32 teb_reg
|
||||||
#ifdef __APPLE__
|
#define m64to32 m64to32_\@
|
||||||
#define m1632 m1632_\@
|
|
||||||
.data
|
.data
|
||||||
m1632:
|
m64to32:
|
||||||
.long 1f # 32-bit code offset
|
.long 1f # 32-bit code offset
|
||||||
.long 0 # 32-bit code segment (filled in at runtime)
|
.long 0 # 32-bit code segment (filled in at runtime)
|
||||||
.text
|
.text
|
||||||
mov r10w, word ptr [\teb_reg+TEB_CS_SEL]
|
mov r10w, word ptr [\teb_reg+TEB_CS_SEL]
|
||||||
mov word ptr [rip+m1632+4], r10w
|
mov word ptr [rip+m64to32+4], r10w
|
||||||
jmp fword ptr [rip+m1632]
|
jmp fword ptr [rip+m64to32]
|
||||||
#else
|
|
||||||
jmp fword ptr [rip] # far jump into 32-bit code
|
|
||||||
.long 1f # 32-bit code offset
|
|
||||||
.word CS_32 # 32-bit code segment
|
|
||||||
#endif
|
|
||||||
.code32
|
.code32
|
||||||
1:
|
1:
|
||||||
endbr32
|
endbr32
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
.macro LJMP64 teb_reg
|
.macro LJMP64 teb_reg
|
||||||
// Annoyingly, we can't assemble this in Intel syntax
|
#define m32to64 m32to64_\@
|
||||||
.att_syntax prefix
|
.data
|
||||||
ljmp $CS_64, $1f
|
m32to64:
|
||||||
.intel_syntax noprefix
|
.long 1f # 64-bit code offset
|
||||||
|
.long 0 # 64-bit code segment (filled in at runtime)
|
||||||
|
.text
|
||||||
|
push eax
|
||||||
|
mov ax, word ptr [\teb_reg+TEB_HOST_CS_SEL]
|
||||||
|
mov word ptr [m32to64+4], ax
|
||||||
|
pop eax
|
||||||
|
jmp fword ptr [m32to64]
|
||||||
.code64
|
.code64
|
||||||
1:
|
1:
|
||||||
endbr64
|
endbr64
|
||||||
|
|||||||
21
src/macros.h
21
src/macros.h
@@ -12,22 +12,13 @@
|
|||||||
|
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
#define TEB_CS_SEL 0xf9c // CodeSelector
|
#define TEB_CS_SEL 0xf9c // CodeSelector
|
||||||
#define TEB_DS_SEL 0xf9e // DataSelector
|
#define TEB_DS_SEL 0xf9e // DataSelector
|
||||||
#define TEB_SP 0xfa0 // CurrentStackPointer
|
#define TEB_SP 0xfa0 // CurrentStackPointer
|
||||||
#define TEB_FSBASE 0xfa8 // HostFsBase
|
#define TEB_FSBASE 0xfa8 // HostFsBase
|
||||||
#define TEB_GSBASE 0xfb0 // HostGsBase
|
#define TEB_GSBASE 0xfb0 // HostGsBase
|
||||||
#define TEB_HAS_FSGSBASE 0xfb8 // HasFsGsBase
|
#define TEB_HAS_FSGSBASE 0xfb8 // HasFsGsBase
|
||||||
|
#define TEB_HOST_CS_SEL 0xfba // HostCodeSelector
|
||||||
#ifdef __linux__
|
|
||||||
#define CS_32 0x23 // 32-bit code segment (Linux)
|
|
||||||
#define CS_64 0x33 // 64-bit code segment (Linux)
|
|
||||||
#define DS_32 0x2b // 32-bit data segment (Linux)
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
#define CS_64 0x2b // 64-bit code segment (macOS)
|
|
||||||
#else
|
|
||||||
#error "Unsupported platform"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ int main(int argc, char **argv) {
|
|||||||
wibo::processPeb = peb;
|
wibo::processPeb = peb;
|
||||||
wibo::initializeTibStackInfo(tib);
|
wibo::initializeTibStackInfo(tib);
|
||||||
if (!wibo::installTibForCurrentThread(tib)) {
|
if (!wibo::installTibForCurrentThread(tib)) {
|
||||||
perror("Failed to install TIB for main thread");
|
perror("Failed to setup x86 segments and TEB");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
29
src/setup.S
29
src/setup.S
@@ -8,16 +8,30 @@
|
|||||||
|
|
||||||
#if defined(__x86_64__) && defined(__linux__)
|
#if defined(__x86_64__) && defined(__linux__)
|
||||||
|
|
||||||
# int tebThreadSetup64(int entryNumber, TEB *teb)
|
# void installSelectors(TEB *teb)
|
||||||
ASM_GLOBAL(tebThreadSetup64, @function)
|
# Installs ds/es selectors in 32-bit mode
|
||||||
|
ASM_GLOBAL(installSelectors, @function)
|
||||||
|
mov r8, rsp # save host stack
|
||||||
|
mov rdx, qword ptr [rdi+TEB_SP] # fetch guest stack
|
||||||
|
mov ax, cs # fetch host code segment selector
|
||||||
|
mov word ptr [rdi+TEB_HOST_CS_SEL], ax # store host code segment selector
|
||||||
|
mov ax, word ptr [rdi+TEB_DS_SEL] # user data segment selector
|
||||||
|
LJMP32 rdi # far jump into 32-bit code
|
||||||
|
mov ds, ax # setup data segment
|
||||||
|
mov es, ax # setup extra segment
|
||||||
|
mov esp, edx # switch to guest stack
|
||||||
|
LJMP64 edi # far jump into 64-bit code
|
||||||
|
mov rsp, r8 # switch to host stack
|
||||||
|
ret
|
||||||
|
ASM_END(installSelectors)
|
||||||
|
|
||||||
|
# int setThreadArea64(int entryNumber, TEB *teb)
|
||||||
|
# Runs syscall SYS_set_thread_area in 32-bit mode
|
||||||
|
ASM_GLOBAL(setThreadArea64, @function)
|
||||||
push rbx # save rbx
|
push rbx # save rbx
|
||||||
mov r8, rsp # save host stack
|
mov r8, rsp # save host stack
|
||||||
rdfsbase r9 # read host FS base
|
|
||||||
mov rdx, qword ptr [rsi+TEB_SP] # fetch guest stack
|
mov rdx, qword ptr [rsi+TEB_SP] # fetch guest stack
|
||||||
LJMP32 rsi # far jump into 32-bit code
|
LJMP32 rsi # far jump into 32-bit code
|
||||||
mov ax, 0x2b # user data segment (Linux)
|
|
||||||
mov ds, ax # setup data segment
|
|
||||||
mov es, ax # setup extra segment
|
|
||||||
mov esp, edx # switch to guest stack
|
mov esp, edx # switch to guest stack
|
||||||
sub esp, 0x10 # sizeof(user_desc)
|
sub esp, 0x10 # sizeof(user_desc)
|
||||||
mov dword ptr [esp], edi # entry_number (arg 0)
|
mov dword ptr [esp], edi # entry_number (arg 0)
|
||||||
@@ -35,10 +49,9 @@ ASM_GLOBAL(tebThreadSetup64, @function)
|
|||||||
LJMP64 esi # far jump into 64-bit code
|
LJMP64 esi # far jump into 64-bit code
|
||||||
cdqe # sign-extend eax to rax
|
cdqe # sign-extend eax to rax
|
||||||
mov rsp, r8 # switch to host stack
|
mov rsp, r8 # switch to host stack
|
||||||
wrfsbase r9 # restore host FS base
|
|
||||||
pop rbx # restore rbx
|
pop rbx # restore rbx
|
||||||
ret
|
ret
|
||||||
ASM_END(tebThreadSetup64)
|
ASM_END(setThreadArea64)
|
||||||
|
|
||||||
#endif // defined(__x86_64__) && defined(__linux__)
|
#endif // defined(__x86_64__) && defined(__linux__)
|
||||||
|
|
||||||
|
|||||||
@@ -98,8 +98,7 @@ bool initializeLdtBitmapLocked() {
|
|||||||
if (count > kMaxLdtEntries) {
|
if (count > kMaxLdtEntries) {
|
||||||
DEBUG_LOG("setup_darwin: i386_get_ldt returned too many entries (%d), truncating to %d\n", count,
|
DEBUG_LOG("setup_darwin: i386_get_ldt returned too many entries (%d), truncating to %d\n", count,
|
||||||
kMaxLdtEntries);
|
kMaxLdtEntries);
|
||||||
errno = ENOSPC;
|
count = kMaxLdtEntries;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
markLdtEntryUsed(i);
|
markLdtEntryUsed(i);
|
||||||
|
|||||||
@@ -1,20 +1,205 @@
|
|||||||
#include "setup.h"
|
#include "setup.h"
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
#include <asm/ldt.h>
|
#include <array>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <asm/ldt.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
std::mutex g_tebSetupMutex;
|
std::mutex g_tebSetupMutex;
|
||||||
int g_entryNumber = -1;
|
int g_threadAreaEntry = -1;
|
||||||
|
#ifdef __x86_64__
|
||||||
|
uint16_t g_codeSelector = 0;
|
||||||
|
uint16_t g_dataSelector = 0;
|
||||||
|
#endif
|
||||||
|
constexpr int kMaxLdtEntries = 8192;
|
||||||
|
constexpr int kBitsPerWord = 32;
|
||||||
|
std::array<uint32_t, kMaxLdtEntries / kBitsPerWord> g_ldtBitmap{};
|
||||||
|
bool g_ldtBitmapInitialized = false;
|
||||||
|
int g_ldtHint = 0;
|
||||||
|
|
||||||
constexpr uint16_t createSelector(int entryNumber) {
|
inline user_desc createLdtEntry(uint32_t entryNumber, uint32_t base, uint32_t size, bool code) {
|
||||||
|
user_desc desc; // NOLINT(cppcoreguidelines-pro-type-member-init)
|
||||||
|
// Must memset to zero to avoid uninitialized padding bytes
|
||||||
|
std::memset(&desc, 0, sizeof(desc));
|
||||||
|
|
||||||
|
desc.entry_number = entryNumber;
|
||||||
|
desc.base_addr = base;
|
||||||
|
|
||||||
|
uint32_t limit;
|
||||||
|
if (size > 0xFFFFF) {
|
||||||
|
// Page granularity (like your DESC_GRAN_PAGE case)
|
||||||
|
limit = (size - 1) >> 12;
|
||||||
|
desc.limit_in_pages = 1;
|
||||||
|
} else {
|
||||||
|
// Byte granularity
|
||||||
|
limit = size - 1;
|
||||||
|
desc.limit_in_pages = 0;
|
||||||
|
}
|
||||||
|
desc.limit = limit;
|
||||||
|
desc.seg_32bit = 1;
|
||||||
|
desc.contents = code ? MODIFY_LDT_CONTENTS_CODE : MODIFY_LDT_CONTENTS_DATA;
|
||||||
|
desc.read_exec_only = 0;
|
||||||
|
desc.seg_not_present = 0;
|
||||||
|
desc.useable = 1;
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int modifyLdtRead(struct user_desc *entries, int bytes) {
|
||||||
|
return static_cast<int>(syscall(SYS_modify_ldt, 2, entries, bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int modifyLdtWrite(const struct user_desc *desc) {
|
||||||
|
return static_cast<int>(syscall(SYS_modify_ldt, 1, desc, sizeof(*desc)));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint16_t createGdtSelector(int entryNumber) {
|
||||||
return static_cast<uint16_t>((entryNumber << 3) | USER_PRIVILEGE);
|
return static_cast<uint16_t>((entryNumber << 3) | USER_PRIVILEGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr uint16_t createLdtSelector(int entryNumber) {
|
||||||
|
return static_cast<uint16_t>((entryNumber << 3) | 0x4 /* TI=1 */ | USER_PRIVILEGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isLdtEntryValid(int entry) { return entry >= 0 && entry < kMaxLdtEntries; }
|
||||||
|
|
||||||
|
inline void markLdtEntryUsed(int entry) {
|
||||||
|
if (!isLdtEntryValid(entry)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_ldtBitmap[entry / kBitsPerWord] |= (1u << (entry % kBitsPerWord));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void markLdtEntryFree(int entry) {
|
||||||
|
if (!isLdtEntryValid(entry)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_ldtBitmap[entry / kBitsPerWord] &= ~(1u << (entry % kBitsPerWord));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isLdtEntryUsed(int entry) {
|
||||||
|
if (!isLdtEntryValid(entry)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return (g_ldtBitmap[entry / kBitsPerWord] & (1u << (entry % kBitsPerWord))) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initializeLdtBitmapLocked() {
|
||||||
|
if (g_ldtBitmapInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_ldtBitmapInitialized = true;
|
||||||
|
struct user_desc table[kMaxLdtEntries];
|
||||||
|
std::memset(table, 0, sizeof(table));
|
||||||
|
int bytes = modifyLdtRead(table, sizeof(table));
|
||||||
|
if (bytes < 0) {
|
||||||
|
DEBUG_LOG("setup_linux: modify_ldt(read) failed during bitmap init (%s), assuming empty table\n",
|
||||||
|
strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int count = bytes / static_cast<int>(sizeof(user_desc));
|
||||||
|
if (count > kMaxLdtEntries) {
|
||||||
|
DEBUG_LOG("setup_linux: modify_ldt(read) returned too many entries (%d), truncating to %d\n", count,
|
||||||
|
kMaxLdtEntries);
|
||||||
|
count = kMaxLdtEntries;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
const user_desc &d = table[i];
|
||||||
|
user_desc unused; // NOLINT(cppcoreguidelines-pro-type-member-init)
|
||||||
|
std::memset(&unused, 0, sizeof(user_desc));
|
||||||
|
bool allZero = std::memcmp(&d, &unused, sizeof(user_desc)) == 0;
|
||||||
|
if (!allZero && !d.seg_not_present) {
|
||||||
|
markLdtEntryUsed(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int allocateLdtEntryLocked() {
|
||||||
|
initializeLdtBitmapLocked();
|
||||||
|
auto tryAllocate = [&](int start) -> int {
|
||||||
|
for (int entry = start; entry < kMaxLdtEntries; ++entry) {
|
||||||
|
if (!isLdtEntryUsed(entry)) {
|
||||||
|
markLdtEntryUsed(entry);
|
||||||
|
g_ldtHint = entry + 1;
|
||||||
|
if (g_ldtHint >= kMaxLdtEntries) {
|
||||||
|
g_ldtHint = 0;
|
||||||
|
}
|
||||||
|
DEBUG_LOG("setup_linux: Allocating LDT entry %d\n", entry);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
int entry = tryAllocate(g_ldtHint);
|
||||||
|
if (entry >= 0) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
entry = tryAllocate(0);
|
||||||
|
if (entry >= 0) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
errno = ENOSPC;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeLdtEntryLocked(int entryNumber) {
|
||||||
|
if (!g_ldtBitmapInitialized || !isLdtEntryValid(entryNumber)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
markLdtEntryFree(entryNumber);
|
||||||
|
if (entryNumber < g_ldtHint) {
|
||||||
|
g_ldtHint = entryNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __x86_64__
|
||||||
|
bool segmentSetupLocked(TEB *teb) {
|
||||||
|
// Create code LDT entry
|
||||||
|
if (g_codeSelector == 0) {
|
||||||
|
int entryNumber = allocateLdtEntryLocked();
|
||||||
|
if (entryNumber < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
user_desc codeLdt = createLdtEntry(entryNumber, 0, 0xFFFFFFFF, true);
|
||||||
|
int ret = modifyLdtWrite(&codeLdt);
|
||||||
|
if (ret == 0) {
|
||||||
|
g_codeSelector = createLdtSelector(entryNumber);
|
||||||
|
DEBUG_LOG("setup_linux: Code LDT selector 0x%x\n", g_codeSelector);
|
||||||
|
} else {
|
||||||
|
freeLdtEntryLocked(entryNumber);
|
||||||
|
DEBUG_LOG("setup_linux: Failed to create code LDT entry, trying default (0x23)\n");
|
||||||
|
g_codeSelector = 0x23;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create data LDT entry
|
||||||
|
if (g_dataSelector == 0) {
|
||||||
|
int entryNumber = allocateLdtEntryLocked();
|
||||||
|
if (entryNumber < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
user_desc dataLdt = createLdtEntry(entryNumber, 0, 0xFFFFFFFF, false);
|
||||||
|
int ret = modifyLdtWrite(&dataLdt);
|
||||||
|
if (ret == 0) {
|
||||||
|
g_dataSelector = createLdtSelector(entryNumber);
|
||||||
|
DEBUG_LOG("setup_linux: Data LDT selector 0x%x\n", g_dataSelector);
|
||||||
|
} else {
|
||||||
|
freeLdtEntryLocked(entryNumber);
|
||||||
|
DEBUG_LOG("setup_linux: Failed to create data LDT entry, trying default (0x2b)\n");
|
||||||
|
g_dataSelector = 0x2b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
teb->CodeSelector = g_codeSelector;
|
||||||
|
teb->DataSelector = g_dataSelector;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
#if defined(__x86_64__)
|
#if defined(__x86_64__)
|
||||||
@@ -22,72 +207,116 @@ constexpr uint16_t createSelector(int entryNumber) {
|
|||||||
#include <cpuid.h>
|
#include <cpuid.h>
|
||||||
|
|
||||||
// Implemented in setup.S
|
// Implemented in setup.S
|
||||||
extern "C" int tebThreadSetup64(int entryNumber, TEB *teb);
|
extern "C" void installSelectors(TEB *teb);
|
||||||
|
extern "C" int setThreadArea64(int entryNumber, TEB *teb);
|
||||||
|
|
||||||
bool tebThreadSetup(TEB *teb) {
|
bool tebThreadSetup(TEB *teb) {
|
||||||
std::lock_guard guard(g_tebSetupMutex);
|
std::lock_guard guard(g_tebSetupMutex);
|
||||||
int ret = tebThreadSetup64(g_entryNumber, teb);
|
|
||||||
if (ret < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (g_entryNumber != ret) {
|
|
||||||
g_entryNumber = ret;
|
|
||||||
DEBUG_LOG("set_thread_area: allocated entry=%d base=%p\n", g_entryNumber, teb);
|
|
||||||
} else {
|
|
||||||
DEBUG_LOG("set_thread_area: reused entry=%d base=%p\n", g_entryNumber, teb);
|
|
||||||
}
|
|
||||||
|
|
||||||
teb->CurrentFsSelector = createSelector(ret);
|
|
||||||
teb->CurrentGsSelector = 0;
|
|
||||||
|
|
||||||
// Check for FSBASE/GSBASE instruction support
|
// Check for FSBASE/GSBASE instruction support
|
||||||
unsigned int regs[4];
|
unsigned int regs[4];
|
||||||
int cpuidMax = __get_cpuid_max(0, nullptr);
|
unsigned int cpuidMax = __get_cpuid_max(0, nullptr);
|
||||||
if (cpuidMax >= 0x7 && __get_cpuid_count(0x7, 0, ®s[0], ®s[1], ®s[2], ®s[3])) {
|
if (cpuidMax >= 0x7 && __get_cpuid_count(0x7, 0, ®s[0], ®s[1], ®s[2], ®s[3])) {
|
||||||
teb->HasFsGsBase = !!(regs[1] & 1);
|
teb->HasFsGsBase = !!(regs[1] & 1);
|
||||||
}
|
}
|
||||||
DEBUG_LOG("setup_linux: FSBASE/GSBASE instruction support: %s\n", teb->HasFsGsBase ? "yes" : "no");
|
DEBUG_LOG("setup_linux: FSBASE/GSBASE instruction support: %s\n", teb->HasFsGsBase ? "yes" : "no");
|
||||||
|
|
||||||
|
// Create code and data LDT entries
|
||||||
|
if (!segmentSetupLocked(teb)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install ds/es selectors
|
||||||
|
installSelectors(teb);
|
||||||
|
|
||||||
|
if (g_threadAreaEntry != -2) {
|
||||||
|
int ret = setThreadArea64(g_threadAreaEntry, teb);
|
||||||
|
if (ret >= 0) {
|
||||||
|
if (g_threadAreaEntry != ret) {
|
||||||
|
g_threadAreaEntry = ret;
|
||||||
|
DEBUG_LOG("setup_linux: allocated thread-local GDT entry=%d base=%p\n", g_threadAreaEntry, teb);
|
||||||
|
} else {
|
||||||
|
DEBUG_LOG("setup_linux: reused thread-local GDT entry=%d base=%p\n", g_threadAreaEntry, teb);
|
||||||
|
}
|
||||||
|
teb->CurrentFsSelector = createGdtSelector(ret);
|
||||||
|
} else {
|
||||||
|
DEBUG_LOG("setup_linux: set_thread_area failed (%s), falling back to LDT\n", strerror(errno));
|
||||||
|
g_threadAreaEntry = -2; // Don't bother trying again
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (teb->CurrentFsSelector == 0) {
|
||||||
|
int entryNumber = allocateLdtEntryLocked();
|
||||||
|
if (entryNumber < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
user_desc fsLdt = createLdtEntry(entryNumber, toGuestPtr(teb), sizeof(TEB), false);
|
||||||
|
int ret = modifyLdtWrite(&fsLdt);
|
||||||
|
if (ret != 0) {
|
||||||
|
freeLdtEntryLocked(entryNumber);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
teb->CurrentFsSelector = createLdtSelector(entryNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_LOG("setup_linux: Using FS selector 0x%x\n", teb->CurrentFsSelector);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(__i386__)
|
#elif defined(__i386__)
|
||||||
|
|
||||||
#include <sys/syscall.h>
|
|
||||||
|
|
||||||
bool tebThreadSetup(TEB *teb) {
|
bool tebThreadSetup(TEB *teb) {
|
||||||
std::lock_guard guard(g_tebSetupMutex);
|
std::lock_guard guard(g_tebSetupMutex);
|
||||||
|
|
||||||
struct user_desc desc; // NOLINT(cppcoreguidelines-pro-type-member-init)
|
if (g_threadAreaEntry != -2) {
|
||||||
std::memset(&desc, 0, sizeof(desc));
|
struct user_desc desc; // NOLINT(cppcoreguidelines-pro-type-member-init)
|
||||||
desc.entry_number = g_entryNumber;
|
std::memset(&desc, 0, sizeof(desc));
|
||||||
desc.base_addr = reinterpret_cast<uintptr_t>(teb);
|
desc.entry_number = g_threadAreaEntry;
|
||||||
desc.limit = static_cast<unsigned int>(sizeof(TEB) - 1);
|
desc.base_addr = reinterpret_cast<uintptr_t>(teb);
|
||||||
desc.seg_32bit = 1;
|
desc.limit = static_cast<unsigned int>(sizeof(TEB) - 1);
|
||||||
desc.contents = 0;
|
desc.seg_32bit = 1;
|
||||||
desc.read_exec_only = 0;
|
desc.contents = 0;
|
||||||
desc.limit_in_pages = 0;
|
desc.read_exec_only = 0;
|
||||||
desc.seg_not_present = 0;
|
desc.limit_in_pages = 0;
|
||||||
desc.useable = 1;
|
desc.seg_not_present = 0;
|
||||||
if (syscall(SYS_set_thread_area, &desc) != 0) {
|
desc.useable = 1;
|
||||||
return false;
|
if (syscall(SYS_set_thread_area, &desc) == 0) {
|
||||||
|
if (g_threadAreaEntry != static_cast<int>(desc.entry_number)) {
|
||||||
|
g_threadAreaEntry = static_cast<int>(desc.entry_number);
|
||||||
|
DEBUG_LOG("setup_linux: allocated thread-local GDT entry=%d base=%p\n", g_threadAreaEntry, teb);
|
||||||
|
} else {
|
||||||
|
DEBUG_LOG("setup_linux: reused thread-local GDT entry=%d base=%p\n", g_threadAreaEntry, teb);
|
||||||
|
}
|
||||||
|
teb->CurrentFsSelector = createGdtSelector(desc.entry_number);
|
||||||
|
} else {
|
||||||
|
DEBUG_LOG("setup_linux: set_thread_area failed (%s), falling back to LDT\n", strerror(errno));
|
||||||
|
g_threadAreaEntry = -2; // Don't bother trying again
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (g_entryNumber != static_cast<int>(desc.entry_number)) {
|
if (teb->CurrentFsSelector == 0) {
|
||||||
g_entryNumber = static_cast<int>(desc.entry_number);
|
int entryNumber = allocateLdtEntryLocked();
|
||||||
DEBUG_LOG("setup_linux: allocated GDT entry=%d base=%p\n", g_entryNumber, teb);
|
if (entryNumber < 0) {
|
||||||
} else {
|
return false;
|
||||||
DEBUG_LOG("setup_linux: reused GDT entry=%d base=%p\n", g_entryNumber, teb);
|
}
|
||||||
|
user_desc fsLdt = createLdtEntry(entryNumber, toGuestPtr(teb), sizeof(TEB), false);
|
||||||
|
int ret = modifyLdtWrite(&fsLdt);
|
||||||
|
if (ret != 0) {
|
||||||
|
freeLdtEntryLocked(entryNumber);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
teb->CurrentFsSelector = createLdtSelector(entryNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
teb->CurrentFsSelector = createSelector(desc.entry_number);
|
DEBUG_LOG("setup_linux: Using FS selector 0x%x\n", teb->CurrentFsSelector);
|
||||||
teb->CurrentGsSelector = 0;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool tebThreadTeardown(TEB *teb) {
|
bool tebThreadTeardown(TEB *teb) {
|
||||||
(void)teb;
|
if (teb->CurrentFsSelector & 0x4 /* TI=1 */) {
|
||||||
// no-op on Linux
|
std::lock_guard guard(g_tebSetupMutex);
|
||||||
|
freeLdtEntryLocked(teb->CurrentFsSelector >> 3);
|
||||||
|
}
|
||||||
|
teb->CurrentFsSelector = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -552,6 +552,7 @@ typedef struct _TEB {
|
|||||||
void *HostFsBase;
|
void *HostFsBase;
|
||||||
void *HostGsBase;
|
void *HostGsBase;
|
||||||
bool HasFsGsBase;
|
bool HasFsGsBase;
|
||||||
|
WORD HostCodeSelector;
|
||||||
#endif
|
#endif
|
||||||
} TEB;
|
} TEB;
|
||||||
typedef GUEST_PTR PTEB;
|
typedef GUEST_PTR PTEB;
|
||||||
@@ -581,6 +582,9 @@ static_assert(offsetof(TEB, HostGsBase) == TEB_GSBASE);
|
|||||||
#ifdef TEB_HAS_FSGSBASE
|
#ifdef TEB_HAS_FSGSBASE
|
||||||
static_assert(offsetof(TEB, HasFsGsBase) == TEB_HAS_FSGSBASE);
|
static_assert(offsetof(TEB, HasFsGsBase) == TEB_HAS_FSGSBASE);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef TEB_HOST_CS_SEL
|
||||||
|
static_assert(offsetof(TEB, HostCodeSelector) == TEB_HOST_CS_SEL);
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct _MEMORY_BASIC_INFORMATION {
|
typedef struct _MEMORY_BASIC_INFORMATION {
|
||||||
GUEST_PTR BaseAddress;
|
GUEST_PTR BaseAddress;
|
||||||
|
|||||||
@@ -191,8 +191,8 @@ static void test_try_acquire(void) {
|
|||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
test_shared_readers();
|
test_shared_readers();
|
||||||
// test_exclusive_blocks_shared();
|
test_exclusive_blocks_shared();
|
||||||
// test_shared_then_exclusive();
|
test_shared_then_exclusive();
|
||||||
// test_try_acquire();
|
test_try_acquire();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user