in6_addr in GCC 4 - c

I see code like following : (legecy code in the project I am working on)
#if __GNUC__ > 3
.ipv6_addr = {.__in6_u = {.__u6_addr32 = {0, 0, 0, 0}}}
#else
.ipv6_addr = {.in6_u = {.u6_addr32 = {0, 0, 0, 0}}}
#endif
where "ipv6_addr" is in type of struct in6_addr. I don't understand why its member in6_u would change to "in6_u" if __GNUC > 3.
My question is: why / when GCC version could impact the name of the field in struct in6_addr ?
Thanks.
Update: my host system has GCC 4.1.2, but the in6_addr was defined as :
in /usr/include/netinet/in.h
/* IPv6 address */
struct in6_addr
{
union
{
uint8_t u6_addr8[16];
uint16_t u6_addr16[8];
uint32_t u6_addr32[4];
} in6_u;
#define s6_addr in6_u.u6_addr8
#define s6_addr16 in6_u.u6_addr16
#define s6_addr32 in6_u.u6_addr32
};
And gcc version is:
$/usr/bin/gcc -v
Using built-in specs.
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-libgcj-multifile --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --disable-plugin --with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre --with-cpu=generic --host=x86_64-redhat-linux
Thread model: posix
gcc version 4.1.2 20080704 (Red Hat 4.1.2-50)

The structure definition of concern here, struct in6_addr, comes from the C runtime library (in <netinet/in.h>). The C runtime library is usually tightly coupled with the operating system in use, but not the C compiler in use. This is especially true for the C compilers that define __GNUC__ to any value (there are at least three of these: GCC, clang, and icc); these compilers are designed to be usable with many different OSes and runtimes. Therefore, in principle, testing __GNUC__ does not tell you anything useful about structure definitions that come from the runtime.
I suspect that the authors of this "legacy code" tested on two different Linux distributions, noted an accidental correlation between the value of __GNUC__ and the contents of <netinet/in.h>, and didn't bother looking for a more correct way to make their code compile.
You should replace the entire conditional with this:
.ipv6_addr = IN6ADDR_ANY_INIT;
The macro IN6ADDR_ANY_INIT is required by the relevant standard (POSIX.1-2008 spec for <netinet/in.h>) to be usable as an initializer for a variable of type in6_addr, setting that variable to the IPv6 wildcard address, which is all-bits-zero. So it will have the same effect without requiring any #ifdefs at all.
To illustrate that #if __GNUC__ > 3 is the wrong test to apply here, here are three different definitions of struct in6_addr, all taken from systems where you might reasonably encounter both __GNUC_==3 and __GNUC__==4 (the 3.x series is getting a little old nowadays, but I still run into it from time to time).
GNU libc 2.17
struct in6_addr
{
union
{
uint8_t __u6_addr8[16];
#if defined __USE_MISC || defined __USE_GNU
uint16_t __u6_addr16[8];
uint32_t __u6_addr32[4];
#endif
} __in6_u;
};
NetBSD 6.1
struct in6_addr {
union {
__uint8_t __u6_addr8[16];
__uint16_t __u6_addr16[8];
uint32_t __u6_addr32[4];
} __u6_addr; /* 128-bit IP6 address */
};
Windows 7 (official SDK; perversely does not provide netinet/in.h):
struct in6_addr {
union {
UCHAR Byte[16];
USHORT Word[8];
} u;
};

Related

SUNPATHLEN on Linux. Where is it defined?

Recently I begun to port some my TCP code from FreeBSD to Linux. Already had a bunch of questions ;) So, here is another one.
The C struct sockaddr_un on Linux have some different definition than of that on FreeBSD. But, to the question. I have such code in my project:
}else if(AF_UNIX == domain){
if(SUNPATHLEN == strnlen(a, SUNPATHLEN)){
return -ENAMETOOLONG;
}
The above tests that Maximum path should be no more than SUNPATHLEN constant. The SUNPATHLEN is defined on FreeBSD, but apparently not on Linux.
Looking through the gcc -E source.c | grep -n4 sockaddr_un, the struct definition is following:
1724:struct sockaddr_un
1725- {
1726- sa_family_t sun_family;
1727- char sun_path[108];
1728- };
Here the length of a buffer is explicitly set to be of 108.
What is a general rule to check for buffer being trimmed/overflowed in Linux, for the case?
Looking through the gcc -E source.c | grep -n4 sockaddr_un, the struct definition is following:
You don't have to (and shouldn't) trawl the source for this kind of information. You should be looking at user-facing documentation to determine the interface characteristics on which you can rely. In this case, you're looking for unix(7):
A UNIX domain socket address is represented in the following
structure:
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[108]; /* Pathname */
};
The sun_family field always contains AF_UNIX. On Linux, sun_path is
108 bytes in size
(emphasis added).
What is a general rule to check for buffer being trimmed/overflowed in
Linux, for the case?
No macro is defined for it, but the capacity of the path buffer is explicitly documented as 108 bytes. You can (and probably should) define your own macro for this if you're going to perform tests related to it.
You could possibly do some variation on this to remove system dependencies:
static struct sockaddr_un dummy_sockaddr_un_;
#define MY_SUN_PATH_SIZE (sizeof(dummy_sockaddr_un_.sun_path))

Casting to union field yields to conversion warning

I am using a Microchip microcontroller which defines the following union:
__extension__ typedef struct tagT1CONBITS {
union {
struct {
uint16_t :1;
uint16_t TCS:1;
uint16_t TSYNC:1;
uint16_t :1;
uint16_t TCKPS:2;
uint16_t TGATE:1;
uint16_t :6;
uint16_t TSIDL:1;
uint16_t :1;
uint16_t TON:1;
};
struct {
uint16_t :4;
uint16_t TCKPS0:1;
uint16_t TCKPS1:1;
};
};
} T1CONBITS;
extern volatile T1CONBITS T1CONbits __attribute__((__sfr__));
Somewhere in my code I am defining a variable as a 8 bit unsigned integer which I would like to assign to one of the fields of the union above. Somewhat as follows:
uint8_t tckps;
// The value of tckps is calculated here by some magic formula
tckps = magicformula();
// We asign the value of tckps to the uC register
T1CONbits.TCKPS = tckps;
I have the -Wconversion option enabled in gcc wich leads to the following warning:
warning: conversion to 'volatile unsigned char:2' from 'uint8_t' may alter its value
I can understand why gcc is warning me. I am currently perfoming a value check on the tckps variable before asigning it so I know that the data loss is not going to be a problem, but I don't know how to satisfy gcc conversion check so that it doesn't warn me in this particular case.
How can I fix the warning?
Thanks in advance!
EDIT: Added toolchain information.
Microchip Language Tool Shell Version 1.33 (Build date: Oct 9 2017).
Copyright (c) 2012-2016 Microchip Technology Inc. All rights reserved
*** Executing: "C:\Program Files (x86)\Microchip\xc16\v1.33\bin\bin/elf-gcc.exe"
"-v"
Using built-in specs.
COLLECT_GCC=C:\Program Files (x86)\Microchip\xc16\v1.33\bin\bin/elf-gcc.exe
Target: pic30-elf
Configured with: /home/xc16/release-builds/build_20171009/src/XC_GCC/gcc/configure --build=i386-linux --host=i386-mingw32 --target=pic30-elf --disable-lto --disable-threads --disable-libmudflap --disable-libssp --disable-libstdcxx-pch --disable-hosted-libstdcxx --with-gnu-as --with-gnu-ld --enable-languages=c --disable-nls --disable-libgomp --without-headers --disable-libffi --disable-bootstrap --prefix=/bin --libexecdir=/bin --program-prefix=pic30- --with-libelf=/home/xc16/release-builds/build_20171009/bin/XC_GCC-elf-mingw32-xclm/host-libs/ --with-dwarf2 --with-gmp=/home/xc16/release-builds/build_20171009/bin/XC_GCC-elf-mingw32-xclm/host-libs --with-ppl=/home/xc16/release-builds/build_20171009/bin/XC_GCC-elf-mingw32-xclm/host-libs --with-cloog=/home/xc16/release-builds/build_20171009/bin/XC_GCC-elf-mingw32-xclm/host-libs --with-zlib=/home/xc16/release-builds/build_20171009/bin/XC_GCC-elf-mingw32-xclm/host-libs --with-bugurl=http://www.microchip.com/support --with-host-libstdcxx=-Wl,-Bstatic,-lstdc++,-Bdynamic,-lm
Thread model: single
gcc version 4.5.1 (XC16, Microchip v1.33) Build date: Oct 9 2017 (Microchip Technology)
This lets met get rid of the warning:
#include <stdint.h>
typedef struct tagT1CONBITS {
union {
struct {
uint16_t :1;
uint16_t TCS:1;
uint16_t TSYNC:1;
uint16_t :1;
uint16_t TCKPS:2;
uint16_t TGATE:1;
uint16_t :6;
uint16_t TSIDL:1;
uint16_t :1;
uint16_t TON:1;
};
struct {
uint16_t :4;
uint16_t TCKPS0:1;
uint16_t TCKPS1:1;
};
};
} T1CONBITS;
volatile T1CONBITS T1CONbits;
uint8_t (*magicformula)(void);
int main(void)
{
uint8_t tckps;
// The value of tckps is calculated here by some magic formula
tckps = magicformula() ;
// We asign the value of tckps to the uC register
T1CONbits.TCKPS = (uint8_t)(tckps & 3); // This fixes the warning
return 0;
}
I compile it with:
gcc -Wall -Wconversion test2.c
The problem I see is that the compiler can't check over the function boundaries that the range of the variable is not exceeded. If you do it at the point of use the compiler can check this.
The cast is to avoid a warning when the expression is promoted to int.
This is a known issue 39170 with the gcc compiler specifically, when compiling with -Wconversion. The problem exists from gcc 4.3.x and later versions. It is possible that they have fixed the issue in some newer version, but I couldn't find any information regarding it.
A possible dirty work-around is to mask the value bit a bit mask as shown in the answer by Wolfgang.
You can selectively ignore gcc warnings with #pragma GCC diagnostic. Below is an example of how you can use this:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
T1CONbits.TCKPS = tckps;
#pragma GCC diagnostic pop
The push pragma stores the current state of diagnostic warnings. The ignored pragma then tells the compiler to ignore the specified warning from that point on. The pop pragma then restores the prior diagnostic state, so any other places where a conversion warning might occur will be printed.
The end result is that the warning is suppressed for only the specific source lines between the pragmas.

Struct layout in apcs-gnu ABI

For this code:
struct S { unsigned char ch[2]; };
int main(void)
{
_Static_assert( sizeof(struct S) == 2, "size was not 2");
}
using GCC (various versions) for ARM with the ABI apcs-gnu (aka. OABI, or EABI version 0), I get the assertion fails. It turns out the size of the struct is 4.
I can work around this by using __attribute__((packed)); but my questions are:
What is the rationale for making this struct size 4?
Is there any documentation specifying the layout of structs in this ABI?
On the ARM website I found documentation for aapcs (EABI version 5) which does specify this struct as having a size of 2; but I could not find anything about apcs-gnu.
This is a GCC-specific decision to trade-off size for performance. It can be overridden with -mstructure-size-boundary=8.
An excerpt from source code:
/* Setting STRUCTURE_SIZE_BOUNDARY to 32 produces more efficient code, but the
value set in previous versions of this toolchain was 8, which produces more
compact structures. The command line option -mstructure_size_boundary=<n>
can be used to change this value. For compatibility with the ARM SDK
however the value should be left at 32. ARM SDT Reference Manual (ARM DUI
0020D) page 2-20 says "Structures are aligned on word boundaries".
The AAPCS specifies a value of 8. */
#define STRUCTURE_SIZE_BOUNDARY arm_structure_size_boundary

How to do arithmetic with OpenCL vector types in host-side code?

