Re: [PATCH v3 3/3] system_data_types.7: note struct timespec::tv_nsec type for x32 and portability

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



[CC += Jakub, glibc]

Hi наб (and other readers),

On 12/6/21 23:03, наб wrote:
There are three files that govern userspace struct timespec on glibc:
1. bits/wordsize.h, defining:
    (a) __WORDSIZE to 32 on ILP32 and 64 on LP64
    (b) on x32: __SYSCALL_WORDSIZE to 64
2. bits/timesize.h, defining
    (a) __TIMESIZE to __WORDSIZE, except on x32 where it's 64
3. bits/types/struct_timespec.h, declaring struct timespec as:
      struct timespec
      {
       __time_t tv_sec;      /* Seconds.  */
      #if __WORDSIZE == 64 \
       || (defined __SYSCALL_WORDSIZE && __SYSCALL_WORDSIZE == 64) \
       || __TIMESIZE == 32
       __syscall_slong_t tv_nsec;    /* Nanoseconds.  */
      #else
      # if __BYTE_ORDER == __BIG_ENDIAN
       int: 32;           /* Padding.  */
       long int tv_nsec;  /* Nanoseconds.  */
      # else
       long int tv_nsec;  /* Nanoseconds.  */
       int: 32;           /* Padding.  */
      # endif
      #endif
      };
    this has two side-effects: struct timespec
    (a) is always sizeof==time_t+8, and
    (b) has tv_nsec as __syscall_slong_t
        *and* !is_same<__syscall_slong_t, long>
        if using LP64 syscalls on an ILP32 system, i.e. on x32.

This means, that the simplified
   struct timespec {
       time_t  tv_sec;  /* Seconds */
       long    tv_nsec; /* Nanoseconds [0 .. 999999999] */
   };
declaration is *invalid* for x32,
where struct timespec::tv_nsec is an int64_t (long long).

Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@xxxxxxxxxxxxxxxxxx>
---
The reasoning is explained in the commit message,
but I elucidated it in a more approachable way in the Notes,
which also include the link from Jakub Wilk upthread.


I (more or less) understand the deduction of why that happens on certain systems, what I was wondering is if there's an intention behind that, or if it's just an accident. Why not just use plain 'long' always as POSIX says it should be. I don't see the feature part of this bug.

Nevertheless, log2(10^(3*3) - 1) <= 31 is a good point!

Yes, that's what I really don't understand. 'long' is at least 32 bits, which is enough for 1G. Was there ever a good reason for glibc to use 64 bits? It seems like a wrong decision that is maintained for consistency. If that's it I'm in favor of mentioning it in a Bugs section (as Jakub proposed), instead of a Notes section. Maybe someone from glibc may give a rationale for this deviation from POSIX.

I also added this as a slight portability guide toward the end.

Nice.


This replaces 3/4 and 4/4.

Okay.


  man7/system_data_types.7 | 30 ++++++++++++++++++++++++++++++
  1 file changed, 30 insertions(+)

diff --git a/man7/system_data_types.7 b/man7/system_data_types.7
index 1e6a3f74c..cce17fc3e 100644
--- a/man7/system_data_types.7
+++ b/man7/system_data_types.7
@@ -1553,6 +1553,36 @@ Describes times in seconds and nanoseconds.
  .IR "Conforming to" :
  C11 and later; POSIX.1-2001 and later.
  .PP
+.IR Notes :
+.I tv_nsec
+is the
+.I syscall
+long, though this affects only fringe architectures like X32,
+which is ILP32, but uses the LP64 AMD64 syscall ABI.

I'd add an explicit mention that this is for glibc, since it deviates from the "Conforming to". Maybe preceding the paragraph with "In/On (I never knew if there goes in or on) glibc," would do.

+.br

We don't use '.br'. In this case I think just continuing in the same paragraph would be fine (i.e., no break at all).

+In reality, the field ends up being defined as:

Here I'd add a blank line with '.PP'.

+.EX
+.in +4

Please, revert the order of .in and .EX.
It's meaningless, but I prefer consistency. See man-pages(7), which shows this order, and also the following:

$ grep -rn '^\.EX$' -A1 man? | grep '\.in' | wc -l
2
$ grep -rn '^\.EX$' -B1 man? | grep '\.in' | wc -l
1048


One of those 2 cases was this patch.

+#if !(__x86_64__ && __ILP32__ /* == x32 */)

Now I notice:

Shouldn't this use defined()?

#if !(defined(__x86_64__) && defined(__ILP32__) /* == x32 */)

Also, I prefer to avoid complexity in mentally parsing the ifs, so I'd reorder it to remove the negation:

#if defined(__x86_64__) && defined(__ILP32__)  /* == x32 */
	long long tv_nsec;
#else
	long      tv_nsec;
#endif

And also note a cosmetic minor thing: at least two spaces before a comment.


Cheers,
Alex

+    long      tv_nsec;
+#else
+    long long tv_nsec;
+#endif
+.in
+.EE
+.PP
+This is a long-standing and long-enshrined
+.UR https://sourceware.org/bugzilla/show_bug.cgi?id=16437
+glibc bug
+.I #16437
+.UE ,
+an incompatible extension to the standards;
+however, as even a 32-bit
+.I long
+can hold the entire
+.I tv_nsec
+range, it's safe to forcibly down-cast it to the standard type.
+.PP
  .IR "See also" :
  .BR clock_gettime (2),
  .BR clock_nanosleep (2),



--
Alejandro Colomar
Linux man-pages comaintainer; https://www.kernel.org/doc/man-pages/
http://www.alejandro-colomar.es/



[Index of Archives]     [Kernel Documentation]     [Netdev]     [Linux Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux