[v2] Y2038: add function __localtime64

Message ID 20181212155707.2191-1-albert.aribaud@3adev.fr
State New, archived
Headers

Commit Message

Albert ARIBAUD Dec. 12, 2018, 3:57 p.m. UTC
  Tested with 'make check' on x86_64-linux-gnu and i686-linux.gnu.

	* include/time.h
	(__localtime64): Add.
        * manual/maint.texi: Document Y2038 symbol handling.
	* time/localtime.c
	(__localtime64): Add.
	[__TIMERSIZE != 64] (__localtime): Turn into a wrapper.
---
v2: fix 'Y2038' => @dfn{Y2038} in manual/maint.texi

 include/time.h    |   9 +++-
 manual/maint.texi | 122 ++++++++++++++++++++++++++++++++++++++++++++++
 time/localtime.c  |  18 +++++--
 3 files changed, 145 insertions(+), 4 deletions(-)
  

Comments

Joseph Myers Dec. 12, 2018, 5:09 p.m. UTC | #1
On Wed, 12 Dec 2018, Albert ARIBAUD (3ADEV) wrote:

>  extern struct tm *__localtime_r (const time_t *__timer,
>  				 struct tm *__tp) attribute_hidden;
> -

I don't think this blank line removal should be included in this patch.

> +In order to make @theglibc{} Y2038-proof, 64-bit time support must be
> +added to those 32-bit-time-only architectures. When adding new code

Two spaces after '.' in Texinfo source, throughout.

> +@item
> +Y2038 support code added to @theglibc{} must not modify or remove
> +existing symbols on 32-bit time architectures. This ensures that
> +existing user object code will remain able to link against the newer
> +Y2038-proof @glibcadj{}.

@glibcadj{} is incorrect here, this is not an adjective use.

> +aleady Y2038-proof.

"already"

> +is supporte (@code{time_t} is 64-bit, @code{time()} returns a 64-bit time

"supported"

Do not use () after a function name to indicate it's a function (see the 
GNU Coding Standards).

> +/* Provide a 32-bit variant if needed */

Comments, both in code and in the manual, should follow GNU standards, so 
end with ".  " (full stop, two spaces).

>  /* The C Standard says that localtime and gmtime return the same pointer.  */
>  struct tm _tmbuf;
>  
> -
>  /* Return the `struct tm' representation of *T in local time,
>     using *TP to store the result.  */
>  struct tm *
> @@ -31,11 +30,24 @@ __localtime_r (const time_t *t, struct tm *tp)
>  }
>  weak_alias (__localtime_r, localtime_r)
>  
> -
>  /* Return the `struct tm' representation of *T in local time.  */

Again, I think the patch should avoid stray removals of blank lines.
  
Albert ARIBAUD Dec. 12, 2018, 5:37 p.m. UTC | #2
Hi Joseph,

On Wed, 12 Dec 2018 17:09:22 +0000, Joseph Myers
<joseph@codesourcery.com> wrote :

> > +@item
> > +Y2038 support code added to @theglibc{} must not modify or remove
> > +existing symbols on 32-bit time architectures. This ensures that
> > +existing user object code will remain able to link against the newer
> > +Y2038-proof @glibcadj{}.  
> 
> @glibcadj{} is incorrect here, this is not an adjective use.

I would need a noun form here, and manual/macros.texi does not provide
one. Should I add a @macro definition for glibcnoun there and use it in
the paragraph above?

Cordialement,
Albert ARIBAUD
3ADEV
  
Joseph Myers Dec. 12, 2018, 5:56 p.m. UTC | #3
On Wed, 12 Dec 2018, Albert ARIBAUD wrote:

> Hi Joseph,
> 
> On Wed, 12 Dec 2018 17:09:22 +0000, Joseph Myers
> <joseph@codesourcery.com> wrote :
> 
> > > +@item
> > > +Y2038 support code added to @theglibc{} must not modify or remove
> > > +existing symbols on 32-bit time architectures. This ensures that
> > > +existing user object code will remain able to link against the newer
> > > +Y2038-proof @glibcadj{}.  
> > 
> > @glibcadj{} is incorrect here, this is not an adjective use.
> 
> I would need a noun form here, and manual/macros.texi does not provide
> one. Should I add a @macro definition for glibcnoun there and use it in
> the paragraph above?

I think this paragraph would better be rewritten anyway, so avoiding the 
need for a new macro.

