I work on the project where I use syscall structures.
Is structure packing same in kernel space as in user space ? And I also would like to have some trustworthy source, where I can read more about that.
Example for better understanding:
I receive data about syscall in this structure in kernel space.
struct sys_enter_socket_t {
long unusedParams;
int __syscall_nr;
long family;
long type;
long protocol;
};
I save it to char array and send it to user space. In user space I recast it to this structure and work with it. Now on my platform it works fine, but I want to be sure, it will work on other platforms.
CodePudding user response:
Is structure packing same in kernel space as in user space ?
User-space is about 50 different languages (assembly, C, C , Pascal, Java, Golang, Ada, MATLAB, ...) each with different rules, sometimes with multiple completely different implementations (e.g. stuff compiled for Windows running under Wine).
Each of these has it's own run-time environment to make it work. For one example, for a function like readdir()
, the C library might (or might not) get completely different structures from the kernel and convert them into whatever makes sense for however the particular C library felt like defining its struct dirent
. Note that this is fundamentally a "good thing" - it means that kernel can change its data structures (e.g. add new fields, increase the size of things like timestamps to fix a "year 2038" problem, etc) without breaking everything in user-space.
In other words, kernel developers say "this is specifically how it is for this kernel (in these conditions)" and every different language run-time in user-space does whatever it needs to do hide the lower level stuff so that nothing else (your program's source code) cares what the kernel developers said.
In user space I recast it to this structure and work with it. Now on my platform it works fine, but I want to be sure, it will work on other platforms.
Compile your program as a 32 bit executable running under a 64-bit kernel; and I think you'll find that (even with the same compiler and linker on the same kernel on the same computer) it does not work.
Specifically; in this case user-space will say that long
is 32 bits and that no padding is necessary, while kernel will say that long
is 64 bits and will add 4 bytes of padding after the int __syscall_nr;
.
To make it easier for different run-time environments to make it work, you could try being explicit, like:
struct sys_enter_socket_t {
uint64_t unusedParams;
uint32_t __syscall_nr;
uint32_t reserved;
uint64_t family;
uint64_t type;
uint64_t protocol;
} __attribute__((packed));
CodePudding user response:
I suppose what you are looking for is so called "data model". On x86_64 we use LP64, which means long and pointer are 64 bit in size (char, short and int are assumed 8, 16 and 32 bits correspondingly). On x86 we used ILP32 which means integer, long and pointer are 32 bits in size (smaller types stay the same - 8 and 16 bits). In both data models long long is 64 bits wide.
Compile your program as a 32 bit executable running under a 64-bit kernel; and I think you'll find that (even with the same compiler and linker on the same kernel on the same computer) it does not work.
This is nice example of how data models work. Thus, "design a type that is equivalent in both execution environments" (as Marco Bonelli put it) using data types with specified size (as Brendan suggested) is a solution.