commit ca1bfbd9fc9043c99d6c1d4f97caca6ac676bb70
parent 04c6b5b7c2535cf384926b6ccdce3dbeb0c78651
Author: Russ Cox <rsc@swtch.com>
Date: Sun, 7 Dec 2008 20:18:46 -0800
libvx32: build on x86-64 using gcc 4.3.2
Diffstat:
12 files changed, 124 insertions(+), 46 deletions(-)
diff --git a/src/libvx32/Makefrag b/src/libvx32/Makefrag
@@ -52,4 +52,3 @@ libvx32/libvx32.a: $(VX32_OBJS)
$(prefix)/lib/libvx32.a: libvx32/libvx32.a
$(INSTALL) $< $@
-
diff --git a/src/libvx32/asm.h b/src/libvx32/asm.h
@@ -1,25 +0,0 @@
-#define ASDF 12345
-#define VXEMU_DATASEL 12
-#define VXEMU_EMUSEL 16
-#define VXEMU_EMUPTR 28
-#define VXEMU_REG 32
-#define VXEMU_EAX 32
-#define VXEMU_ECX 36
-#define VXEMU_EDX 40
-#define VXEMU_EBX 44
-#define VXEMU_ESP 48
-#define VXEMU_EBP 52
-#define VXEMU_ESI 56
-#define VXEMU_EDI 60
-#define VXEMU_EIP 64
-#define VXEMU_EFLAGS 68
-#define VXEMU_TRAPNO 80
-#define VXEMU_JMPINFO 100
-#define VXEMU_HOST_SS 124
-#define VXEMU_HOST_DS 126
-#define VXEMU_HOST_ES 128
-#define VXEMU_HOST_VS 130
-#define VXEMU_HOST_ESP 132
-#define VXEMU_ETABLEN 152
-#define VXEMU_ETABMASK 156
-#define VXEMU_ETAB 164
diff --git a/src/libvx32/elf.c b/src/libvx32/elf.c
@@ -153,10 +153,12 @@ static int elfloader(vxproc *proc,
// For "big mem" we need more than 1/2 GB, but 64-bit Linux
// has only about 1 GB of address space to give out with MAP_32BIT,
// and we've used up some of it for the vxemu structure.
- // Ask for (1<<30) - (1<<24), which should be close enough to 1GB
+ // Used to ask for (1<<30) - (1<<24), which should be close enough to 1GB
// to run the SPEC programs but leave enough for things like vxemu.
+ // On Ubuntu 8.10, I get intermittent ouf of memory errors from this
+ // mmap, so back off to 1<<29.
if (vx_elfbigmem)
- size = (1<<30) - (1<<24);
+ size = (1<<29);
mm = NULL;
diff --git a/src/libvx32/emu.c b/src/libvx32/emu.c
@@ -26,7 +26,7 @@
// Special values for unused entries in entrypoint hash table
#define NULLSRCEIP ((uint32_t)-1)
-#define NULLDSTEIP ((uint32_t)vxrun_nullfrag);
+#define NULLDSTEIP ((uint32_t)(uintptr_t)vxrun_nullfrag);
int vx32_debugxlate = 0;
@@ -1904,6 +1904,6 @@ void vxprint(char *fmt, ...)
va_start(arg, fmt);
vsnprintf(buf, sizeof buf, fmt, arg);
va_end(arg);
- write(2, buf, strlen(buf));
+ USED(write(2, buf, strlen(buf)));
}
diff --git a/src/libvx32/linux-asm.S b/src/libvx32/linux-asm.S
@@ -6,6 +6,8 @@
// notice also that unlike the linux getcontext,
// we *do* copy the segment registers
+#ifdef i386
+
.globl vx32_getcontext
vx32_getcontext:
movl 4(%esp), %eax
@@ -33,8 +35,10 @@ vx32_getcontext:
/* 56(%eax) is eip */
movw %cs, %cx
movl %ecx, 60(%eax)
- pushfl
- popl 64(%eax)
+ pushf
+ movl 0(%esp), %ecx
+ popf
+ movl %ecx, 64(%eax)
/* 68(%eax) is another esp */
movw %ss, %cx
movl %ecx, 72(%eax)
@@ -49,3 +53,53 @@ vx32_getcontext:
movl $0, %eax
ret
+#else /* x86-64 */
+
+.globl vx32_getcontext
+vx32_getcontext:
+ // mcontext_t pointer is in %rdi
+ movq %r8, 0(%rdi)
+ movq %r9, 8(%rdi)
+ movq %r10, 16(%rdi)
+ movq %r11, 24(%rdi)
+ movq %r12, 32(%rdi)
+ movq %r13, 40(%rdi)
+ movq %r14, 48(%rdi)
+ movq %r15, 56(%rdi)
+ movq %rdi, 64(%rdi)
+ movq %rsi, 72(%rdi)
+ movq %rbp, 80(%rdi)
+ movq %rbx, 88(%rdi)
+ movq %rdx, 96(%rdi)
+ movq $1, 104(%rdi) /* %rax */
+ movq %rcx, 112(%rdi)
+ /* 120(%rdi) is rsp */
+ leaq 8(%rsp), %rax
+ movq %rax, 120(%rdi)
+ /* 128(%rdi) is rip */
+ movq 0(%rsp), %rax
+ movq %rax, 128(%rdi)
+ /* 136(%rdi) is eflags */
+ pushf
+ popq %rax
+ movq %rax, 136(%rdi)
+ /* 144(%rdi) is cs, gs, fs, pad */
+ xorq %rax, %rax
+ movw %fs, %ax
+ shlq $16, %rax
+ movw %gs, %ax
+ shlq $16, %rax
+ movw %cs, %ax
+ movq %rax, 144(%rdi)
+ /* 152(%rdi) is err */
+ movq $0, 152(%rdi)
+ /* 160(%rdi) is trapno */
+ movq $0, 160(%rdi)
+ /* 168(%rdi) is oldmask */
+ movq $0, 168(%rdi)
+ /* 176(%rdi) is cr2 */
+ movq $0, 176(%rdi)
+ movq $0, %rax
+ ret
+
+#endif
diff --git a/src/libvx32/linux.c b/src/libvx32/linux.c
@@ -8,6 +8,7 @@
#include <ucontext.h>
#include <sys/ucontext.h>
#include <asm/ldt.h>
+#include <errno.h>
#include "vx32.h"
#include "vx32impl.h"
@@ -19,6 +20,7 @@ int vxemu_map(vxemu *emu, vxmmap *mm)
{
struct vxproc *vxp;
struct user_desc desc;
+ uint ldt[2];
#ifdef __x86_64
static int didflat;
#endif
@@ -73,13 +75,55 @@ int vxemu_map(vxemu *emu, vxmmap *mm)
desc.seg_not_present = 0;
desc.useable = 1;
- desc.entry_number = emu->runptr.sel / 8;
+ desc.entry_number = FLATCODE / 8;
desc.base_addr = 0;
desc.limit = 0xfffff;
desc.contents = MODIFY_LDT_CONTENTS_CODE;
if (modify_ldt(1, &desc, sizeof(desc)) < 0)
return -1;
-
+
+ /*
+ * Linux 2.6.27 has a bug: it does not load the L (long mode)
+ * bit from desc.lm when copying desc into its own
+ * copy of the LDT entry on the kernel stack.
+ * Instead, it leaves L uninitialized, picking up whatever
+ * random bit was left on the kernel stack by the
+ * previous call sequence. We need L to be 0.
+ * If it ends up 1, the *ljmpq in run64.S will GP fault.
+ * Luckily, we can look for this by asking to read
+ * back the raw LDT bytes. If we observe this problem,
+ * try to fix it by doing a modify_ldt with base = limit = 0,
+ * which clears the entire stack ldt structure, and then
+ * quickly do another modify_ldt with desc, hoping that
+ * the bit will still be zero when we get there for the
+ * second modify_ldt. I wish I were making this up.
+ * This is fixed in Linus's git repository, but the Ubuntu
+ * git repositories are still out of date. See for example
+ * http://swtch.com/go/ubuntu-ldt
+ * http://swtch.com/go/linus-ldt
+ *
+ * Remember, folks, Free Software is only free if your
+ * time has no value.
+ */
+ if(modify_ldt(0, ldt, sizeof ldt) < 0)
+ return -1;
+ if(ldt[1] & 0x00200000) {
+ if (vx32_debugxlate)
+ vxprint("FLATCODE LDT=%08x %08x; working around\n", ldt[0], ldt[1]);
+ desc.limit = 0;
+ modify_ldt(1, &desc, sizeof desc);
+ desc.limit = 0xfffff;
+ modify_ldt(1, &desc, sizeof desc);
+ modify_ldt(0, ldt, sizeof ldt);
+ if(ldt[1] & 0x00200000) {
+ vxprint("cannot work around Linux FLATCODE bug\n");
+ errno = EBADE;
+ return -1;
+ }
+ if (vx32_debugxlate)
+ vxprint("FLATCODE LDT=%08x %08x\n", ldt[0], ldt[1]);
+ }
+
desc.entry_number = FLATDATA / 8;
desc.base_addr = 0;
desc.limit = 0xfffff;
@@ -179,6 +223,7 @@ int vx32_sighandler(int signo, siginfo_t *si, void *v)
if(0) vxprint("vx32_sighandler signo=%d eip=%#x esp=%#x vs=%#x\n",
signo, ctx->ctxeip, ctx->esp, vs);
+ if(0) dumpsigcontext(ctx);
if ((vs & 15) != 15) // 8 (emu), LDT, RPL=3
return 0;
@@ -325,8 +370,9 @@ int vx32_sighandler(int signo, siginfo_t *si, void *v)
// But on a segmentation fault (as opposed to a paging fault),
// cr2 is not updated and the kernel sends an si_addr == 0.
// Be sure to use si_addr, not cr2.
- emu->cpu.trapva = (uint32_t)si->si_addr;
- memmove(mc->gregs, emu->trapenv->gregs, 19*4);
+ emu->cpu.trapva = (uint32_t)(uintptr_t)si->si_addr;
+ memmove(mc->gregs, emu->trapenv->gregs, sizeof emu->trapenv->gregs);
+
return 1;
}
diff --git a/src/libvx32/rts.S b/src/libvx32/rts.S
@@ -2,8 +2,8 @@
// Assembly-language runtime support for translated vx32 code.
//
-#include "asm.h"
-#include "os.h"
+#include "libvx32/asm.h"
+#include "libvx32/os.h"
// The method we use to get back out to host code differs for 32/64-bit hosts.
diff --git a/src/libvx32/run32.S b/src/libvx32/run32.S
@@ -2,8 +2,8 @@
// Assembly-language support code for vx32-to-x86-32 translation
//
-#include "asm.h"
-#include "os.h"
+#include "libvx32/asm.h"
+#include "libvx32/os.h"
.text
diff --git a/src/libvx32/run64.S b/src/libvx32/run64.S
@@ -2,8 +2,8 @@
// Assembly-language support code for vx32-to-x86-64 translation
//
-#include "asm.h"
-#include "os.h"
+#include "libvx32/asm.h"
+#include "libvx32/os.h"
.text
diff --git a/src/libvx32/sig.c b/src/libvx32/sig.c
@@ -69,7 +69,7 @@ int vxemu_sighandler(vxemu *emu, uint32_t trapeip)
// In vx32 runtime code (rts.S, run32/64.S), the registers are in flux.
// Single-step until we can get out; then they'll be safe to look at.
// This only makes sense if the trap is an external trap like a timer.
- char *eip = (char*)trapeip;
+ char *eip = (char*)(uintptr_t)trapeip;
if ((emu->cpu_trap&VXTRAP_CATEGORY) != VXTRAP_CPU){
// The check for run32/64.S doesn't look for the entire file.
// Instead it looks for anywhere in the file except
diff --git a/src/libvx32/vx32impl.h b/src/libvx32/vx32impl.h
@@ -251,5 +251,7 @@ void vxprint(char*, ...);
int vx32_sighandler(int, siginfo_t*, void*);
int vxemu_sighandler(vxemu*, uint32_t);
+#define USED(x) if(x){} /* shut up gcc not-used warning */
+
#endif // VX32_IMPL_H
diff --git a/src/libvx32/x86dis.c b/src/libvx32/x86dis.c
@@ -1700,11 +1700,11 @@ fmtarg(char *p, char *ep, xdarg *da, uint32_t npc)
break;
case DA_REL:
addr = da->disp + npc;
- if (addr == (uint32_t)vxrun_gentrap)
+ if (addr == (uint32_t)(uintptr_t)vxrun_gentrap)
p += snprintf(p, ep-p, "vxrun_gentrap");
- else if (addr == (uint32_t)vxrun_lookup_backpatch)
+ else if (addr == (uint32_t)(uintptr_t)vxrun_lookup_backpatch)
p += snprintf(p, ep-p, "vxrun_lookup_backpatch");
- else if (addr == (uint32_t)vxrun_lookup_indirect)
+ else if (addr == (uint32_t)(uintptr_t)vxrun_lookup_indirect)
p += snprintf(p, ep-p, "vxrun_lookup_indirect");
else
p += snprintf(p, ep-p, "%#x", da->disp + npc);