Here's my code:
#include <stdio.h>
#include <CL/cl.h>
#include <CL/cl_platform.h>
int main(){
cl_float3 f3 = (cl_float3){1, 1, 1};
cl_float3 f31 = (cl_float3) {2, 2, 2};
cl_float3 f32 = (cl_float3) {2, 2, 2};
f3 = f31 + f32;
printf("%g %g %g \n", f3.x, f3.y, f3.z);
return 0;
}
When compiling with gcc 4.6, it produces the error
test.c:14:11: error: invalid operands to binary + (have ‘cl_float3’ and ‘cl_float3’)
Very strange to me, because the OpenCL Specification demontrates in section 6.4 just that, an addition of two floatn. Do I need to include any other headers?
But even more strange is that when compiling with -std=c99 I get errors like
test.c:16:26: error: ‘cl_float3’ has no member named ‘x’
..for all components (x, y and z)...
The reason for the compilation problem with structure subscripts can be seen in the implementation of the standard in the AMD SDK.
If you look at the <CL/cl_platform.h> header from AMD toolkit you could see how the structures are defined.
typedef cl_float4 cl_float3;
typedef union
{
cl_float CL_ALIGNED(16) s[4];
#if (defined( __GNUC__) || defined( __IBMC__ )) && ! defined( __STRICT_ANSI__ )
__extension__ struct{ cl_float x, y, z, w; };
....
#endif
}cl_float4;
The #if clause is ignored when gcc is invoked with --std=c99.
To make your code work with --std=c99 you could replace references to f3.x with f3.s[0] and so on.
OpenCL programs consist of two parts.
A program which runs on the host. This is normally written in C or C++, but it's nothing special except that it uses the API described in sections 4 & 5 of the OpenCL Specification.
A kernel which runs on the OpenCL device (normally a GPU). This is written in the language specified in section 6. This isn't C, but it's close. It adds things like vector operations (like you're trying to use). This is compiled by the host program passing a string which contains the kernel code to OpenCL via the API.
You've confused the two, and tried to use features of the kernel language in the host code.
cl_float.v is another option:
#include <assert.h>
#include <CL/cl.h>
int main(void) {
cl_float4 f = {{1, 2, 3, 4}};
cl_float4 g = {{5, 6, 7, 8}};
cl_float4 h;
h.v4 = f.v4 + g.v4;
assert(h.s[0] == 6);
assert(h.s[1] == 8);
return EXIT_SUCCESS;
}
which can be run as:
gcc -std=c89 -Wall -Wextra tmp.c -lOpenCL && ./a.out
in Ubuntu 16.10, gcc 6.2.0.
v is defined in Linux GCC x86 via GCC vector extensions.
The file https://github.com/KhronosGroup/OpenCL-Headers/blob/bf0f43b76f4556c3d5717f8ba8a01216b27f4af7/cl_platform.h contains:
#if defined( __SSE__ )
[...]
#if defined( __GNUC__ )
typedef float __cl_float4 __attribute__((vector_size(16)));
[...]
#define __CL_FLOAT4__ 1
and then:
typedef union
{
[...]
#if defined( __CL_FLOAT4__)
__cl_float4 v4;
#endif
}cl_float4;
Not sure this barrage of ifdefs was a good move by Khronos, but it is what we have.
I recommend that you just always use .s[0], which is the most portable option. We shouldn't need to speedup the host SIMD if we are focusing on the GPU...
C11 anonymous structs
The error error: ‘cl_float3’ has no member named ‘x’ happens because of the lines mentioned at: https://stackoverflow.com/a/10981639/895245
More precisely, this feature is called "anonymous struct", and it is an extension that was standardized in C11.
So in theory it should also work with -std=c11, but it currently doesn't because the CL headers weren't updated to check for C11, see also: https://github.com/KhronosGroup/OpenCL-Headers/issues/18

Use GNU versions of basename() and dirname() in C source

How do I use the GNU C Library version of basename() and dirname()?.
If you
#include <libgen.h>
for dirname
You're already getting the POSIX, not the GNU, version of basename(). (Even if you
#define _GNU_SOURCE
As far as I know there is no conditional importing in C. Is there a gcc specific trick?
Just write it yourself and give it a different name than basename. This GNU insistence on creating alternate non-conforming versions of standard functions that can be written in 1-3 lines is completely batty.
char *gnu_basename(char *path)
{
char *base = strrchr(path, '/');
return base ? base+1 : path;
}
This way, your program will also be more portable.
According to the man page you should do
#define _GNU_SOURCE
#include <string.h>
If you get the POSIX version, libgen.h is probably already included before that point. You may want to include -D_GNU_SOURCE in the CPPFLAGS for compilation:
gcc -D_GNU_SOURCE ....
Compare: POSIX Version vs GNU Version on Compiler Explorer.
After examining libgen.h, I'm pretty sure I have a warning-free and error-free solution:
/* my C program */
#define _GNU_SOURCE /* for GNU version of basename(3) */
#include <libgen.h> /* for dirname(3) */
#undef basename /* (snide comment about libgen.h removed) */
#include <string.h> /* for basename(3) (GNU version) and strcmp(3) */
/* rest of C program... */
With the #undef line, now my program includes dirname(3) from libgen.h and the GNU version of basename(3) from string.h.
No compiler warnings/errors from either gcc (version 4.5.2) or clang (version 3.3).
Make sure you're building with the GNU C library, rather than your system's (presumed) POSIX-compatible default.
This is often set in the GCC spec file. Use the -v option to show the current settings:
$ gcc -v
Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.4.4-14ubuntu5' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.4 --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --disable-werror --with-arch-32=i686 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5)
It's crazy basename and dirname have two versions.
We worked at a big project, it looks like these two apis already caused
potentially bugs. So we marked "basename" "dirname" as deprecated for warning if
someone use it:
#ifdef basename
__attribute__ ((deprecated))
char *__xpg_basename(char *path);
#else
__attribute__ ((deprecated))
char *basename(const char *path);
#endif
__attribute__ ((deprecated))
char *dirname(char *path);
We also try to introduce a base c foundation library such as glib or libcork,
but it looks like too heavy. So we write a tiny library for this purpose, it
implementation like this:
#include <libgen.h> // for dirname
#include <linux/limits.h> // for PATH_MAX
#include <stdio.h> // for snprintf
#include <string.h> // for basename
#include <stdbool.h> // for bool
bool get_basename(const char *path, char *name, size_t name_size) {
char path_copy[PATH_MAX] = {'\0'};
strncpy(path_copy, path, sizeof(path_copy) - 1);
return snprintf(name, name_size, "%s", basename(path_copy)) < name_size;
}
bool get_dirname(const char *path, char *name, size_t name_size) {
char path_copy[PATH_MAX] = {'\0'};
strncpy(path_copy, path, sizeof(path_copy) - 1);
return snprintf(name, name_size, "%s", dirname(path_copy)) < name_size;
}
Then we replace all basename dirname call with get_basename get_dirname.

Resources