Checking size of structure in C without running the code
03 / 10 / 2023

Let's say, you are reading C source and see a definition of a struct that you want to know size of or offset of given member inside it. This might be usefull for example when you are reading disassembly output without debug symbols, but have some types definitions that you know the original binary was compiled with. Or you are just wondering how much memory some type consume.

One way of doing It would be to count it manually, but you can make mistakes or the structure might be complex, like struct task_struct in the Linux kernel.

The proper way of doing it would probably be accessing some kind of compiler API to get this info. Something like LSP or libclang. But there is a faster way by causing a compilation error and tricking compiler into printing this information to stderr.

Trick for getting size of structure

#define ERROR_SIZEOF(type) \
	_Pragma("GCC diagnostic push"); \
	_Pragma("GCC diagnostic error \"-Wint-conversion\""); \
	char (*_error_sizeof)[sizeof(type)] = 1; \
	_Pragma("GCC diagnostic pop");

This will essentially enable errors on "-Wint-conversion" warning and then cause It with the assignment statement. Now to get the size of the structure with this user just have to modify source code with

ERROR_SIZEOF(<type we want to know the size of>);
Doing it for mentioned struct task_struct in the Linux kernel will cause something like:
./include/linux/sched.h:1557:47: error: initialization of ‘char (*)[5376]’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
 1557 |         char (*_error_sizeof)[sizeof(type)] = 1; \
      |                                               ^
./include/linux/sched.h:1560:1: note: in expansion of macro ‘ERROR_SIZEOF’
 1560 | ERROR_SIZEOF(struct task_struct);
      | ^~~~~~~~~~~~
The important part of the error message is ‘char (*)[5376]’. The size of this array declaration will reveal the size of the struct for which we invoked the error.

Trick for getting offset of member inside of structure

Similar trick can be done to get offset of member inside of structure with macro showed below.

#define ERROR_OFFSETOF(type, member) \
	_Pragma("GCC diagnostic push"); \
	_Pragma("GCC diagnostic error \"-Wint-conversion\""); \
	char (*_error_offsetof)[(unsigned long long)&((type*)0)->member] = 1; \
	_Pragma("GCC diagnostic pop");

And using it like:

ERROR_OFFSETOF(<type>, <member of type that we want to know offset of>);

Doing it for mentioned struct task_struct and struct task_struct __rcu *parent in the Linux kernel will cause something like:

./include/linux/sched.h:1565:76: error: initialization of ‘char (*)[448]’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
 1565 |         char (*_error_offsetof)[(unsigned long long)&((type*)0)->member] = 1; \
      |                                                                            ^
./include/linux/sched.h:1568:1: note: in expansion of macro ‘ERROR_OFFSETOF’
 1568 | ERROR_OFFSETOF(struct task_struct, parent);
      | ^~~~~~~~~~~~~~

The important part of the error message, in this case is ‘char (*)[448]’. The size of this array declaration will reveal the offset of member inside the structure for which we invoked the error.