the segment registers are just indices to the kernels descriptor tables. setting the segment registers can be done with assembly instructions from userspace. but what you need is being able to modify the descriptors in a save way from userspace! i needed this for linuxemu to implement set_thread_area syscalls under plan9 so i made the modifications to the kernel: /n/sources/contrib/cinap_lenrek/segdescpatch it adds the files ldt and gdt to devarch. heres a linuxemu process where glibc has setup a special descriptor and loaded %gs to point to it under plan9: cat /dev/gdt 000b data WPUBG 3 1819c080 fffff 000c data - 0 00000000 00000 000d data - 0 00000000 00000 PC 0x00020439 pread+0x7 /sys/src/libc/9syscall/pread.s:5 SP 0xdeffcd70 ECODE 0xf010080c EFLAG 0x00000286 CS 0x00000023 DS 0x0000001b SS 0x0000001b GS 0x0000005b FS 0x00000000 ES 0x0000001b TRAP 0x00000040 system call AX 0x00000032 BX 0x00032b4c CX 0x00000001 DX 0x00000000 DI 0x080e7468 SI 0x00000000 BP 0xdeffcf2c format of is simple text: /* * format: * idx[4] type[8] flags[8] dpl[1] base[8] limit[5]\n */ idx is the descriptor index (the one you load into a segment registers by the selector) type is code or data dpl is the priority level (usualy just 3 for userspace) base and limit describe the location of the segment (limit is in pages if G flag is set) /* * flags: * P = present * A = accessed (for code/data) * E = expand down (for data) * W = writable (for data) * R = readable (for code) * C = conforming (for code) * G = limit granularity in pages (for code/data) * D = 32 bit operand size (for code) * B = 32 bit stack pointer (for data) * Y = busy (for tss and tss16) * U = available for use by system software */ gdt and ldt are both per process. the only difference between gdt and ldt is that gdt has a small fixed number of descriptors in the gdt that you can modify. the ldt refers to the local descriptor table wich can have up to 2^13 user descriptors. -- cinap