Message ID | 1291812551-12590-1-git-send-email-pbonzini@redhat.com |
---|---|
State | New |
Headers | show |
On 12/08/2010 01:49 PM, Paolo Bonzini wrote: > Signed-off-by: Paolo Bonzini<pbonzini@redhat.com> > --- > I had this patch lying around but I don't think I ever got > qruncom to work completely. > > Makefile.target | 3 ++ > tests/Makefile | 7 ++-- > tests/qruncom.c | 93 +++++++++++++++++++++++++++++++++++------------------- > 3 files changed, 67 insertions(+), 36 deletions(-) > > diff --git a/Makefile.target b/Makefile.target > index 5784844..4ac8f6f 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -339,6 +339,9 @@ obj-y += $(addprefix ../libdis/, $(libdis-y)) > obj-y += $(libobj-y) > obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y)) > > +else # !CONFIG_SOFTMMU > +libqemu.a: $(addprefix ../, $(common-obj-y)) $(libobj-y) $(addprefix ../libdis/, $(libdis-y)) > + ar rc $@ $^ > endif # CONFIG_SOFTMMU > > obj-y += $(addprefix ../, $(trace-obj-y)) > diff --git a/tests/Makefile b/tests/Makefile > index e43ec70..6dbeb6f 100644 > --- a/tests/Makefile > +++ b/tests/Makefile > @@ -116,9 +116,10 @@ speed: sha1 sha1-i386 > > # broken test > # NOTE: -fomit-frame-pointer is currently needed : this is a bug in libqemu > -qruncom: qruncom.c ../ioport-user.c ../i386-user/libqemu.a > - $(CC) $(CFLAGS) -fomit-frame-pointer $(LDFLAGS) -I../target-i386 -I.. -I../i386-user -I../fpu \ > - -o $@ $(filter %.c, $^) -L../i386-user -lqemu -lm > +qruncom: qruncom.c > + #$(MAKE) -C ../i386-linux-user libqemu.a > + $(CC) $(CFLAGS) -fomit-frame-pointer $(LDFLAGS) -I../target-i386 -I.. -I../linux-user -I../i386-linux-user -I../fpu \ > + -o $@ $(filter %.c, $^) -L../i386-linux-user -lqemu -lm > > # arm test > hello-arm: hello-arm.o > diff --git a/tests/qruncom.c b/tests/qruncom.c > index 079f7a2..66fc223 100644 > --- a/tests/qruncom.c > +++ b/tests/qruncom.c > @@ -12,10 +12,68 @@ > #include<signal.h> > #include<malloc.h> > > +#define NEED_CPU_H 1 > #include "cpu.h" > > //#define SIGTEST > > +unsigned long guest_base = 0; > +int have_guest_base = 0; > +int singlestep = 0; > +unsigned long last_brk = 0; > + > +void cpu_outb(uint32_t addr, uint8_t val) > +{ > + fprintf(stderr, "outb: port=0x%04"PRIx32", data=%02"PRIx8"\n", > + addr, val); > +} > + > +void cpu_outw(uint32_t addr, uint16_t val) > +{ > + fprintf(stderr, "outw: port=0x%04"PRIx32", data=%04"PRIx16"\n", > + addr, val); > +} > + > +void cpu_outl(uint32_t addr, uint32_t val) > +{ > + fprintf(stderr, "outl: port=0x%04"PRIx32", data=%08"PRIx32"\n", > + addr, val); > +} > + > +uint8_t cpu_inb(uint32_t addr) > +{ > + fprintf(stderr, "inb: port=0x%04"PRIx32"\n", addr); > + return 0; > +} > + > +uint16_t cpu_inw(uint32_t addr) > +{ > + fprintf(stderr, "inw: port=0x%04"PRIx32"\n", addr); > + return 0; > +} > + > +uint32_t cpu_inl(uint32_t addr) > +{ > + fprintf(stderr, "inl: port=0x%04"PRIx32"\n", addr); > + return 0; > +} > + > +void cpu_list_lock(void) > +{ > +} > + > +void cpu_list_unlock(void) > +{ > +} > + > +void mmap_lock(void) > +{ > +} > + > +void mmap_unlock(void) > +{ > +} > + > int cpu_get_pic_interrupt(CPUState *env) > { > return -1; > @@ -44,26 +102,6 @@ static void set_idt(int n, unsigned int dpl) > set_gate(idt_table + n, 0, dpl, 0, 0); > } > > -void qemu_free(void *ptr) > -{ > - free(ptr); > -} > - > -void *qemu_malloc(size_t size) > -{ > - return malloc(size); > -} > - > -void *qemu_mallocz(size_t size) > -{ > - void *ptr; > - ptr = qemu_malloc(size); > - if (!ptr) > - return NULL; > - memset(ptr, 0, size); > - return ptr; > -} > - > void *qemu_vmalloc(size_t size) > { > return memalign(4096, size); > @@ -74,17 +112,6 @@ void qemu_vfree(void *ptr) > free(ptr); > } > > -void qemu_printf(const char *fmt, ...) > -{ > - va_list ap; > - va_start(ap, fmt); > - vprintf(fmt, ap); > - va_end(ap); > -} > - > -/* XXX: this is a bug in helper2.c */ > -int errno; > - > /**********************************************/ > > #define COM_BASE_ADDR 0x10100 > @@ -99,7 +126,7 @@ static void usage(void) > > static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg) > { > - return (uint8_t *)((seg<< 4) + (reg& 0xffff)); > + return (uint8_t *)(uintptr_t) ((seg<< 4) + (reg& 0xffff)); > } > > static inline void pushw(CPUState *env, int val) > @@ -241,7 +268,7 @@ int main(int argc, char **argv) > case EXCP0D_GPF: > { > int int_num, ah; > - int_num = *(uint8_t *)(env->segs[R_CS].base + env->eip + 1); > + int_num = *(uint8_t *)(uintptr_t) (env->segs[R_CS].base + env->eip + 1); > if (int_num != 0x21) > goto unknown_int; > ah = (env->regs[R_EAX]>> 8)& 0xff; Hi! Thank you for your help! I've linked qemu-malloc.o and cutils.o together with qruncom.c and I managed to succesfully make it! here the make line: > #$(MAKE) -C ../i386-linux-user libqemu.a > $(CC) $(CFLAGS) -fomit-frame-pointer $(LDFLAGS) -I../target-i386 > -I.. -I../linux-user -I../i386-linux-user -I../fpu \ > -o $@ ../qemu-malloc.o ../cutils.o $(filter %.c, $^) > -L../i386-linux-user -lqemu -lm Anyway running it with a com file as argument gave the error: > mmap: Operation not permitted I think the problem is with "MAP_FIXED" parameter in mmap (http://opengroup.org/onlinepubs/007908799/xsh/mmap.html) having chosen 0x00000000 as starting address.. but it is pretty difficult for me atm to understand it, I've never used this function before and I am a beginner in these topics Removing that parameter mmap succeeds, but then I get "segmentation fault" in cpu_init Any idea? Thank you in advance! Stefano B.
What is the reason to duplicate cpu_{in,out}[bwl]() instead of ioport-user.c? On Wed, Dec 08, 2010 at 01:49:11PM +0100, Paolo Bonzini wrote: > Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> > --- > I had this patch lying around but I don't think I ever got > qruncom to work completely. > > Makefile.target | 3 ++ > tests/Makefile | 7 ++-- > tests/qruncom.c | 93 +++++++++++++++++++++++++++++++++++------------------- > 3 files changed, 67 insertions(+), 36 deletions(-) > > diff --git a/Makefile.target b/Makefile.target > index 5784844..4ac8f6f 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -339,6 +339,9 @@ obj-y += $(addprefix ../libdis/, $(libdis-y)) > obj-y += $(libobj-y) > obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y)) > > +else # !CONFIG_SOFTMMU > +libqemu.a: $(addprefix ../, $(common-obj-y)) $(libobj-y) $(addprefix ../libdis/, $(libdis-y)) > + ar rc $@ $^ > endif # CONFIG_SOFTMMU > > obj-y += $(addprefix ../, $(trace-obj-y)) > diff --git a/tests/Makefile b/tests/Makefile > index e43ec70..6dbeb6f 100644 > --- a/tests/Makefile > +++ b/tests/Makefile > @@ -116,9 +116,10 @@ speed: sha1 sha1-i386 > > # broken test > # NOTE: -fomit-frame-pointer is currently needed : this is a bug in libqemu > -qruncom: qruncom.c ../ioport-user.c ../i386-user/libqemu.a > - $(CC) $(CFLAGS) -fomit-frame-pointer $(LDFLAGS) -I../target-i386 -I.. -I../i386-user -I../fpu \ > - -o $@ $(filter %.c, $^) -L../i386-user -lqemu -lm > +qruncom: qruncom.c > + #$(MAKE) -C ../i386-linux-user libqemu.a > + $(CC) $(CFLAGS) -fomit-frame-pointer $(LDFLAGS) -I../target-i386 -I.. -I../linux-user -I../i386-linux-user -I../fpu \ > + -o $@ $(filter %.c, $^) -L../i386-linux-user -lqemu -lm > > # arm test > hello-arm: hello-arm.o > diff --git a/tests/qruncom.c b/tests/qruncom.c > index 079f7a2..66fc223 100644 > --- a/tests/qruncom.c > +++ b/tests/qruncom.c > @@ -12,10 +12,68 @@ > #include <signal.h> > #include <malloc.h> > > +#define NEED_CPU_H 1 > #include "cpu.h" > > //#define SIGTEST > > +unsigned long guest_base = 0; > +int have_guest_base = 0; > +int singlestep = 0; > +unsigned long last_brk = 0; > + > +void cpu_outb(uint32_t addr, uint8_t val) > +{ > + fprintf(stderr, "outb: port=0x%04"PRIx32", data=%02"PRIx8"\n", > + addr, val); > +} > + > +void cpu_outw(uint32_t addr, uint16_t val) > +{ > + fprintf(stderr, "outw: port=0x%04"PRIx32", data=%04"PRIx16"\n", > + addr, val); > +} > + > +void cpu_outl(uint32_t addr, uint32_t val) > +{ > + fprintf(stderr, "outl: port=0x%04"PRIx32", data=%08"PRIx32"\n", > + addr, val); > +} > + > +uint8_t cpu_inb(uint32_t addr) > +{ > + fprintf(stderr, "inb: port=0x%04"PRIx32"\n", addr); > + return 0; > +} > + > +uint16_t cpu_inw(uint32_t addr) > +{ > + fprintf(stderr, "inw: port=0x%04"PRIx32"\n", addr); > + return 0; > +} > + > +uint32_t cpu_inl(uint32_t addr) > +{ > + fprintf(stderr, "inl: port=0x%04"PRIx32"\n", addr); > + return 0; > +} > + > +void cpu_list_lock(void) > +{ > +} > + > +void cpu_list_unlock(void) > +{ > +} > + > +void mmap_lock(void) > +{ > +} > + > +void mmap_unlock(void) > +{ > +} > + > int cpu_get_pic_interrupt(CPUState *env) > { > return -1; > @@ -44,26 +102,6 @@ static void set_idt(int n, unsigned int dpl) > set_gate(idt_table + n, 0, dpl, 0, 0); > } > > -void qemu_free(void *ptr) > -{ > - free(ptr); > -} > - > -void *qemu_malloc(size_t size) > -{ > - return malloc(size); > -} > - > -void *qemu_mallocz(size_t size) > -{ > - void *ptr; > - ptr = qemu_malloc(size); > - if (!ptr) > - return NULL; > - memset(ptr, 0, size); > - return ptr; > -} > - > void *qemu_vmalloc(size_t size) > { > return memalign(4096, size); > @@ -74,17 +112,6 @@ void qemu_vfree(void *ptr) > free(ptr); > } > > -void qemu_printf(const char *fmt, ...) > -{ > - va_list ap; > - va_start(ap, fmt); > - vprintf(fmt, ap); > - va_end(ap); > -} > - > -/* XXX: this is a bug in helper2.c */ > -int errno; > - > /**********************************************/ > > #define COM_BASE_ADDR 0x10100 > @@ -99,7 +126,7 @@ static void usage(void) > > static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg) > { > - return (uint8_t *)((seg << 4) + (reg & 0xffff)); > + return (uint8_t *)(uintptr_t) ((seg << 4) + (reg & 0xffff)); > } > > static inline void pushw(CPUState *env, int val) > @@ -241,7 +268,7 @@ int main(int argc, char **argv) > case EXCP0D_GPF: > { > int int_num, ah; > - int_num = *(uint8_t *)(env->segs[R_CS].base + env->eip + 1); > + int_num = *(uint8_t *)(uintptr_t) (env->segs[R_CS].base + env->eip + 1); > if (int_num != 0x21) > goto unknown_int; > ah = (env->regs[R_EAX] >> 8) & 0xff; > -- > 1.7.3.2 > >
On 12/09/2010 04:32 AM, Isaku Yamahata wrote: > What is the reason to duplicate cpu_{in,out}[bwl]() instead of > ioport-user.c? That's the reason why I hadn't submitted the patch so far, it's not really finished. I also wanted to remove the need for mmap(MAP_FIXED). Paolo
On 12/08/2010 10:43 PM, Stefano Bonifazi wrote: > I've linked qemu-malloc.o and cutils.o together with qruncom.c and I > managed to succesfully make it! > here the make line: >> #$(MAKE) -C ../i386-linux-user libqemu.a >> $(CC) $(CFLAGS) -fomit-frame-pointer $(LDFLAGS) -I../target-i386 -I.. >> -I../linux-user -I../i386-linux-user -I../fpu \ >> -o $@ ../qemu-malloc.o ../cutils.o $(filter %.c, $^) >> -L../i386-linux-user -lqemu -lm > > Anyway running it with a com file as argument gave the error: >> mmap: Operation not permitted > I think the problem is with "MAP_FIXED" parameter in mmap > (http://opengroup.org/onlinepubs/007908799/xsh/mmap.html) having chosen > 0x00000000 as starting address.. but it is pretty difficult for me atm > to understand it, I've never used this function before and I am a > beginner in these topics > Removing that parameter mmap succeeds, but then I get "segmentation > fault" in cpu_init You have to run it as root I think. Paolo
On 12/09/2010 08:16 AM, Paolo Bonzini wrote: > On 12/08/2010 10:43 PM, Stefano Bonifazi wrote: >> >> Anyway running it with a com file as argument gave the error: >>> mmap: Operation not permitted >> > > You have to run it as root I think. > > Paolo Thank you! Running as root worked, though it raises then the following error (from gdb) I am currently trying to understand: > /home/stefano/LinuxDev/qemu-0.12.5/tcg/tcg.c:1367: tcg fatal error > > Program received signal SIGABRT, Aborted. > 0x0012e416 in __kernel_vsyscall () Surely any hint on how to to fix this will be very welcome :) I wish I could understand also what was wrong before, I mean /mmap/.. I understand you can't babysit me, but the gap between what one studies at university and the real world is very big and I feel lost :( I've read pretty much about mmap trying to figure out myself but understanding how to map a file (what I could find in every article about mmap online) is not the same as understanding how it works inside QEMU .. I know each process gets its own logical address space, if I understood fine mmap should take a portion of qruncom address space and give it to the emulator that should then see that as its own address space (please correct me if I am wrong!) .. Now if I got fine the flag MAP_FIXED, obliges the process to give that portion of address space starting at its /addr/ parameter (the first).. or if it is not possible to give an error.. My big doubt is how can the process give exactly that portion of address space starting at zero by just running it as root?.. I am expecting that area of address space to be taken by I dunno, code, data of the process itself.. honestly I don't know how things are allocated when a process is run(and I wish I could learn that).. but how can one think that addresses around zero are free for a mapping?? I'll appreciate very much any explanation, or links where to learn those topics! :) Thank you very much! Best Regards! Stefano B.
On 12/09/2010 06:29 PM, Stefano Bonifazi wrote:
> how can one think that addresses around zero are free for a mapping??
Addresses around zero are always free, because if they weren't you
couldn't detect NULL pointer dereferences reliably.
mmap-ing at zero thus is a tricky operation, because it removes the
possibility to detect NULL pointer dereferences. What's worse, such
ability would be lost even for _kernel_ dereferences of NULL, thus
opening a large security hole for privilege-escalation or kernel
exploits. So, mmap-ing addresses close to zero is restricted to root.
Paolo
On 12/10/2010 09:53 AM, Paolo Bonzini wrote: > On 12/09/2010 06:29 PM, Stefano Bonifazi wrote: >> how can one think that addresses around zero are free for a mapping?? > > Addresses around zero are always free, because if they weren't you > couldn't detect NULL pointer dereferences reliably. > > mmap-ing at zero thus is a tricky operation, because it removes the > possibility to detect NULL pointer dereferences. What's worse, such > ability would be lost even for _kernel_ dereferences of NULL, thus > opening a large security hole for privilege-escalation or kernel > exploits. So, mmap-ing addresses close to zero is restricted to root. > > Paolo Hi! Thank you! Very clear explanation! :) - So why can't I simply change the following: > vm86_mem = mmap((void *)0x00000000, 0x110000, > PROT_WRITE | PROT_READ | PROT_EXEC, > MAP_FIXED|MAP_ANON | MAP_PRIVATE, -1, 0); > page_set_flags(0x00000000, 0x110000, > PAGE_WRITE | PAGE_READ | PAGE_EXEC | PAGE_VALID); into something like: >> vm86_mem = mmap((void *)0x00000000, 0x110000, >> PROT_WRITE | PROT_READ | PROT_EXEC, >> MAP_ANON | MAP_PRIVATE, -1, 0); >> page_set_flags(vm86_mem, 0x110000+vm86_mem, >> PAGE_WRITE | PAGE_READ | PAGE_EXEC | PAGE_VALID); ? - Any luck with the tcg fatal error? I am trying to understand how tcg works for fixing the error.. but it is so complicated! :) Thank You again! Best Regards! Stefano B.
-----Original Message----- From: Paolo Bonzini [mailto:pbonzini@redhat.com] Sent: venerdì 10 dicembre 2010 22:49 To: Stefano Bonifazi Subject: Re: [PATCH] fix qruncom compilation problems >For runcom (without the "q") this wouldn't work, because it runs the code in vm86 mode. It's possible that this is ok for qruncom with other >changes to let the TCG backend know about vm86_mem. So informing the interpreter with >> page_set_flags(0x00000000+vm86_mem, 0x110000+vm86_mem, PAGE_WRITE | PAGE_READ | >> PAGE_EXEC | PAGE_VALID); Would be not enough? >I was looking at this in my free time and it seriously shrunk later, so I'm afraid I cannot help. Surely I do understand you! Your help has been very very useful and appreciated already thank you! May you direct me to somebody who's working on it? Some TCG guru who could understand immediately what's wrong? :) I noticed, far now, that each question on this mailing list is answered only by one QEMU developer, is that a sort of policy or just a coincidence? Again thank you! Best regards! Stefano B.
On 12/11/2010 03:42 PM, Stefano Bonifazi wrote: > Surely I do understand you! Your help has been very very useful and > appreciated already thank you! May you direct me to somebody who's working > on it? Some TCG guru who could understand immediately what's wrong?:) > I noticed, far now, that each question on this mailing list is answered > only by one QEMU developer, is that a sort of policy or just a coincidence? It's a coincidence. :) Paolo
diff --git a/Makefile.target b/Makefile.target index 5784844..4ac8f6f 100644 --- a/Makefile.target +++ b/Makefile.target @@ -339,6 +339,9 @@ obj-y += $(addprefix ../libdis/, $(libdis-y)) obj-y += $(libobj-y) obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y)) +else # !CONFIG_SOFTMMU +libqemu.a: $(addprefix ../, $(common-obj-y)) $(libobj-y) $(addprefix ../libdis/, $(libdis-y)) + ar rc $@ $^ endif # CONFIG_SOFTMMU obj-y += $(addprefix ../, $(trace-obj-y)) diff --git a/tests/Makefile b/tests/Makefile index e43ec70..6dbeb6f 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -116,9 +116,10 @@ speed: sha1 sha1-i386 # broken test # NOTE: -fomit-frame-pointer is currently needed : this is a bug in libqemu -qruncom: qruncom.c ../ioport-user.c ../i386-user/libqemu.a - $(CC) $(CFLAGS) -fomit-frame-pointer $(LDFLAGS) -I../target-i386 -I.. -I../i386-user -I../fpu \ - -o $@ $(filter %.c, $^) -L../i386-user -lqemu -lm +qruncom: qruncom.c + #$(MAKE) -C ../i386-linux-user libqemu.a + $(CC) $(CFLAGS) -fomit-frame-pointer $(LDFLAGS) -I../target-i386 -I.. -I../linux-user -I../i386-linux-user -I../fpu \ + -o $@ $(filter %.c, $^) -L../i386-linux-user -lqemu -lm # arm test hello-arm: hello-arm.o diff --git a/tests/qruncom.c b/tests/qruncom.c index 079f7a2..66fc223 100644 --- a/tests/qruncom.c +++ b/tests/qruncom.c @@ -12,10 +12,68 @@ #include <signal.h> #include <malloc.h> +#define NEED_CPU_H 1 #include "cpu.h" //#define SIGTEST +unsigned long guest_base = 0; +int have_guest_base = 0; +int singlestep = 0; +unsigned long last_brk = 0; + +void cpu_outb(uint32_t addr, uint8_t val) +{ + fprintf(stderr, "outb: port=0x%04"PRIx32", data=%02"PRIx8"\n", + addr, val); +} + +void cpu_outw(uint32_t addr, uint16_t val) +{ + fprintf(stderr, "outw: port=0x%04"PRIx32", data=%04"PRIx16"\n", + addr, val); +} + +void cpu_outl(uint32_t addr, uint32_t val) +{ + fprintf(stderr, "outl: port=0x%04"PRIx32", data=%08"PRIx32"\n", + addr, val); +} + +uint8_t cpu_inb(uint32_t addr) +{ + fprintf(stderr, "inb: port=0x%04"PRIx32"\n", addr); + return 0; +} + +uint16_t cpu_inw(uint32_t addr) +{ + fprintf(stderr, "inw: port=0x%04"PRIx32"\n", addr); + return 0; +} + +uint32_t cpu_inl(uint32_t addr) +{ + fprintf(stderr, "inl: port=0x%04"PRIx32"\n", addr); + return 0; +} + +void cpu_list_lock(void) +{ +} + +void cpu_list_unlock(void) +{ +} + +void mmap_lock(void) +{ +} + +void mmap_unlock(void) +{ +} + int cpu_get_pic_interrupt(CPUState *env) { return -1; @@ -44,26 +102,6 @@ static void set_idt(int n, unsigned int dpl) set_gate(idt_table + n, 0, dpl, 0, 0); } -void qemu_free(void *ptr) -{ - free(ptr); -} - -void *qemu_malloc(size_t size) -{ - return malloc(size); -} - -void *qemu_mallocz(size_t size) -{ - void *ptr; - ptr = qemu_malloc(size); - if (!ptr) - return NULL; - memset(ptr, 0, size); - return ptr; -} - void *qemu_vmalloc(size_t size) { return memalign(4096, size); @@ -74,17 +112,6 @@ void qemu_vfree(void *ptr) free(ptr); } -void qemu_printf(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); -} - -/* XXX: this is a bug in helper2.c */ -int errno; - /**********************************************/ #define COM_BASE_ADDR 0x10100 @@ -99,7 +126,7 @@ static void usage(void) static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg) { - return (uint8_t *)((seg << 4) + (reg & 0xffff)); + return (uint8_t *)(uintptr_t) ((seg << 4) + (reg & 0xffff)); } static inline void pushw(CPUState *env, int val) @@ -241,7 +268,7 @@ int main(int argc, char **argv) case EXCP0D_GPF: { int int_num, ah; - int_num = *(uint8_t *)(env->segs[R_CS].base + env->eip + 1); + int_num = *(uint8_t *)(uintptr_t) (env->segs[R_CS].base + env->eip + 1); if (int_num != 0x21) goto unknown_int; ah = (env->regs[R_EAX] >> 8) & 0xff;
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- I had this patch lying around but I don't think I ever got qruncom to work completely. Makefile.target | 3 ++ tests/Makefile | 7 ++-- tests/qruncom.c | 93 +++++++++++++++++++++++++++++++++++------------------- 3 files changed, 67 insertions(+), 36 deletions(-)