This is the mail archive of the
gdb-prs@sources.redhat.com
mailing list for the GDB project.
pending/1387: [RFA] sh-tdep.c: Follow up patch to implement two different ABIs
- From: Corinna Vinschen <vinschen at redhat dot com>
- To: gdb-gnats at sources dot redhat dot com
- Date: Wed, 17 Sep 2003 18:11:27 +0200
- Subject: pending/1387: [RFA] sh-tdep.c: Follow up patch to implement two different ABIs
- Reply-to: gdb-patches at sources dot redhat dot com
>Number: 1387
>Category: pending
>Synopsis: [RFA] sh-tdep.c: Follow up patch to implement two different ABIs
>Confidential: yes
>Severity: serious
>Priority: medium
>Responsible: unassigned
>State: open
>Class: change-request
>Submitter-Id: unknown
>Arrival-Date: Sat Sep 20 21:38:01 UTC 2003
>Closed-Date:
>Last-Modified:
>Originator:
>Release:
>Organization:
>Environment:
>Description:
Hi,
the below patch is a follow up patch which relies on the pending sh frame
stuff patch. This patch now adds the ability to switch between two
different ABIs on the gdb command line, the "gcc" and the "renesas" ABI.
The patch also implements the differences between these ABIs which changes
the way how arguments are passed and values are returned. Additionally it
fixes the argument passing of doubles in the gcc ABI on sh variations with
FPU. The changes in detail:
- Add the commands `set calling_convention <foo>' and
`show calling_convention' with <foo> being one of "gcc" and "renesas".
Default to "gcc".
- Differ between a fpu and a nofpu version of sh_use_struct_convention.
The nofpu version behaves like the fpu version, except that it uses
struct convention also for all types >= 8 bytes when the Renesas ABI
is active. This especially affects long longs and doubles which are
then passed on the stack.
- The Renesas ABI passes the address in which structs have to be returned
not in r2 as the gcc ABI, but instead this return address is pushed
onto the stack after all arguments have been pushed. This affects
sh_extract_struct_value_address() as well as the sh_push_dummy_call*()
functions.
- To simplify the sh_push_dummy_call*() functions, I created two helper
functions which took the part of right-justifying values < 4 byte,
called sh_justify_value_in_reg(), and to count the number of bytes
to be allocated on stack for all arguments, called sh_stack_allocsize().
- Two new functions are now used to evaluate the next floating point
register to use for an argument. The tricky part with floats is
that gcc and Renesas ABI are different in two situations:
- gcc passes floats in little-endian mode using regsters criss-crossing,
fr5, fr4, fr7, fr6, fr9, fr8 ... In big-endian mode or in Renesas
ABI, the order is simple fr4, fr5, fr6, ...
- In both ABIs doubles are passed in even register pairs, fr4/fr5,
fr6/fr7, ... The difference is when a register is skipped to pass
the next double. Example:
void foo(float a, double b, float c);
In gcc ABI, a is passed in fr4, b then skips the odd-numbered fr5
register, so it's passed in fr6/fr7 and c is then passed in the next
free register, fr8.
In Renesas ABI, a is passed in fr4, b is passed as in gcc ABI in
fr6/fr7 but c is passed in the lowest unused register, in this example
in fr5.
- In the Renesas ABI for CPUs with FPU, long longs are not passed in
registers but on stack.
- The struct_return code in both sh_push_dummy_call*() functions is moved
to the end of the function since it must be done after the argument
passing for the Rensas ABI.
- I renamed the flag `odd_sized_struct' to a more descriptive name,
`pass_on_stack' since the old name does in no way reflect how the
decision about passing on stack or in register is actually made. The
actual decision is as follows:
- On FPU CPUs, pass in registers unless the datatype is bigger than
16 bytes. Renesas ABI additionally passes long longs on the stack
as well.
- On non-FPU CPUs, doubles and long doubles are passed always on the stack.
- On all CPUs, everything else is passed in registers until the argument
registers are filled up, the remaining arguments are passed on stack.
If an argument doesn't fit entirely in the remaining registers, it's
split between regs and stack as see fit.
- The code and comment that some data is sometimes passed in registers
*and* stack simultaneously is dropped. That's not how it works.
Seems to be something sh5 specific but that's now in sh64-tdep.c.
- Also in sh_push_dummy_call*(), the code which adds 4 for every 4 bytes
managed, is changed from e.g.
len -= register_size (gdbarch, argreg);
to
len -= 4;
The reason is that the original line is not exactly correct. It adds the
register_size of the *next* register, not the register actually filled
with data. Since all data and registers in question are 4 byte regs (and
the target specific code should know that anyway), I've simplified the
affected code.
Corinna
ChangeLog:
==========
* sh-tdep.c (sh_cc_gcc): New static string.
(sh_cc_renesas): Ditto.
(sh_cc_enum): New array pointing to calling convention strings.
(sh_active_calling_convention): New variable pointing to
current active calling convention.
(sh_use_struct_convention_fpu): New function.
(sh_use_struct_convention_nofpu): New function.
(sh_use_struct_convention): Remove. Superseeded by the previous
two functions.
(sh_extract_struct_value_address): Care for Renesas ABI.
(sh_justify_value_in_reg): New function.
(sh_stack_allocsize): Ditto.
(flt_argreg_array): New array used for floating point argument
passing.
(sh_init_flt_argreg): New function.
(sh_next_flt_argreg): Ditto.
(sh_push_dummy_call_fpu): Simplify. Rename "odd_sized_struct" to
"pass_on_stack". Use new helper functions. Accomodate Renesas ABI.
Fix argument passing strategy.
(sh_push_dummy_call_nofpu): Ditto.
(sh_gdbarch_init): Accomodate new sh_use_struct_convention_fpu and
sh_use_struct_convention_nofpu functions.
(_initialize_sh_tdep): Initialize new "set calling_convention",
"show calling_convention" commands.
--- sh-tdep.c.AFTERPATCH2 2003-09-15 17:14:33.000000000 +0200
+++ sh-tdep.c 2003-09-16 18:17:39.000000000 +0200
@@ -55,6 +55,17 @@
/* registers numbers shared with the simulator */
#include "gdb/sim-sh.h"
+static const char sh_cc_gcc[] = "gcc";
+static const char sh_cc_renesas[] = "renesas";
+static const char *sh_cc_enum[] =
+{
+ sh_cc_gcc,
+ sh_cc_renesas,
+ NULL
+};
+
+static const char *sh_active_calling_convention = sh_cc_gcc;
+
static void (*sh_show_regs) (void);
#define SH_NUM_REGS 59
@@ -569,10 +580,25 @@ sh_skip_prologue (CORE_ADDR start_pc)
/* Should call_function allocate stack space for a struct return? */
static int
-sh_use_struct_convention (int gcc_p, struct type *type)
+sh_use_struct_convention_fpu (int gcc_p, struct type *type)
+{
+ int len = TYPE_LENGTH (type);
+ int nelem = TYPE_NFIELDS (type);
+
+ return ((len != 1 && len != 2 && len != 4 && len != 8) || nelem != 1) &&
+ (len != 8 || TYPE_LENGTH (TYPE_FIELD_TYPE (type, 0)) != 4);
+}
+
+static int
+sh_use_struct_convention_nofpu (int gcc_p, struct type *type)
{
int len = TYPE_LENGTH (type);
int nelem = TYPE_NFIELDS (type);
+
+ /* The Renesas ABI returns long longs/doubles etc. always on stack. */
+ if (sh_active_calling_convention == sh_cc_renesas && len >= 8)
+ return 1;
+
return ((len != 1 && len != 2 && len != 4 && len != 8) || nelem != 1) &&
(len != 8 || TYPE_LENGTH (TYPE_FIELD_TYPE (type, 0)) != 4);
}
@@ -584,8 +610,13 @@ static CORE_ADDR
sh_extract_struct_value_address (struct regcache *regcache)
{
ULONGEST addr;
-
- regcache_cooked_read_unsigned (regcache, STRUCT_RETURN_REGNUM, &addr);
+ if (sh_active_calling_convention != sh_cc_renesas)
+ regcache_cooked_read_unsigned (regcache, STRUCT_RETURN_REGNUM, &addr);
+ else
+ {
+ regcache_cooked_read_unsigned (regcache, SP_REGNUM, &addr);
+ addr = read_memory_unsigned_integer (addr, 4);
+ }
return addr;
}
@@ -647,6 +678,102 @@ sh_frame_align (struct gdbarch *ignore,
not displace any of the other arguments passed in via registers R4
to R7. */
+/* Helper function to justify value in register according to endianess. */
+static char *
+sh_justify_value_in_reg (struct value *val, int len)
+{
+ static char valbuf[4];
+
+ memset (valbuf, 0, sizeof (valbuf));
+ if (len < 4)
+ {
+ /* value gets right-justified in the register or stack word */
+ if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
+ memcpy (valbuf + (4 - len), (char *) VALUE_CONTENTS (val), len);
+ else
+ memcpy (valbuf, (char *) VALUE_CONTENTS (val), len);
+ return valbuf;
+ }
+ return (char *) VALUE_CONTENTS (val);
+}
+
+/* Helper function to eval number of bytes to allocate on stack. */
+static CORE_ADDR
+sh_stack_allocsize (int nargs, struct value **args)
+{
+ int stack_alloc = 0;
+ while (nargs-- > 0)
+ stack_alloc += ((TYPE_LENGTH (VALUE_TYPE (args[nargs])) + 3) & ~3);
+ return stack_alloc;
+}
+
+/* Helper functions for getting the float arguments right. Registers usage
+ depends on the ABI and the endianess. The comments should enlighten how
+ it's intended to work. */
+
+/* This array stores which of the float arg registers are already in use. */
+static int flt_argreg_array[FLOAT_ARGLAST_REGNUM - FLOAT_ARG0_REGNUM + 1];
+
+/* This function just resets the above array to "no reg used so far". */
+static void
+sh_init_flt_argreg (void)
+{
+ memset (flt_argreg_array, 0, sizeof flt_argreg_array);
+}
+
+/* This function returns the next register to use for float arg passing.
+ It returns either a valid value between FLOAT_ARG0_REGNUM and
+ FLOAT_ARGLAST_REGNUM if a register is available, otherwise it returns
+ FLOAT_ARGLAST_REGNUM + 1 to indicate that no register is available.
+
+ Note that register number 0 in flt_argreg_array corresponds with the
+ real float register fr4. In contrast to FLOAT_ARG0_REGNUM (value is
+ 29) the parity of the register number is preserved, which is important
+ for the double register passing test (see the "argreg & 1" test below). */
+static int
+sh_next_flt_argreg (int len)
+{
+ int argreg;
+
+ /* First search for the next free register. */
+ for (argreg = 0; argreg <= FLOAT_ARGLAST_REGNUM - FLOAT_ARG0_REGNUM; ++argreg)
+ if (!flt_argreg_array[argreg])
+ break;
+
+ /* No register left? */
+ if (argreg > FLOAT_ARGLAST_REGNUM - FLOAT_ARG0_REGNUM)
+ return FLOAT_ARGLAST_REGNUM + 1;
+
+ if (len == 8)
+ {
+ /* Doubles are always starting in a even register number. */
+ if (argreg & 1)
+ {
+ /* In gcc ABI, the skipped register is lost for further argument
+ passing now. Not so in Renesas ABI. */
+ if (sh_active_calling_convention != sh_cc_renesas)
+ flt_argreg_array[argreg] = 1;
+
+ ++argreg;
+
+ /* No register left? */
+ if (argreg > FLOAT_ARGLAST_REGNUM - FLOAT_ARG0_REGNUM)
+ return FLOAT_ARGLAST_REGNUM + 1;
+ }
+ /* Also mark the next register as used. */
+ flt_argreg_array[argreg + 1] = 1;
+ }
+ else if (TARGET_BYTE_ORDER == BFD_ENDIAN_LITTLE
+ && sh_active_calling_convention != sh_cc_renesas)
+ {
+ /* In little endian, gcc passes floats like this: f5, f4, f7, f6, ... */
+ if (!flt_argreg_array[argreg + 1])
+ ++argreg;
+ }
+ flt_argreg_array[argreg] = 1;
+ return FLOAT_ARG0_REGNUM + argreg;
+}
+
static CORE_ADDR
sh_push_dummy_call_fpu (struct gdbarch *gdbarch,
CORE_ADDR func_addr,
@@ -656,77 +783,61 @@ sh_push_dummy_call_fpu (struct gdbarch *
CORE_ADDR sp, int struct_return,
CORE_ADDR struct_addr)
{
- int stack_offset, stack_alloc;
- int argreg, flt_argreg;
+ int stack_offset = 0;
+ int argreg = ARG0_REGNUM;
+ int flt_argreg;
int argnum;
struct type *type;
CORE_ADDR regval;
char *val;
- char valbuf[4];
int len;
- int odd_sized_struct;
+ int pass_on_stack;
/* first force sp to a 4-byte alignment */
sp = sh_frame_align (gdbarch, sp);
- if (struct_return)
- regcache_cooked_write_unsigned (regcache,
- STRUCT_RETURN_REGNUM,
- struct_addr);
-
- /* Now make sure there's space on the stack */
- for (argnum = 0, stack_alloc = 0; argnum < nargs; argnum++)
- stack_alloc += ((TYPE_LENGTH (VALUE_TYPE (args[argnum])) + 3) & ~3);
- sp -= stack_alloc; /* make room on stack for args */
+ /* make room on stack for args */
+ sp -= sh_stack_allocsize (nargs, args);
+
+ /* Initialize float argument mechanism. */
+ sh_init_flt_argreg ();
/* Now load as many as possible of the first arguments into
registers, and push the rest onto the stack. There are 16 bytes
in four registers available. Loop thru args from first to last. */
-
- argreg = ARG0_REGNUM;
- flt_argreg = FLOAT_ARG0_REGNUM;
- for (argnum = 0, stack_offset = 0; argnum < nargs; argnum++)
+ for (argnum = 0; argnum < nargs; argnum++)
{
type = VALUE_TYPE (args[argnum]);
len = TYPE_LENGTH (type);
- memset (valbuf, 0, sizeof (valbuf));
- if (len < 4)
- {
- /* value gets right-justified in the register or stack word */
- if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
- memcpy (valbuf + (4 - len),
- (char *) VALUE_CONTENTS (args[argnum]), len);
- else
- memcpy (valbuf, (char *) VALUE_CONTENTS (args[argnum]), len);
- val = valbuf;
- }
- else
- val = (char *) VALUE_CONTENTS (args[argnum]);
+ val = sh_justify_value_in_reg (args[argnum], len);
+
+ /* Some decisions have to be made how various types are handled.
+ This also differs in different ABIs. */
+ pass_on_stack = 0;
+ if (len > 16)
+ pass_on_stack = 1; /* Types bigger than 16 bytes are passed on stack. */
+ else if (sh_active_calling_convention == sh_cc_renesas
+ && TYPE_CODE (type) == TYPE_CODE_INT && len == 8)
+ pass_on_stack = 1; /* So are long longs in renesas ABI */
+
+ /* Find out the next register to use for a floating point value. */
+ if (TYPE_CODE (type) == TYPE_CODE_FLT)
+ flt_argreg = sh_next_flt_argreg (len);
- if (len > 4 && (len & 3) != 0)
- odd_sized_struct = 1; /* Such structs go entirely on stack. */
- else if (len > 16)
- odd_sized_struct = 1; /* So do aggregates bigger than 4 words. */
- else
- odd_sized_struct = 0;
while (len > 0)
{
if ((TYPE_CODE (type) == TYPE_CODE_FLT
&& flt_argreg > FLOAT_ARGLAST_REGNUM)
|| argreg > ARGLAST_REGNUM
- || odd_sized_struct)
+ || pass_on_stack)
{
- /* must go on the stack */
write_memory (sp + stack_offset, val, 4);
stack_offset += 4;
}
- /* NOTE WELL!!!!! This is not an "else if" clause!!!
- That's because some *&^%$ things get passed on the stack
- AND in the registers! */
- if (TYPE_CODE (type) == TYPE_CODE_FLT &&
- flt_argreg > 0 && flt_argreg <= FLOAT_ARGLAST_REGNUM)
+ else if (TYPE_CODE (type) == TYPE_CODE_FLT
+ && flt_argreg <= FLOAT_ARGLAST_REGNUM)
{
- /* Argument goes in a single-precision fp reg. */
+ /* Argument goes in a float argument register. */
regval = extract_unsigned_integer (val, register_size (gdbarch,
argreg));
regcache_cooked_write_unsigned (regcache, flt_argreg++, regval);
@@ -741,11 +852,25 @@ sh_push_dummy_call_fpu (struct gdbarch *
/* Store the value 4 bytes at a time. This means that things
larger than 4 bytes may go partly in registers and partly
on the stack. */
- len -= register_size (gdbarch, argreg);
- val += register_size (gdbarch, argreg);
+ len -= 4;
+ val += 4;
}
}
+ if (struct_return)
+ {
+ if (sh_active_calling_convention != sh_cc_renesas)
+ /* Using the gcc ABI, the "struct return pointer" pseudo-argument has
+ its own dedicated register */
+ regcache_cooked_write_unsigned (regcache,
+ STRUCT_RETURN_REGNUM,
+ struct_addr);
+ else
+ /* If the function uses the renesas ABI, subtract another 4 bytes from
+ the stack and store the struct return address there. */
+ write_memory_unsigned_integer (sp -= 4, 4, struct_addr);
+ }
+
/* Store return address. */
regcache_cooked_write_unsigned (regcache, PR_REGNUM, bp_addr);
@@ -764,69 +889,45 @@ sh_push_dummy_call_nofpu (struct gdbarch
CORE_ADDR sp, int struct_return,
CORE_ADDR struct_addr)
{
- int stack_offset, stack_alloc;
- int argreg;
+ int stack_offset = 0;
+ int argreg = ARG0_REGNUM;
int argnum;
struct type *type;
CORE_ADDR regval;
char *val;
- char valbuf[4];
int len;
- int odd_sized_struct;
+ int pass_on_stack;
/* first force sp to a 4-byte alignment */
sp = sh_frame_align (gdbarch, sp);
- if (struct_return)
- regcache_cooked_write_unsigned (regcache,
- STRUCT_RETURN_REGNUM,
- struct_addr);
-
- /* Now make sure there's space on the stack */
- for (argnum = 0, stack_alloc = 0; argnum < nargs; argnum++)
- stack_alloc += ((TYPE_LENGTH (VALUE_TYPE (args[argnum])) + 3) & ~3);
- sp -= stack_alloc; /* make room on stack for args */
+ /* make room on stack for args */
+ sp -= sh_stack_allocsize (nargs, args);
/* Now load as many as possible of the first arguments into
registers, and push the rest onto the stack. There are 16 bytes
in four registers available. Loop thru args from first to last. */
-
- argreg = ARG0_REGNUM;
- for (argnum = 0, stack_offset = 0; argnum < nargs; argnum++)
+ for (argnum = 0; argnum < nargs; argnum++)
{
type = VALUE_TYPE (args[argnum]);
len = TYPE_LENGTH (type);
- memset (valbuf, 0, sizeof (valbuf));
- if (len < 4)
- {
- /* value gets right-justified in the register or stack word */
- if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
- memcpy (valbuf + (4 - len),
- (char *) VALUE_CONTENTS (args[argnum]), len);
- else
- memcpy (valbuf, (char *) VALUE_CONTENTS (args[argnum]), len);
- val = valbuf;
- }
- else
- val = (char *) VALUE_CONTENTS (args[argnum]);
+ val = sh_justify_value_in_reg (args[argnum], len);
+
+ /* Some decisions have to be made how various types are handled.
+ This also differs in different ABIs. */
+ pass_on_stack = 0;
+ if (TYPE_CODE (type) == TYPE_CODE_FLT && len > 4
+ && sh_active_calling_convention == sh_cc_renesas)
+ pass_on_stack = 1; /* Renesas ABI pushes doubles entirely on stack. */
- if (len > 4 && (len & 3) != 0)
- odd_sized_struct = 1; /* such structs go entirely on stack */
- else
- odd_sized_struct = 0;
while (len > 0)
{
- if (argreg > ARGLAST_REGNUM
- || odd_sized_struct)
+ if (argreg > ARGLAST_REGNUM || pass_on_stack)
{
- /* must go on the stack */
write_memory (sp + stack_offset, val, 4);
stack_offset += 4;
}
- /* NOTE WELL!!!!! This is not an "else if" clause!!!
- That's because some *&^%$ things get passed on the stack
- AND in the registers! */
- if (argreg <= ARGLAST_REGNUM)
+ else if (argreg <= ARGLAST_REGNUM)
{
/* there's room in a register */
regval = extract_unsigned_integer (val, register_size (gdbarch,
@@ -836,11 +937,25 @@ sh_push_dummy_call_nofpu (struct gdbarch
/* Store the value 4 bytes at a time. This means that things
larger than 4 bytes may go partly in registers and partly
on the stack. */
- len -= register_size (gdbarch, argreg);
- val += register_size (gdbarch, argreg);
+ len -= 4;
+ val += 4;
}
}
+ if (struct_return)
+ {
+ if (sh_active_calling_convention != sh_cc_renesas)
+ /* Using the gcc ABI, the "struct return pointer" pseudo-argument has
+ its own dedicated register */
+ regcache_cooked_write_unsigned (regcache,
+ STRUCT_RETURN_REGNUM,
+ struct_addr);
+ else
+ /* If the function uses the renesas ABI, subtract another 4 bytes from
+ the stack and store the struct return address there. */
+ write_memory_unsigned_integer (sp -= 4, 4, struct_addr);
+ }
+
/* Store return address. */
regcache_cooked_write_unsigned (regcache, PR_REGNUM, bp_addr);
@@ -2060,7 +2175,6 @@ sh_gdbarch_init (struct gdbarch_info inf
set_gdbarch_print_registers_info (gdbarch, sh_print_registers_info);
set_gdbarch_breakpoint_from_pc (gdbarch, sh_breakpoint_from_pc);
- set_gdbarch_use_struct_convention (gdbarch, sh_use_struct_convention);
set_gdbarch_print_insn (gdbarch, gdb_print_insn_sh);
set_gdbarch_register_sim_regno (gdbarch, legacy_register_sim_regno);
@@ -2079,6 +2193,7 @@ sh_gdbarch_init (struct gdbarch_info inf
set_gdbarch_push_dummy_code (gdbarch, sh_push_dummy_code);
set_gdbarch_push_dummy_call (gdbarch, sh_push_dummy_call_nofpu);
+ set_gdbarch_use_struct_convention (gdbarch, sh_use_struct_convention_nofpu);
set_gdbarch_frame_args_skip (gdbarch, 0);
set_gdbarch_frameless_function_invocation (gdbarch,
@@ -2114,6 +2229,7 @@ sh_gdbarch_init (struct gdbarch_info inf
set_gdbarch_store_return_value (gdbarch, sh3e_sh4_store_return_value);
set_gdbarch_extract_return_value (gdbarch, sh3e_sh4_extract_return_value);
set_gdbarch_push_dummy_call (gdbarch, sh_push_dummy_call_fpu);
+ set_gdbarch_use_struct_convention (gdbarch, sh_use_struct_convention_fpu);
break;
case bfd_mach_sh_dsp:
@@ -2135,6 +2251,7 @@ sh_gdbarch_init (struct gdbarch_info inf
set_gdbarch_store_return_value (gdbarch, sh3e_sh4_store_return_value);
set_gdbarch_extract_return_value (gdbarch, sh3e_sh4_extract_return_value);
set_gdbarch_push_dummy_call (gdbarch, sh_push_dummy_call_fpu);
+ set_gdbarch_use_struct_convention (gdbarch, sh_use_struct_convention_fpu);
break;
case bfd_mach_sh3_dsp:
@@ -2152,6 +2269,7 @@ sh_gdbarch_init (struct gdbarch_info inf
set_gdbarch_store_return_value (gdbarch, sh3e_sh4_store_return_value);
set_gdbarch_extract_return_value (gdbarch, sh3e_sh4_extract_return_value);
set_gdbarch_push_dummy_call (gdbarch, sh_push_dummy_call_fpu);
+ set_gdbarch_use_struct_convention (gdbarch, sh_use_struct_convention_fpu);
break;
default:
@@ -2178,4 +2296,11 @@ _initialize_sh_tdep (void)
gdbarch_register (bfd_arch_sh, sh_gdbarch_init, NULL);
add_com ("regs", class_vars, sh_show_regs_command, "Print all registers");
+
+ add_show_from_set (
+ add_set_enum_cmd ("calling_convention", class_vars, sh_cc_enum,
+ &sh_active_calling_convention,
+ "Set calling convention used when calling target "
+ "functions from GDB.",
+ &setlist), &showlist);
}
--
Corinna Vinschen
Cygwin Developer
Red Hat, Inc.
>How-To-Repeat:
>Fix:
>Release-Note:
>Audit-Trail:
>Unformatted: