I am experimenting with running 32-bit code inside a 64-bit Linux process. The 32-bit code is completely autonomous, it makes direct IA32 system calls on its own. If I loaded this code into a 32-bit process, it would work fine.
At first I thought that I could just allocate a stack for 32-bit code, switch to it, and everything will work fine, but this is not so good. Mostly because the instructions associated with the stack (POP / PUSH / ...) did 8-byte shifts instead of 4 bytes.
From googling, I found out that I can switch to 32-bit mode by switching to the 0x23 segment selector. Unfortunately, segments are something that I know very little about.
I can go into 32-bit mode with something like this (AT & T built-in assembly):
movl $0x23, 4(%%rsp) // segment selector 0x23
movq %0, %%rax
movl %%eax, (%%rsp) // target 32-bit address to jump to
lret
Where% 0 contains the 32-bit address where the code is displayed. The code starts to work, I see that PUSH / POP now works as it should, but it crashes even earlier than when I ran the code in 64-bit mode on an apparently harmless instruction:
0x8fe48201 mov 0xa483c(%rbx),%ecx
Where %rbx(or more like %ebx, since this code is already 32-bit, GDB just doesn't know that) contains 0x8fe48200. The address that he is trying to read from ( 0x8feeca3c) is valid and readable (according to /proc/XXX/maps), and when I read it from inside GDB, it contains a wait value.
, Linux SIGSEGV , - 0 ( strace p $_siginfo._sifields._sigfault.si_addr gdb). - , 0x8feeca3c 32- .
, ?
UPDATE: , segfaults 0, 0 . , ( !), .
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
const unsigned char instructions[] = {
0x6a, 0,
0x58,
0xe8, 0, 0, 0, 0,
0x5b,
0x8b, 0x83, 0, 0x20, 0, 0,
0xf4
};
int main()
{
void* area;
void* stack;
area = mmap(NULL, 3*4096, PROT_WRITE|PROT_READ|PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, -1, 0);
memcpy(area, instructions, sizeof(instructions));
stack = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, -1, 0);
stack = (void*) (((uint64_t) stack) + 4096 - 4);
memset(((char*)area) + 2*4096, 0xab, 100);
__asm__ volatile("movq %1, %%rsp;" \
"subq $8, %%rsp;" \
"movl $0x23, 4(%%rsp);" \
"movq %0, %%rax;" \
"movl %%eax, (%%rsp);" \
"lret" :: "m"(area), "r"(stack) :);
}