1

I have below piece of code in my module and im compiling it with ICC compiler.

/**
 * Static (compile-time) assertion.
 * Basically, use COND to dimension an array.  If COND is false/zero the
 * array size will be -1 and we'll get a compilation error.
 */

    #define STATIC_ASSERT(COND) \
       do { \
          (void) sizeof(char [1 - 2*!(COND)]); \
       } while (0)
     #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + \
                         (((off_t) 1 << 31) << 31)) 
       STATIC_ASSERT(LARGE_OFF_T % 2147483629 == 721 &&
                     LARGE_OFF_T % 2147483647 == 1);

the above assertion fails with array size must be grater than 0.

Can anyone help me understand when does this assertion fail and how to fix it ? I have tried compiling with _DFILE_OFFSET_BITS=64 but in vain.

Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328

1 Answers1

4

The code attempts to determine whether off_t is at least 64 bits wide, including a sign bit.

In STATIC_ASSERT, if COND is true, 1 - 2*!(COND) evaluates as 1-2*!1 = 1-2*0 = 1, and the array size is 1, which is fine. If COND is false, it evaluates as 1-2*!0 = 1-2*1 = -1, which causes the compiler to issue a diagnostic. So STATIC_ASSERT acts to generate a compiler message if COND is false.

LARGE_OFF_T attempts to perform some arithmetic in the off_t type. If off_t is at least 64 bits wide, (off_t) 1 << 31) << 31 evaluates to 262. Then the whole expression evaluates to 262 − 1 + 262 = 263−1. This number has a remainder of 721 modulo 2147483629 and 1 modulo 2147483647, so it passes the test and does not cause the compiler to issue a diagnostic.

If off_t is, say, 32 bits wide and unsigned, (off_t) 1 << 31) << 31 evaluates to 0, and the whole expression evaluates to 0 − 1 + 0 = 232−1 (since unsigned arithmetic wraps), which has a remainder modulo 2147483629 of 37, so it fails the test, and the compiler generates a diagnostic.

The test is not reliable if off_t is signed. If off_t is signed and 32 bits wide, the behavior of (off_t) 1 << 31 is not defined by the C standard. If it is signed and less than 63 bits wide, the behavior of ((off_t) 1 << 31) << 31 is undefined, and, if it is signed and less than 64 bits wide, the final addition is undefined.

If the test is failing for you, you need to correct it by adjusting the build conditions so that off_t is an integer type that is at least 64 bits wide. There is insufficient information in the question to say how to do this.

There are indications in the code that it expects off_t to be a signed type. If it were known to be unsigned, then one could ensure it were at least 64 bits simply by testing 18446744073709551615u <= (off_t) -1. And value that the code attempts to construct, 263−1, is the maximum value of a 64-bit signed integer, rather than an unsigned integer. This is unfortunate because, as indicated above, if off_t is signed and less than 64 bits wide, the operations have undefined behavior. A better approach might be to test 9223372036854775807 == (off_t) 9223372036854775807, because overflow during conversion to a signed integer type is at least implementation-defined, rather than not defined by the C standard. One might also settle for 64 <= sizeof(off_t) * CHAR_BIT. (CHAR_BIT is defined in <limits.h>.)

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • @EricPostpischil: explanation of the mechanism is nice but I think OP's real problem (an XY Problem) is that they're incorrectly attempting to override glibc's 32-bit `off_t` default and failing to do so because of wrong macro name. – R.. GitHub STOP HELPING ICE Feb 17 '20 at 15:35
  • @R..: Out of my knowledge, and, as the penultimate paragraph says, “There is insufficient information in the question to say how to do this.” There is some information [here](https://stackoverflow.com/questions/9073667/where-to-find-the-complete-definition-of-off-t-type), but somebody should enter an answer about getting the desired `off_t`; I do not think I am going to investigate it, at least not today. – Eric Postpischil Feb 17 '20 at 15:39
  • 1
    @chux: I thought I edited the `u` in previously, but it seems the edit got lost somehow. – Eric Postpischil Feb 17 '20 at 16:20