Rather than writing the text as being about a change to glibc, and so 
talking about "existing" symbols or object code and "newer" glibc, it 
might better be written in terms of there being two kinds of glibc 
configurations: those that only support 64-bit time_t, and those that 
support both 32-bit and 64-bit time_t.  (With a note, to be removed later, 
that the latter are a work in progress, so that external interfaces for 
64-bit time_t are not yet enabled on such configurations.)

Then you can explain that for the configurations with only 64-bit time_t, 
there are no variant symbol names or function definitions for different 
time_t choices (with the consequent internal #defines, etc.) - whereas on 
configurations with both 32-bit and 64-bit time_t, time-related functions 
generally have two definitions, one being a wrapper round the other (and 
the symbol names not containing a "64" are the ones for 32-bit time_t).

This avoids needing to apply descriptions like "newer" or "Y2038-proof" to 
"GNU C Library" (although such descriptions might still be relevant to 
apply to functions).

With this structure, the fact that configurations with both 32-bit and 
64-bit time_t are ones that used to have only 32-bit time_t is essentially 
a historical note that explains why a particular design is used.  At the 
level of individual symbols, it may not even be true that one is the old 
symbol and one is the new one; it's entirely plausible that new 
time-related functions may be added after the 64-bit time_t support for 
such platforms is complete (but before 32-bit time_t support is completely 
obsoleted and the relevant symbols made into compat symbols), and so would 
get both versions of the symbol added to glibc at the same time.
  

Patch

diff --git a/include/time.h b/include/time.h
index 37964f7b76..251a2b0329 100644
--- a/include/time.h
+++ b/include/time.h
@@ -56,9 +56,16 @@  extern time_t __mktime_internal (struct tm *__tp,
 				 struct tm *(*__func) (const time_t *,
 						       struct tm *),
 				 long int *__offset) attribute_hidden;
+
+#if __TIMESIZE == 64
+# define __localtime64 localtime
+#else
+extern struct tm *__localtime64 (const __time64_t *__timer);
+libc_hidden_proto (__localtime64)
+#endif
+
 extern struct tm *__localtime_r (const time_t *__timer,
 				 struct tm *__tp) attribute_hidden;
-
 extern struct tm *__gmtime_r (const time_t *__restrict __timer,
 			      struct tm *__restrict __tp);
 libc_hidden_proto (__gmtime_r)
diff --git a/manual/maint.texi b/manual/maint.texi
index fce06bfa88..49be3621a0 100644
--- a/manual/maint.texi
+++ b/manual/maint.texi
@@ -5,6 +5,7 @@ 
 @menu
 * Source Layout::         How to add new functions or header files
                              to the GNU C Library.
+* Symbol handling::       How to handle symbols in the GNU C Library.
 * Porting::               How to port the GNU C Library to
                              a new machine or operating system.
 @end menu
@@ -183,6 +184,127 @@  header file in the machine-specific directory, e.g.,
 @file{sysdeps/powerpc/sys/platform/ppc.h}.
 
 
+@node Symbol handling
+@appendixsec Symbol handling in the GNU C Library
+
+@menu
+* 64-bit time symbol handling :: How to handle 64-bit time related
+                                    symbols in the GNU C Library.
+@end menu
+
+@node 64-bit time symbol handling
+@appendixsubsec 64-bit time symbol handling in the GNU C Library
+
+Some of the architectures supported by @theglibc{} already use 64-bit
+time (@code{__TIMESIZE == 64}), while other architectures use 32-bit time
+(@code{__TIMESIZE != 64}) and therefore cannot handle dates beyond
+2038-01-19 03:14:07 (aka @dfn{Y2038}).
+
+In order to make @theglibc{} Y2038-proof, 64-bit time support must be
+added to those 32-bit-time-only architectures. When adding new code
+for Y2038 support, some principles must be followed:
+
+@itemize @bullet
+
+@item
+Y2038 support code added to @theglibc{} must not modify or remove
+existing symbols on 32-bit time architectures. This ensures that
+existing user object code will remain able to link against the newer
+Y2038-proof @glibcadj{}.
+
+@item
+Y2038 support code added to @theglibc{} must not add any new symbol
+when building on 64-bit time architectures when existing symbols are
+aleady Y2038-proof.
+
+@item
+In public headers, @theglibc{} supports either 64- or 32-bit time with
+a single set of names. On 64-bit time architectures, only 64-bit time
+is supporte (@code{time_t} is 64-bit, @code{time()} returns a 64-bit time
+etc); on 32-bit architectures, 32-bit time is supported (@code{time_t}
+is 32-bit, @code{time()} returns a 32-bit time etc) unless
+@code{_TIME_BITS} is defined equal to @code{64} before including
+headers from @theglibc{}, in which case 64-bit time is supported.
+
+@end itemize
+
+In order to simplify Y2038 support, all Y2038 proofing follows the
+same mechanism: for each non-Y2038-proof 32-bit-time function, a
+Y2038-proof 64-bit-time version is created, and the 32-bit-time function
+is rewritten as a wrapper around the 64-bit-time function.
+
+Here is an example with @code{localtime}:
+
+Function @code{localtime} is declared in @file{time/time.h} as
+@smallexample
+extern struct tm *localtime (const time_t *__timer) __THROW;
+libc_hidden_proto (localtime)
+@end smallexample
+
+On 64-bit time architectures, we declare @code{__localtime64} to be a
+macro which evaluates to @code{localtime}, so that
+@smallexample
+extern struct tm *__localtime64 (const __time64_t *__timer) __THROW;
+@end smallexample
+evaluates to
+@smallexample
+extern struct tm *localtime (const time_t *__timer) __THROW;
+@end smallexample
+
+(for 64-bit-time architectures, @code{__time64_t} is declared as a
+macro which evaluates to @code{time_t})
+
+On 32-bit time architectures, we declare @code{__localtime64} similar
+to @code{localtime} except it uses Y2038-proof types:
+@smallexample
+#if __TIMESIZE == 64
+# define __localtime64 localtime
+#else
+extern struct tm *__localtime64 (const __time64_t *__timer) __THROW;
+libc_hidden_proto (__localtime64)
+#endif
+@end smallexample
+
+(type @code{time_t} is replaced with @code{__time64_t} because
+@code{time_t} is not Y2038-proof, whereas @code{struct tm} is not
+replaced because it is already Y2038-proof.)
+
+The implementation of @code{localtime} is replaced as follows:
+
+@smallexample
+struct tm *
+__localtime64 (const __time64_t *t)
+(
+  return __tz_convert (*t, 1, &_tmbuf);
+)
+libc_hidden_def (__localtime64)
+
+/* Provide a 32-bit variant if needed */
+
+#if __TIMESIZE != 64
+
+struct tm *
+localtime (const time_t *t)
+(
+  __time64_t t64 = *t;
+  return __localtime64 (&t64);
+)
+libc_hidden_def (localtime)
+
+#endif
+@end smallexample
+
+For 64-bit-time architectures, @code{__localtime64} is a macro which
+evaluates to @code{localtime} and @code{__time64} is a macro which
+evaluates to @code{time_t}, so the first definition above amounts to
+the original definition of @code{localtime}; and since @code{__TIMESIZE}
+equals 64, the second definition is removed by the preprocessor.
+
+For 32-bit architectures, the first definition creates the new function
+@code{__localtime64} which uses Y2038-proof types, and since
+@code{__TIMESIZE} is different from 64, the second definition provides
+@code{localtime} as a wrapper around @code{__localtime64}.
+
 @node Porting
 @appendixsec Porting @theglibc{}
 
diff --git a/time/localtime.c b/time/localtime.c
index 92dbfe0f8c..50e0be2272 100644
--- a/time/localtime.c
+++ b/time/localtime.c
@@ -21,7 +21,6 @@ 
 /* The C Standard says that localtime and gmtime return the same pointer.  */
 struct tm _tmbuf;
 
-
 /* Return the `struct tm' representation of *T in local time,
    using *TP to store the result.  */
 struct tm *
@@ -31,11 +30,24 @@  __localtime_r (const time_t *t, struct tm *tp)
 }
 weak_alias (__localtime_r, localtime_r)
 
-
 /* Return the `struct tm' representation of *T in local time.  */
 struct tm *
-localtime (const time_t *t)
+__localtime64 (const __time64_t *t)
 {
   return __tz_convert (*t, 1, &_tmbuf);
 }
+libc_hidden_def (__localtime64)
+
+/* Provide a 32-bit variant if needed */
+
+#if __TIMESIZE != 64
+
+struct tm *
+localtime (const time_t *t)
+{
+  __time64_t t64 = *t;
+  return __localtime64 (&t64);
+}
 libc_hidden_def (localtime)
+
+#endif