#2140 Bug in gcc 4.8.1 Ada run-time library

David Gressett

There is a bug in the gcc Ada 4.8.1 runtime which causes some routines
in the Ada.Calendar runtime package (and its child packages) to produce
incorrect results.

This can be demonstrated with the following Ada program:

with Ada.Calendar,

use Ada.Calendar,

procedure Simple_BugTest
Test_Date: Time;
YN: Year_number;
MN: Month_Number;
DN: Day_Number;
SN: Day_Duration;
ISN: Integer;

Test_Date := Time_Of(2013, 10, 3);
Split(Test_Date, YN, MN, DN, SN);
ISN := Integer(SN);
Put_Line("Time_Of(2013, 10, 3) is ");
Put("Year:" & Integer'Image(YN));
Put(", Month:" & Integer'Image(MN));
Put(", Day:" & Integer'Image(DN));
Put_Line(", Seconds:" & Integer'Image(ISN));
end Simple_BugTest;

When compiled with the MinGW 4.8.1-4 Ada compiler, which uses
the MinGW 4.0.3-1 runtime package, this produces the following
incorrect output:

Time_Of(2013, 10, 3) is
Year: 2013, Month: 9, Day: 29, Seconds: 68327

When compiled with an Ada runtime patch which corrects the
problem, the output becomes:

Time_Of(2013, 10, 3) is
Year: 2013, Month: 10, Day: 3, Seconds: 0

which is correct.

The bug which causes the problem is in the Ada runtime library
source code, found in the gcc/ada subdirectory of the compiler
source. It is caused by an incorrect assumption about time
types which is embedded in the Ada and C source code files
in the gcc/ada subdirectory.

The Ada calendar routines define the Ada version of the time_t type as

type time_t is range
-(2 (Standard'Address_Size - Integer'(1))) ..
(Standard'Address_Size - Integer'(1)) - 1);

The key item here is the Ada expression


The MinGW Ada compiler is a 32-bit compiler and this expression
evaluates to 32. The result is that the Ada time_t is a 32-bit
signed integer type. A value of this type is passed to the
C procedure __gnat_localtime_tzoff which is in a C file named
sysdep.c in the Ada runtime source code directory.

In the C code, the receiving argument of this Ada time_t type
is a C time_t type, the first argument of __gnat_localtime_tzoff,
declared as:

const time_t *timer

This is where the trouble starts. When the C time_t is a 64-bit
type, the value passed into __gnat_localtime_tzoff contains the
Ada time_t value as 32 bits of a 64-bit value, and the other 32
bits are nonsense.

In short, the Ada runtime library code is assuming that Ada
Standard'Address_Size and C sizeof(time_t) are always the same
value. The V4 release of the MinGW runtime has broken that assumption.

The patch works by defining a new time type for use in the
__gnat_localtime_tzoff function: ada_time_t.

This new type is based on the existing type intptr_t found in
<stdint.h> and is defined by the patch as

typedef intptr_t ada_time_t;

The offending time declaration is in the first argument of

__gnat_localtime_tzoff (const time_t timer,
const int
is_historic, long *off)

The patch corrects that to

__gnat_localtime_tzoff (const ada_time_t ada_timer,
const int
is_historic, long *off)

The patch defines a new local variable in __gnat_localtime_tzoff:

time_t timer;

The local variable is then initialized:

timer = (time_t) *ada_timer;

This line is where the bug is actually fixed. The 32-bit Ada time
type is properly extended to a 64-bit type, but it would just be
a trivial copy when compiled by a 64-bit compiler.

The new local variable timer then replaces all instances of *timer
in the body of the function __gnat_localtime_tzoff.

Note that a patch like this would work on any 32-bit platform which
introduced a new 64-bit time_t type in its C runtime. That is why I
did not base ada_time_t on a Windows-specific type; this patch is
a useful example for developers of Ada runtimes on other platforms.

1 Attachments