I have MCU code (for AVR) with set of different drivers. Driver in use is selected at startup time (in init code) and only one of the drivers will be used at a time. Therefore, i can alias all of RAM segments into the same location. That is required, as RAM is very limited and all of drivers require 2-3 KB of storage for buffers etc.
Currently i have structures typedef
'ed in all header files:
typedef struct {
// Driver-specific variables for internal use.
// None of those and accessed from outside C-file
int internal_variable1_for_driver1;
int internal_variable2_for_driver1;
...
} st_driver1_t;
Other drivers have similar structures
#include "driver1.h"
#include "driver2.h"
#include "driver3.h"
...
union {
st_driver1_t DRV1;
st_driver2_t DRV2;
st_driver3_t DRV3;
...
} DRV;
DISCLAIMER: I understand that all RAM access from modules, other than selected one, should be disabled as it will alter whole union. I have a lot of ASM code in portfolio and that's obvious for me.
Now i have some conflicts because i have to include all_drivers.h, and therefore all driverN.h files into each driver. I want to hide all those headers from all of other drivers.
Is it ok to define all driver-specific structures with __attribute__((common))
in C-files and therefore hide all of structure from headers. As i understand, linker will merge all those structures into overlapping segment and create some analog of union.
struct __attribute__((common)) {
int var1;
int var2;
...
} DRV;
struct __attribute__((common)) {
long OtherVar1;
int ExtraOne;
...
} DRV;
Also, should I define those structs as static
or not? Will this solution be stable or is it undocumented hack?
Is it ok to define all driver-specific structures with
__attribute__((common))
With GCC and compatible compilers, you can use __attribute__((__common__))
for common variables (and __attribute__((__no_common__))
if some variables should not be common, but common is the default).
GCC switched its default behaviour from -fcommon
to -fno-common
some time ago (in 2020 with v10), so you can also compile all compilation units that need this with -fcommon
.
should I define those structs as
static
[...]?
No. There are some conditions that objects must meet to be coalesced as common:
The objects are global (thus also in static storage).
The objects don't have initializers.
The objects have the same (assembly) name.
The compiler must be advised to put the objects in common as discussed above.
Sometimes it's preferred that the objects don't have the same name on C/C++ level, for example that the object for driver1 is named driver1
etc. That's fine if the objects have the same name on assembly level, which can be achieved by using the same assembly name for all such objects. This is a feature provided by GCC. The following example sets the assembly name to asm_name
, so in the assembly code the name asm_name
will be used, but in C code you use driver1
:
typedef struct { ... } driver1_t;
__attribute__((__common__))
driver1_t driver1 __asm ("asm_name");
If the ABI requires leading underscores, you should use a name that fits that ABI and use an assembly name that starts with an _
like _asm_name
.
There are use cases where common is much clearer and more convenient than putting all objects into a union, so don't be discouraged by these comments.