Fix for Bug 3671 - QEMU GDB stub listens on IPv6-only port on Windows 7
The connection string used by the GDB stub does not specify which
version of the Internet Protocol should be used by the port on
which it listens. On host platforms with IPv6 support, such as
Windows 7, this means that the stub listens on an IPv6-only port.
Since the GDB client uses IPv4, this means that the client cannot
connect to QEMU.
/*
* qemu user main
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <machine/trap.h>
#include "qemu.h"
#include "qemu-common.h"
/* For tb_lock */
#include "exec-all.h"
#define DEBUG_LOGFILE "/tmp/qemu.log"
static const char *interp_prefix = CONFIG_QEMU_PREFIX;
const char *qemu_uname_release = CONFIG_UNAME_RELEASE;
extern char **environ;
/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
we allocate a bigger stack. Need a better solution, for example
by remapping the process stack directly at the right place */
unsigned long x86_stack_size = 512 * 1024;
void gemu_log(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
#ifdef TARGET_SPARC
#define SPARC64_STACK_BIAS 2047
//#define DEBUG_WIN
/* WARNING: dealing with register windows _is_ complicated. More info
can be found at http://www.sics.se/~psm/sparcstack.html */
static inline int get_reg_index(CPUSPARCState *env, int cwp, int index)
{
index = (index + cwp * 16) % (16 * env->nwindows);
/* wrap handling : if cwp is on the last window, then we use the
registers 'after' the end */
if (index < 8 && env->cwp == env->nwindows - 1)
index += 16 * env->nwindows;
return index;
}
/* save the register window 'cwp1' */
static inline void save_window_offset(CPUSPARCState *env, int cwp1)
{
unsigned int i;
abi_ulong sp_ptr;
sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)];
#ifdef TARGET_SPARC64
if (sp_ptr & 3)
sp_ptr += SPARC64_STACK_BIAS;
#endif
#if defined(DEBUG_WIN)
printf("win_overflow: sp_ptr=0x" TARGET_ABI_FMT_lx " save_cwp=%d\n",
sp_ptr, cwp1);
#endif
for(i = 0; i < 16; i++) {
/* FIXME - what to do if put_user() fails? */
put_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr);
sp_ptr += sizeof(abi_ulong);
}
}
static void save_window(CPUSPARCState *env)
{
#ifndef TARGET_SPARC64
unsigned int new_wim;
new_wim = ((env->wim >> 1) | (env->wim << (env->nwindows - 1))) &
((1LL << env->nwindows) - 1);
save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2));
env->wim = new_wim;
#else
save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2));
env->cansave++;
env->canrestore--;
#endif
}
static void restore_window(CPUSPARCState *env)
{
#ifndef TARGET_SPARC64
unsigned int new_wim;
#endif
unsigned int i, cwp1;
abi_ulong sp_ptr;
#ifndef TARGET_SPARC64
new_wim = ((env->wim << 1) | (env->wim >> (env->nwindows - 1))) &
((1LL << env->nwindows) - 1);
#endif
/* restore the invalid window */
cwp1 = cpu_cwp_inc(env, env->cwp + 1);
sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)];
#ifdef TARGET_SPARC64
if (sp_ptr & 3)
sp_ptr += SPARC64_STACK_BIAS;
#endif
#if defined(DEBUG_WIN)
printf("win_underflow: sp_ptr=0x" TARGET_ABI_FMT_lx " load_cwp=%d\n",
sp_ptr, cwp1);
#endif
for(i = 0; i < 16; i++) {
/* FIXME - what to do if get_user() fails? */
get_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr);
sp_ptr += sizeof(abi_ulong);
}
#ifdef TARGET_SPARC64
env->canrestore++;
if (env->cleanwin < env->nwindows - 1)
env->cleanwin++;
env->cansave--;
#else
env->wim = new_wim;
#endif
}
static void flush_windows(CPUSPARCState *env)
{
int offset, cwp1;
offset = 1;
for(;;) {
/* if restore would invoke restore_window(), then we can stop */
cwp1 = cpu_cwp_inc(env, env->cwp + offset);
#ifndef TARGET_SPARC64
if (env->wim & (1 << cwp1))
break;
#else
if (env->canrestore == 0)
break;
env->cansave++;
env->canrestore--;
#endif
save_window_offset(env, cwp1);
offset++;
}
cwp1 = cpu_cwp_inc(env, env->cwp + 1);
#ifndef TARGET_SPARC64
/* set wim so that restore will reload the registers */
env->wim = 1 << cwp1;
#endif
#if defined(DEBUG_WIN)
printf("flush_windows: nb=%d\n", offset - 1);
#endif
}
void cpu_loop(CPUSPARCState *env, enum BSDType bsd_type)
{
int trapnr, ret, syscall_nr;
//target_siginfo_t info;
while (1) {
trapnr = cpu_sparc_exec (env);
switch (trapnr) {
#ifndef TARGET_SPARC64
case 0x80:
#else
case 0x100:
#endif
syscall_nr = env->gregs[1];
if (bsd_type == target_freebsd)
ret = do_freebsd_syscall(env, syscall_nr,
env->regwptr[0], env->regwptr[1],
env->regwptr[2], env->regwptr[3],
env->regwptr[4], env->regwptr[5]);
else if (bsd_type == target_netbsd)
ret = do_netbsd_syscall(env, syscall_nr,
env->regwptr[0], env->regwptr[1],
env->regwptr[2], env->regwptr[3],
env->regwptr[4], env->regwptr[5]);
else { //if (bsd_type == target_openbsd)
#if defined(TARGET_SPARC64)
syscall_nr &= ~(TARGET_OPENBSD_SYSCALL_G7RFLAG |
TARGET_OPENBSD_SYSCALL_G2RFLAG);
#endif
ret = do_openbsd_syscall(env, syscall_nr,
env->regwptr[0], env->regwptr[1],
env->regwptr[2], env->regwptr[3],
env->regwptr[4], env->regwptr[5]);
}
if ((unsigned int)ret >= (unsigned int)(-515)) {
#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
env->xcc |= PSR_CARRY;
#else
env->psr |= PSR_CARRY;
#endif
} else {
#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
env->xcc &= ~PSR_CARRY;
#else
env->psr &= ~PSR_CARRY;
#endif
}
env->regwptr[0] = ret;
/* next instruction */
#if defined(TARGET_SPARC64)
if (bsd_type == target_openbsd &&
env->gregs[1] & TARGET_OPENBSD_SYSCALL_G2RFLAG) {
env->pc = env->gregs[2];
env->npc = env->pc + 4;
} else if (bsd_type == target_openbsd &&
env->gregs[1] & TARGET_OPENBSD_SYSCALL_G7RFLAG) {
env->pc = env->gregs[7];
env->npc = env->pc + 4;
} else {
env->pc = env->npc;
env->npc = env->npc + 4;
}
#else
env->pc = env->npc;
env->npc = env->npc + 4;
#endif
break;
case 0x83: /* flush windows */
#ifdef TARGET_ABI32
case 0x103:
#endif
flush_windows(env);
/* next instruction */
env->pc = env->npc;
env->npc = env->npc + 4;
break;
#ifndef TARGET_SPARC64
case TT_WIN_OVF: /* window overflow */
save_window(env);
break;
case TT_WIN_UNF: /* window underflow */
restore_window(env);
break;
case TT_TFAULT:
case TT_DFAULT:
#if 0
{
info.si_signo = SIGSEGV;
info.si_errno = 0;
/* XXX: check env->error_code */
info.si_code = TARGET_SEGV_MAPERR;
info._sifields._sigfault._addr = env->mmuregs[4];
queue_signal(env, info.si_signo, &info);
}
#endif
break;
#else
case TT_SPILL: /* window overflow */
save_window(env);
break;
case TT_FILL: /* window underflow */
restore_window(env);
break;
case TT_TFAULT:
case TT_DFAULT:
#if 0
{
info.si_signo = SIGSEGV;
info.si_errno = 0;
/* XXX: check env->error_code */
info.si_code = TARGET_SEGV_MAPERR;
if (trapnr == TT_DFAULT)
info._sifields._sigfault._addr = env->dmmuregs[4];
else
info._sifields._sigfault._addr = env->tsptr->tpc;
//queue_signal(env, info.si_signo, &info);
}
#endif
break;
#endif
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
break;
case EXCP_DEBUG:
{
int sig;
sig = gdb_handlesig (env, TARGET_SIGTRAP);
#if 0
if (sig)
{
info.si_signo = sig;
info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT;
//queue_signal(env, info.si_signo, &info);
}
#endif
}
break;
default:
printf ("Unhandled trap: 0x%x\n", trapnr);
cpu_dump_state(env, stderr, fprintf, 0);
exit (1);
}
process_pending_signals (env);
}
}
#endif
static void usage(void)
{
printf("qemu-" TARGET_ARCH " version " QEMU_VERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n"
"usage: qemu-" TARGET_ARCH " [options] program [arguments...]\n"
"BSD CPU emulator (compiled for %s emulation)\n"
"\n"
"Standard options:\n"
"-h print this help\n"
"-g port wait gdb connection to port\n"
"-L path set the elf interpreter prefix (default=%s)\n"
"-s size set the stack size in bytes (default=%ld)\n"
"-cpu model select CPU (-cpu ? for list)\n"
"-drop-ld-preload drop LD_PRELOAD for target process\n"
"-bsd type select emulated BSD type FreeBSD/NetBSD/OpenBSD (default)\n"
"\n"
"Debug options:\n"
"-d options activate log (logfile=%s)\n"
"-p pagesize set the host page size to 'pagesize'\n"
"-strace log system calls\n"
"\n"
"Environment variables:\n"
"QEMU_STRACE Print system calls and arguments similar to the\n"
" 'strace' program. Enable by setting to any value.\n"
,
TARGET_ARCH,
interp_prefix,
x86_stack_size,
DEBUG_LOGFILE);
_exit(1);
}
THREAD CPUState *thread_env;
/* Assumes contents are already zeroed. */
void init_task_state(TaskState *ts)
{
int i;
ts->used = 1;
ts->first_free = ts->sigqueue_table;
for (i = 0; i < MAX_SIGQUEUE_SIZE - 1; i++) {
ts->sigqueue_table[i].next = &ts->sigqueue_table[i + 1];
}
ts->sigqueue_table[i].next = NULL;
}
int main(int argc, char **argv)
{
const char *filename;
const char *cpu_model;
struct target_pt_regs regs1, *regs = ®s1;
struct image_info info1, *info = &info1;
TaskState ts1, *ts = &ts1;
CPUState *env;
int optind;
const char *r;
int gdbstub_port = 0;
int drop_ld_preload = 0, environ_count = 0;
char **target_environ, **wrk, **dst;
enum BSDType bsd_type = target_openbsd;
if (argc <= 1)
usage();
/* init debug */
cpu_set_log_filename(DEBUG_LOGFILE);
cpu_model = NULL;
optind = 1;
for(;;) {
if (optind >= argc)
break;
r = argv[optind];
if (r[0] != '-')
break;
optind++;
r++;
if (!strcmp(r, "-")) {
break;
} else if (!strcmp(r, "d")) {
int mask;
const CPULogItem *item;
if (optind >= argc)
break;
r = argv[optind++];
mask = cpu_str_to_log_mask(r);
if (!mask) {
printf("Log items (comma separated):\n");
for(item = cpu_log_items; item->mask != 0; item++) {
printf("%-10s %s\n", item->name, item->help);
}
exit(1);
}
cpu_set_log(mask);
} else if (!strcmp(r, "s")) {
r = argv[optind++];
x86_stack_size = strtol(r, (char **)&r, 0);
if (x86_stack_size <= 0)
usage();
if (*r == 'M')
x86_stack_size *= 1024 * 1024;
else if (*r == 'k' || *r == 'K')
x86_stack_size *= 1024;
} else if (!strcmp(r, "L")) {
interp_prefix = argv[optind++];
} else if (!strcmp(r, "p")) {
qemu_host_page_size = atoi(argv[optind++]);
if (qemu_host_page_size == 0 ||
(qemu_host_page_size & (qemu_host_page_size - 1)) != 0) {
fprintf(stderr, "page size must be a power of two\n");
exit(1);
}
} else if (!strcmp(r, "g")) {
gdbstub_port = atoi(argv[optind++]);
} else if (!strcmp(r, "r")) {
qemu_uname_release = argv[optind++];
} else if (!strcmp(r, "cpu")) {
cpu_model = argv[optind++];
if (strcmp(cpu_model, "?") == 0) {
/* XXX: implement xxx_cpu_list for targets that still miss it */
#if defined(cpu_list)
cpu_list(stdout, &fprintf);
#endif
_exit(1);
}
} else if (!strcmp(r, "drop-ld-preload")) {
drop_ld_preload = 1;
} else if (!strcmp(r, "bsd")) {
if (!strcasecmp(argv[optind], "freebsd")) {
bsd_type = target_freebsd;
} else if (!strcasecmp(argv[optind], "netbsd")) {
bsd_type = target_netbsd;
} else if (!strcasecmp(argv[optind], "openbsd")) {
bsd_type = target_openbsd;
} else {
usage();
}
optind++;
} else if (!strcmp(r, "strace")) {
do_strace = 1;
} else
{
usage();
}
}
if (optind >= argc)
usage();
filename = argv[optind];
/* Zero out regs */
memset(regs, 0, sizeof(struct target_pt_regs));
/* Zero out image_info */
memset(info, 0, sizeof(struct image_info));
/* Scan interp_prefix dir for replacement files. */
init_paths(interp_prefix);
if (cpu_model == NULL) {
#if defined(TARGET_SPARC)
#ifdef TARGET_SPARC64
cpu_model = "TI UltraSparc II";
#else
cpu_model = "Fujitsu MB86904";
#endif
#else
cpu_model = "any";
#endif
}
cpu_exec_init_all(0);
/* NOTE: we need to init the CPU at this stage to get
qemu_host_page_size */
env = cpu_init(cpu_model);
if (!env) {
fprintf(stderr, "Unable to find CPU definition\n");
exit(1);
}
thread_env = env;
if (getenv("QEMU_STRACE")) {
do_strace = 1;
}
wrk = environ;
while (*(wrk++))
environ_count++;
target_environ = malloc((environ_count + 1) * sizeof(char *));
if (!target_environ)
abort();
for (wrk = environ, dst = target_environ; *wrk; wrk++) {
if (drop_ld_preload && !strncmp(*wrk, "LD_PRELOAD=", 11))
continue;
*(dst++) = strdup(*wrk);
}
*dst = NULL; /* NULL terminate target_environ */
if (loader_exec(filename, argv+optind, target_environ, regs, info) != 0) {
printf("Error loading %s\n", filename);
_exit(1);
}
for (wrk = target_environ; *wrk; wrk++) {
free(*wrk);
}
free(target_environ);
if (loglevel) {
page_dump(logfile);
fprintf(logfile, "start_brk 0x" TARGET_ABI_FMT_lx "\n", info->start_brk);
fprintf(logfile, "end_code 0x" TARGET_ABI_FMT_lx "\n", info->end_code);
fprintf(logfile, "start_code 0x" TARGET_ABI_FMT_lx "\n",
info->start_code);
fprintf(logfile, "start_data 0x" TARGET_ABI_FMT_lx "\n",
info->start_data);
fprintf(logfile, "end_data 0x" TARGET_ABI_FMT_lx "\n", info->end_data);
fprintf(logfile, "start_stack 0x" TARGET_ABI_FMT_lx "\n",
info->start_stack);
fprintf(logfile, "brk 0x" TARGET_ABI_FMT_lx "\n", info->brk);
fprintf(logfile, "entry 0x" TARGET_ABI_FMT_lx "\n", info->entry);
}
target_set_brk(info->brk);
syscall_init();
signal_init();
/* build Task State */
memset(ts, 0, sizeof(TaskState));
init_task_state(ts);
ts->info = info;
env->opaque = ts;
env->user_mode_only = 1;
#if defined(TARGET_SPARC)
{
int i;
env->pc = regs->pc;
env->npc = regs->npc;
env->y = regs->y;
for(i = 0; i < 8; i++)
env->gregs[i] = regs->u_regs[i];
for(i = 0; i < 8; i++)
env->regwptr[i] = regs->u_regs[i + 8];
}
#else
#error unsupported target CPU
#endif
if (gdbstub_port) {
gdbserver_start (gdbstub_port);
gdb_handlesig(env, 0);
}
cpu_loop(env, bsd_type);
/* never exits */
return 0;
}