diff options
Diffstat (limited to '')
| -rw-r--r-- | libsyscalls.h | 369 | ||||
| -rw-r--r-- | libsyscalls/internal-begin.h | 7 | ||||
| -rw-r--r-- | libsyscalls/internal-end.h | 5 | ||||
| -rw-r--r-- | libsyscalls_get_datatype_description.c | 80 | ||||
| -rw-r--r-- | libsyscalls_get_integer_alignment.c | 42 | ||||
| -rw-r--r-- | libsyscalls_get_struct_description.c | 656 | ||||
| -rw-r--r-- | libsyscalls_get_syscall.c | 9 | ||||
| -rw-r--r-- | libsyscalls_get_syscall_display_info.c | 169 | ||||
| -rw-r--r-- | libsyscalls_make_signed_integer.c | 56 | ||||
| -rw-r--r-- | libsyscalls_parse_signed_integer.c | 61 | ||||
| -rw-r--r-- | libsyscalls_perror.c | 2 | ||||
| -rw-r--r-- | libsyscalls_section_value.c | 74 | ||||
| -rw-r--r-- | libsyscalls_to_tracee_endian.c | 60 | ||||
| -rw-r--r-- | libsyscalls_to_tracer_endian.c | 59 | ||||
| -rw-r--r-- | libsyscalls_unsection_value.c | 76 | 
15 files changed, 1576 insertions, 149 deletions
| diff --git a/libsyscalls.h b/libsyscalls.h index caf4a4b..382022b 100644 --- a/libsyscalls.h +++ b/libsyscalls.h @@ -31,7 +31,8 @@  	X(LIBSYSCALLS_E_NOMEM, "Failed to allocate required memory") D\  	X(LIBSYSCALLS_E_INVAL, "Invalid arguments passed to function") D\  	X(LIBSYSCALLS_E_NOSUCHTYPE, "Type does not exist on the selected operating system or architecture") D\ -	X(LIBSYSCALLS_E_ISSTRUCT, "Type is a structure or union") +	X(LIBSYSCALLS_E_ISSTRUCT, "Type is a structure or union") D\ +	X(LIBSYSCALLS_E_ISNOTSTRUCT, "Type is not a structure or union")  /**   * libsyscall error numbers @@ -51,30 +52,49 @@ enum libsyscalls_os {   * Architectures   */  enum libsyscalls_arch { -	LIBSYSCALLS_ARCH_ALPHA, +	LIBSYSCALLS_ARCH_ALPHA_LE, +	LIBSYSCALLS_ARCH_ALPHA_BE,  	LIBSYSCALLS_ARCH_AMD64, -	LIBSYSCALLS_ARCH_AMD64_X32, /* 32-bit ABI, not traditional 32-bit x86 ISA */ -	LIBSYSCALLS_ARCH_ARM_OABI, -	LIBSYSCALLS_ARCH_ARM_EABI, -	LIBSYSCALLS_ARCH_IA64, +	LIBSYSCALLS_ARCH_AMD64_X32, /* 32-bit ABI on 64-bit ISA */ +	LIBSYSCALLS_ARCH_ARM_OABI_LE, +	LIBSYSCALLS_ARCH_ARM_OABI_BE, +	LIBSYSCALLS_ARCH_ARM_EABI_LE, +	LIBSYSCALLS_ARCH_ARM_EABI_BE, +	LIBSYSCALLS_ARCH_IA64_LE, /* 64-bit pointers */ +	LIBSYSCALLS_ARCH_IA64_BE, /* 64-bit pointers */ +	LIBSYSCALLS_ARCH_IA64_P32_LE, /* 32-bit pointers */ +	LIBSYSCALLS_ARCH_IA64_P32_BE, /* 32-bit pointers */  	LIBSYSCALLS_ARCH_M68K, -	LIBSYSCALLS_ARCH_MICROBLAZE, -	LIBSYSCALLS_ARCH_MIPS_O32, -	LIBSYSCALLS_ARCH_MIPS_N32, -	LIBSYSCALLS_ARCH_MIPS_N64, +	LIBSYSCALLS_ARCH_MICROBLAZE_32_LE, +	LIBSYSCALLS_ARCH_MICROBLAZE_32_BE, +	LIBSYSCALLS_ARCH_MICROBLAZE_64_LE, +	LIBSYSCALLS_ARCH_MICROBLAZE_64_BE, +	LIBSYSCALLS_ARCH_MIPS_O32_LE, +	LIBSYSCALLS_ARCH_MIPS_O32_BE, +	LIBSYSCALLS_ARCH_MIPS_N32_LE, /* 32-bit ABI on 64-bit ISA */ +	LIBSYSCALLS_ARCH_MIPS_N32_BE, /* 32-bit ABI on 64-bit ISA */ +	LIBSYSCALLS_ARCH_MIPS_N64_LE, +	LIBSYSCALLS_ARCH_MIPS_N64_BE,  	LIBSYSCALLS_ARCH_PARISC_32,  	LIBSYSCALLS_ARCH_PARISC_64, -	LIBSYSCALLS_ARCH_POWERPC_32, -	LIBSYSCALLS_ARCH_POWERPC_64, -	LIBSYSCALLS_ARCH_POWERPC_NOSPU, -	LIBSYSCALLS_ARCH_POWERPC_SPU, +	LIBSYSCALLS_ARCH_POWERPC_32_LE, +	LIBSYSCALLS_ARCH_POWERPC_32_BE, +	LIBSYSCALLS_ARCH_POWERPC_64_LE, +	LIBSYSCALLS_ARCH_POWERPC_64_BE, +	LIBSYSCALLS_ARCH_POWERPC_NOSPU_LE, +	LIBSYSCALLS_ARCH_POWERPC_NOSPU_BE, +	LIBSYSCALLS_ARCH_POWERPC_SPU_LE, +	LIBSYSCALLS_ARCH_POWERPC_SPU_BE,  	LIBSYSCALLS_ARCH_S390_32,  	LIBSYSCALLS_ARCH_S390_64, -	LIBSYSCALLS_ARCH_SH, +	LIBSYSCALLS_ARCH_SH_LE, /* does not cover SH-5 */ +	LIBSYSCALLS_ARCH_SH_BE, /* does not cover SH-5 */  	LIBSYSCALLS_ARCH_SPARC_32, -	LIBSYSCALLS_ARCH_SPARC_64, +	LIBSYSCALLS_ARCH_SPARC_64_LE, +	LIBSYSCALLS_ARCH_SPARC_64_BE,  	LIBSYSCALLS_ARCH_I386, -	LIBSYSCALLS_ARCH_XTENSA +	LIBSYSCALLS_ARCH_XTENSA_LE, +	LIBSYSCALLS_ARCH_XTENSA_BE  };  /** @@ -174,18 +194,23 @@ enum libsyscalls_datatype {  #define LIBSYSCALLS_LIST_STRUCTS_AND_UNIONS(X, SUFFIX, D, FIRST)\  	X(LIBSYSCALLS_TYPE_STRUCT_AIO_SIGSET ## SUFFIX, FIRST, "struct") D\ +	X(LIBSYSCALLS_TYPE_STRUCT_COMPAT_AIO_SIGSET ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_CACHESTAT ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_CACHESTAT_RANGE ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_CLONE_ARGS ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_EPOLL_EVENT ## SUFFIX,, "struct") D\ +	X(LIBSYSCALLS_TYPE_STRUCT_OABI_EPOLL_EVENT ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_FILE_HANDLE ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_FUTEX_WAITV ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_IOB ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_IOVEC ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_IO_EVENT ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_IO_URING_PARAMS ## SUFFIX,, "struct") D\ +	X(LIBSYSCALLS_TYPE_STRUCT_ITIMERVAL ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_ITIMERSPEC ## SUFFIX,, "struct") D\ +	X(LIBSYSCALLS_TYPE_STRUCT_ITIMERSPEC64 ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_KEXEC_SEGMENT ## SUFFIX,, "struct") D\ +	X(LIBSYSCALLS_TYPE_STRUCT_COMPAT_KEXEC_SEGMENT ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_LANDLOCK_RULESET_ATTR ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_LINUX_DIRENT ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_LINUX_DIRENT64 ## SUFFIX,, "struct") D\ @@ -214,6 +239,7 @@ enum libsyscalls_datatype {  	X(LIBSYSCALLS_TYPE_STRUCT_POLLFD ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_RLIMIT ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_RLIMIT64 ## SUFFIX,, "struct") D\ +	X(LIBSYSCALLS_TYPE_STRUCT_COMPAT_RLIMIT ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_ROBUST_LIST_HEAD ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_RSEQ ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_RUSAGE ## SUFFIX,, "struct") D\ @@ -227,7 +253,7 @@ enum libsyscalls_datatype {  	X(LIBSYSCALLS_TYPE_STRUCT_SIGALTSTACK ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_SIGEVENT ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_SIGINFO ## SUFFIX,, "struct") D\ -	X(LIBSYSCALLS_TYPE_STRUCT_SOCKADDR ## SUFFIX,, "struct") D /* size is always in the next argument */\ +	X(LIBSYSCALLS_TYPE_STRUCT_SOCKADDR ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_STAT ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_STAT64 ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_STATFS ## SUFFIX,, "struct") D\ @@ -235,6 +261,8 @@ enum libsyscalls_datatype {  	X(LIBSYSCALLS_TYPE_STRUCT_STATX ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_SYSINFO ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_TIMESPEC ## SUFFIX,, "struct") D\ +	X(LIBSYSCALLS_TYPE_STRUCT_TIMESPEC64 ## SUFFIX,, "struct") D\ +	X(LIBSYSCALLS_TYPE_STRUCT_TIMEVAL ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_TIMEX ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_TIMEZONE ## SUFFIX,, "struct") D\  	X(LIBSYSCALLS_TYPE_STRUCT_TMS ## SUFFIX,, "struct") D\ @@ -246,7 +274,9 @@ enum libsyscalls_datatype {  	X(LIBSYSCALLS_TYPE_2_INTS ## SUFFIX, FIRST, "int[2]") D\  	X(LIBSYSCALLS_TYPE_2_INTS_FD ## SUFFIX,, "int[2]") D\  	X(LIBSYSCALLS_TYPE_2_UINT32S ## SUFFIX,, "uint32[2]") D\ -	X(LIBSYSCALLS_TYPE_FD_SET ## SUFFIX,, "unsigned long[]") /* size depends on OS, (1024 / CHAR_BIT / sizeof(long)) in Linux */ +	X(LIBSYSCALLS_TYPE_FD_SET ## SUFFIX,, "unsigned long[]") /* size depends on OS, (1024/CHAR_BIT/sizeof(long)) in Linux */ D\ +	X(LIBSYSCALLS_TYPE_BUFFER_9 ## SUFFIX,, "char[9]") D\ +	X(LIBSYSCALLS_TYPE_BUFFER_65 ## SUFFIX,, "char[65]")  	/* these don't have any suffix */  	LIBSYSCALLS_MAKE_SPECIAL_TYPES_(), @@ -256,7 +286,7 @@ enum libsyscalls_datatype {  	LIBSYSCALLS_MAKE_ANNOTATED_NUMERICALS_(),  	LIBSYSCALLS_MAKE_STRUCTS_AND_UNIONS_(), -	/* the following are always pointers unless returned */ +	/* the following are always pointers if used as a parameter */  	/* size in next parameter (or the previous parameter if there is no next parameter),  	 * updated with fill if possible, otherwise returned (these use the suffix _ARRAY) */ @@ -2074,7 +2104,7 @@ struct libsyscalls_datatype_description {  	 * guaranteed to be split otherwise) and this value is just  	 * the number of bits stored in the register or struct field  	 *  -	 * It is possible for the datatype to contain unused dummy +	 * It is possible for the data type to contain unused dummy  	 * bits in order to make it a power of 2 or a multiple of  	 * a smaller data type. For example, on x86, `long double`  	 * uses 128 bits, even though the value only uses 80 bits. @@ -2194,7 +2224,9 @@ struct libsyscalls_datatype_description {  	 * `sizeof(x.byteorder) / sizeof(*x.byteorder)`  	 * elements are read (where x is the instance of the  	 * structure) (you should definitely have your own -	 * macro for that one) +	 * macro for that one). As an alternative to +	 * `LIBSYSCALLS_IS_BYTEORDER_END`, you may use the +	 * LIBSYSCALLS_IS_BYTEORDER_END_AT macro  	 */  	unsigned char byteorder[32]; @@ -2214,6 +2246,14 @@ struct libsyscalls_datatype_description {  # define LIBSYSCALLS_IS_BYTEORDER_END(SHIFT)\  	(!~((SHIFT) | ~LIBSYSCALLS_FIELD_MASK_(1ULL, struct libsyscalls_datatype_description, byteorder[0])))  #endif + +	/** +	 * Test whether `DESC->byteorder[INDEX]` is +	 * the (post-last) end of `DESC->byteorder` +	 */ +#define LIBSYSCALLS_IS_BYTEORDER_END_AT(DESC, INDEX)\ +		((size_t)(INDEX) == sizeof((DESC)->byteorder) / sizeof(*(DESC)->byteorder) || \ +		 LIBSYSCALLS_IS_BYTEORDER_END((DESC)->byteorder[INDEX]))  };  /** @@ -2262,6 +2302,51 @@ struct libsyscalls_structure_field {  	unsigned short int symbolic_field : 1;  	/** +	 * Only used with .input_field set to 1 +	 *  +	 * If 1, it is probably not an error if the +	 * field is partially initialised as long as all +	 * its memory is allocated +	 *  +	 * In the case that a field is split using for +	 * example one field with the type +	 * LIBSYSCALLS_TYPE_UINT64_FRONT_32 and another +	 * field with the type +	 * LIBSYSCALLS_TYPE_UINT64_BACK_32 (these two +	 * (for some split types, their may be more than +	 * two fields) fields will have the same name), +	 * it is one is partially initialised and the +	 * other completely uninitialised. Generally, +	 * as long as at least one of the parts are +	 * partially initialised, the application should +	 * not, unless it has more detailed knowledge +	 * about the system call, assume that there is +	 * an error. This happens because kernel's version +	 * of the library uses a scalar field, where as +	 * the userspace version (usually libc's version) +	 * uses a union of differently sized field. It +	 * is not necessarily the case that it can be +	 * known which field in the union is used: for +	 * example in epoll_ctl(2), it is completely up +	 * to the application, and the application has +	 * no way of telling the kernel which is used, +	 * and the kernel will in epoll_wait(2) simply +	 * return the union's value as it was set, meaning +	 * that some bits may be effectively uninitialised. +	 * Even if the field is complete uninitialised, +	 * it may still be the case that this is not an +	 * error, however, it probably is an error; for +	 * example in the case of epoll(7), it is completely +	 * possible, albeit unlikely (because it's silly), +	 * that the user calls epoll_ctl(2) without the +	 * `.data` field set, because it doesn't look at +	 * in when epoll_wait(2) returns, but insteads +	 * just does unblocking I/O one each file +	 * descriptor it has added to the epoll(7). +	 */ +	unsigned short int partial_init_ok : 1; + +	/**  	 * If 1, the field is a pointer that the reads from  	 *   	 * Note that there are types that fundamentally are @@ -2281,7 +2366,16 @@ struct libsyscalls_structure_field {  	 */  	unsigned short int output_pointer : 1; -	unsigned short int padding1__ : 16 - (6*1); +	/** +	 * If 1, it has been determined that the field is +	 * not being used (usually because that it's not +	 * the active field in a `union` or because a +	 * flag somewhere in the `struct` is either set or +	 * cleared) +	 */ +	unsigned short int unused : 1; + +	unsigned short int padding1__ : 16 - (8*1);  #if LIBSYSCALLS_IS_PADDING_REQUIRED_(3, 16, LIBSYSCALLS_POINTER_BIT_)  # if !LIBSYSCALLS_CAN_ALIGN_(3, 16, CHAR_BIT) @@ -2289,6 +2383,30 @@ struct libsyscalls_structure_field {  # endif  	char padding2__[LIBSYSCALLS_PAD_(sizeof(void *)/sizeof(char), (3*16)/CHAR_BIT)];  #endif + +	/** +	 * Data type description +	 *  +	 * The application is free to make changes, however, +	 * however the pointers themselves must not be touched +	 */ +	union { +		/** +		 * Description to use if `.type` refers to a +		 * `struct` or `union`; that is, if +		 * `(.type & ~LIBSYSCALLS_TYPEBITSMASK) >= +		 * LIBSYSCALLS_TYPEOFFSET_STRUCTS_AND_UNIONS` +		 */ +		struct libsyscalls_structure_description *structure; + +		/** +		 * Description to use if `.type` does not refer +		 * to a `struct` or `union`; that is, if +		 * `(.type & ~LIBSYSCALLS_TYPEBITSMASK) < +		 * LIBSYSCALLS_TYPEOFFSET_STRUCTS_AND_UNIONS` +		 */ +		struct libsyscalls_datatype_description *nonstructure; +	} type_description;  };  /** @@ -2320,6 +2438,12 @@ struct libsyscalls_structure_description {  	unsigned short int num_fields : 14;  	/** +	 * If 1, the data type is a `union`, +	 * if 0, the data type is a `struct` +	 */ +	unsigned short int is_union : 1; + +	/**  	 * Only used if .array_size is 0  	 *   	 * Assuming the kernel writes into the array, if 1, the number @@ -2333,18 +2457,12 @@ struct libsyscalls_structure_description {  	unsigned short int fill_is_known : 1;  	/** -	 * If 1, the data type is a `union`, -	 * if 0, the data type is a `struct` -	 */ -	unsigned short int is_union : 1; - -	/**  	 * The size (in number of elements) of the array the data type  	 * is used in, or 1 if the data type was not an array or  	 * 0 if the array size is determined by another parameter/field  	 *   	 * If 1, .relative_position_of_size will be set to 0, -	 * and .absolute_position_of_size -1, hoever if that is not +	 * and .absolute_position_of_size -1, however if that is not  	 * the case and this field is set to 1, then .size (which may  	 * or may not be 0 at this point) is determined by the indicated  	 * parameter @@ -2441,7 +2559,7 @@ libsyscalls_perror(const char *, enum libsyscalls_error);   * (not [*min_out, *max_out - 1]) but there may still be numbers within   * this range that does not correspond to a system call   */ -LIBSYSCALLS_GCC_ATTRIBUTES_(__warn_unused_result__) +LIBSYSCALLS_GCC_ATTRIBUTES_(__warn_unused_result__, __access__(__write_only__, 3), __access__(__write_only__, 4))  enum libsyscalls_error  libsyscalls_get_syscall_range(enum libsyscalls_os, enum libsyscalls_arch, long long int *, long long int *); @@ -2468,7 +2586,7 @@ libsyscalls_get_syscall_range(enum libsyscalls_os, enum libsyscalls_arch, long l   * of operating system (`os`) and architecture (`arch`) is   * unsupported   */ -LIBSYSCALLS_GCC_ATTRIBUTES_(__warn_unused_result__) +LIBSYSCALLS_GCC_ATTRIBUTES_(__warn_unused_result__, __access__(__write_only__, 4))  enum libsyscalls_error  libsyscalls_get_syscall(enum libsyscalls_os, enum libsyscalls_arch, long long int, const struct libsyscalls_syscall **); @@ -2498,6 +2616,7 @@ libsyscalls_get_syscall(enum libsyscalls_os, enum libsyscalls_arch, long long in   * is not supported, however it may be successful even if the   * architecture (`arch`) not supported   */ +LIBSYSCALLS_GCC_ATTRIBUTES_(__access__(__write_only__, 3), __access__(__write_only__, 4))  enum libsyscalls_error  libsyscalls_get_syscall_errors(enum libsyscalls_os, enum libsyscalls_arch, const struct libsyscalls_named_number **, size_t *); @@ -2527,6 +2646,7 @@ libsyscalls_get_syscall_errors(enum libsyscalls_os, enum libsyscalls_arch, const   * is not supported, however it may be successful even if the   * architecture (`arch`) not supported   */ +LIBSYSCALLS_GCC_ATTRIBUTES_(__access__(__write_only__, 3), __access__(__write_only__, 4))  enum libsyscalls_error  libsyscalls_get_signals(enum libsyscalls_os, enum libsyscalls_arch, const struct libsyscalls_named_number **, size_t *); @@ -2542,7 +2662,11 @@ libsyscalls_get_signals(enum libsyscalls_os, enum libsyscalls_arch, const struct   * @param   syscall_arguments  The values store in register (as upon system call entry)   *                             the used for the system call, in the order the registers   *                             are used as system call arguments - * @param   info_out           Output parameter for the additional system call information + * @param   info_out           Output parameter for the additional system call information; + *                             the caller responsible for deallocating the `*info_out` + *                             with free(3) when it is no longer needed. Be aware that + *                             `*info_out` will only be set when LIBSYSCALLS_E_OK is + *                             returned.   * @return                     LIBSYSCALLS_E_OK         - On success   *                             LIBSYSCALLS_E_OSNOSUP    - The library is not compiled with support for   *                                                        the selected operating system (`os`) @@ -2552,17 +2676,23 @@ libsyscalls_get_signals(enum libsyscalls_os, enum libsyscalls_arch, const struct   *                             LIBSYSCALLS_E_NOMEM      - Failed to allocate memory for `*info_out`   *                             LIBSYSCALLS_E_INVAL      - One of the arguments was NULL   */ -LIBSYSCALLS_GCC_ATTRIBUTES_(__warn_unused_result__, __nonnull__) +LIBSYSCALLS_GCC_ATTRIBUTES_(__warn_unused_result__, __nonnull__, __access__(__read_only__, 3), __access__(__write_only__, 6))  enum libsyscalls_error  libsyscalls_get_syscall_display_info(enum libsyscalls_os, enum libsyscalls_arch, const struct libsyscalls_syscall_abi *, -                                     long long int, unsigned long long int *, struct libsyscalls_syscall_display_info **); +                                     long long int, const unsigned long long int *, struct libsyscalls_syscall_display_info **);  /** - * Get information on how an instance of data type is to be parsed + * Get information on how an instance of non-struct, non-union + * data type is to be parsed + *  + * Do not use for data types inside structures, + * instead, use `.type_description.nonstructure` + * in the structure's field, as it may contain + * important adjustments   *    * @param   os               The operating system the data type is used on   * @param   arch             The architecture the data type is used on - * @param   datatype         The datatype, `LIBSYSCALLS_TYPE_DYNAMIC` can + * @param   datatype         The data type, `LIBSYSCALLS_TYPE_DYNAMIC` can   *                           be used to get the size of the registers used   *                           as system call parameters   * @param   description_out  Output parameter for the type description @@ -2575,13 +2705,14 @@ libsyscalls_get_syscall_display_info(enum libsyscalls_os, enum libsyscalls_arch,   *                           LIBSYSCALLS_E_INVAL      - `datatype` is not a valid data type   *                           LIBSYSCALLS_E_NOSUCHTYPE - `datatype` does not exist on the selected   *                                                      operating system (`os`) or architecture (`arch`) - *                           LIBSYSCALLS_E_ISSTRUCT   - `datatype` represents a structure + *                           LIBSYSCALLS_E_ISSTRUCT   - `datatype` represents a structure or union, + *                                                      or an array of a structure or union   *    * The function may complete successfully for some data   * types even if it for other data types would return   * LIBSYSCALLS_E_OSNOSUP or LIBSYSCALLS_E_ARCHNOSUP   */ -LIBSYSCALLS_GCC_ATTRIBUTES_(__warn_unused_result__) +LIBSYSCALLS_GCC_ATTRIBUTES_(__warn_unused_result__, __access__(__write_only__, 4))  enum libsyscalls_error  libsyscalls_get_datatype_description(enum libsyscalls_os, enum libsyscalls_arch, enum libsyscalls_datatype,                                       struct libsyscalls_datatype_description *); @@ -2625,11 +2756,167 @@ libsyscalls_get_datatype_description(enum libsyscalls_os, enum libsyscalls_arch,   * return LIBSYSCALLS_E_ARCHNOSUP, in such cases, the result   * is indeed reliable   */ -LIBSYSCALLS_GCC_ATTRIBUTES_(__warn_unused_result__) +LIBSYSCALLS_GCC_ATTRIBUTES_(__warn_unused_result__, __access__(__write_only__, 4))  enum libsyscalls_error  libsyscalls_get_integer_alignment(enum libsyscalls_os, enum libsyscalls_arch, unsigned, unsigned *); -/* TODO add libsyscalls_get_struct_description */ +/** + * Parse a string of bits as a signed integer + *  + * In case `representation` is LIBSYSCALLS_SIGN_UNDETERMINED, + * the bits are parsed as an unsigned integer + *  + * @param   value_in        The bits to parse as a signed integer + * @param   representation  The sign representation used on the interger + * @param   bits            The number of bits in the integer + * @param   value_out       Output parameter for the absolute value of the signed integer (may be NULL) + * @param   negative_out    Output parameter for whether the signed integer is negative (may be NULL) + * @return                  LIBSYSCALLS_E_OK    - On success + *                          LIBSYSCALLS_E_INVAL - `representation` is not recognised by the library + *                          LIBSYSCALLS_E_INVAL - `bits` is 0 + *                          LIBSYSCALLS_E_INVAL - `bits` is greater than `sizeof(long long int) * CHAR_BIT` + */ +LIBSYSCALLS_GCC_ATTRIBUTES_(__access__(__write_only__, 4), __access__(__write_only__, 5)) +enum libsyscalls_error +libsyscalls_parse_signed_integer(unsigned long long int, enum libsyscalls_datatype_sign_representation, +                                 size_t, unsigned long long int *, int *); + +/** + * Create a string of bits representing a signed integer + *  + * In case `representation` is LIBSYSCALLS_SIGN_UNDETERMINED, + * the bit string will represent an unsigned integer + *  + * @param   value_in        The absolute value if the signed integer + * @param   negative        Whether the signed integer is negative + * @param   representation  The sign representation used on the interger + * @param   bits            The number of bits in the integer + * @param   value_out       Output parameter for the bit string (may be NULL) + * @return                  LIBSYSCALLS_E_OK    - On success + *                          LIBSYSCALLS_E_INVAL - `representation` is not recognised by the library + *                          LIBSYSCALLS_E_INVAL - `bits` is 0 + *                          LIBSYSCALLS_E_INVAL - `bits` is greater than `sizeof(long long int) * CHAR_BIT` + */ +LIBSYSCALLS_GCC_ATTRIBUTES_(__access__(__write_only__, 5)) +enum libsyscalls_error +libsyscalls_make_signed_integer(unsigned long long int, int, enum libsyscalls_datatype_sign_representation, +                                size_t, unsigned long long int *); + +/** + * Take bits from a section of a split value + * and shift its bit into place, so that the OR + * of the result for each section creates the + * original unsplit value + *  + * If `section` is LIBSYSCALLS_SECTION_UNDETERMINED, + * no changes will be made + *  + * @param   value_in        The contiguous bits in the section of the value + * @param   bits            The number of bits in the section of the value + * @param   section         The section of the value + * @param   value_out       Output parameter for the value with its bits shifted into place (may be NULL) + * @return                  LIBSYSCALLS_E_OK    - On success + *                          LIBSYSCALLS_E_INVAL - `representation` is not recognised by the library + *                          LIBSYSCALLS_E_INVAL - `bits` is invalid for `section` + *                          LIBSYSCALLS_E_INVAL - `bits` is greater than `sizeof(long long int) * CHAR_BIT` + */ +LIBSYSCALLS_GCC_ATTRIBUTES_(__access__(__write_only__, 4)) +enum libsyscalls_error +libsyscalls_unsection_value(unsigned long long int, size_t, enum libsyscalls_datatype_section, unsigned long long int *); + +/** + * Create a split section from a value + *  + * If `section` is LIBSYSCALLS_SECTION_UNDETERMINED, + * no changes will be made + *  + * @param   value_in        The whole value + * @param   bits            The number of bits in the whole value + * @param   section         The section of the value to return + * @param   value_out       Output parameter for bits in the section value shifted into contiguity (may be NULL) + * @return                  LIBSYSCALLS_E_OK    - On success + *                          LIBSYSCALLS_E_INVAL - `representation` is not recognised by the library + *                          LIBSYSCALLS_E_INVAL - `bits` is invalid for `section` + *                          LIBSYSCALLS_E_INVAL - `bits` is greater than `sizeof(long long int) * CHAR_BIT` + */ +LIBSYSCALLS_GCC_ATTRIBUTES_(__access__(__write_only__, 4)) +enum libsyscalls_error +libsyscalls_section_value(unsigned long long int, size_t, enum libsyscalls_datatype_section, unsigned long long int *); + +/** + * Converts a value from the tracee's endian to the tracer's endian + *  + * @param   value_in   Buffer containing the value to convert (does not need to be aligned) + * @param   offset_in  Offset in `value_in`, in bits + * @param   type       Details about the data type + * @param   value_out  Output parameter for the value in the tracer's endian + * @return             LIBSYSCALLS_E_OK    - On success + *                     LIBSYSCALLS_E_INVAL - Either parameter is NULL + *                     LIBSYSCALLS_E_INVAL - The data type is too wide + */ +LIBSYSCALLS_GCC_ATTRIBUTES_(__nonnull__, __access__(__read_only__, 1), __access__(__read_only__, 3), __access__(__write_only__, 4)) +enum libsyscalls_error +libsyscalls_to_tracer_endian(const void *, size_t, const struct libsyscalls_datatype_description *, unsigned long long int *); + +/** + * Converts a value from the tracer's endian to the tracee's endian + *  + * @param   value_in    Buffer containing the value to convert + * @param   type        Details about the data type + * @param   value_out   Output parameter for the value in the tracee's + *                      endian (does not need to be aligned); preexisting + *                      bits that do not overlap with the position of the + *                      value will be retained + * @param   out_offset  Offset in `value_out`, in bits + * @return              LIBSYSCALLS_E_OK    - On success + *                      LIBSYSCALLS_E_INVAL - Either parameter is NULL + *                      LIBSYSCALLS_E_INVAL - The data type is too wide + */ +LIBSYSCALLS_GCC_ATTRIBUTES_(__nonnull__, __access__(__read_only__, 2), __access__(__read_write__, 3)) +enum libsyscalls_error +libsyscalls_to_tracee_endian(unsigned long long int, const struct libsyscalls_datatype_description *, void *, size_t); + +/** + * Get information on how an instance of struct or union is to be parsed + *  + * Do not use for structures inside other structures, + * instead, use `.type_description.structure` in the + * structure's field, as it may contain important + * adjustments + *  + * @param   os               The operating system the data type is used on + * @param   arch             The architecture the data type is used on + * @param   datatype         The data type + * @param   data             The data that is to be parsed (may be `NULL` if `data_size` is 0) + * @param   data_size        The number of bytes available in `data`, may be short or in excess + * @param   description_out  Output parameter for the type description; + *                           the caller responsible for deallocating the `*info_out` + *                           with free(3) when it is no longer needed. Be aware that + *                           `*info_out` will only be set when LIBSYSCALLS_E_OK is + *                           returned. + * @return                   LIBSYSCALLS_E_OK          - On success + *                           LIBSYSCALLS_E_OSNOSUP     - The library is not compiled with support for + *                                                       the selected operating system (`os`) + *                           LIBSYSCALLS_E_ARCHNOSUP   - The library is not compiled with support for + *                                                       the selected architecture (`arch`) on the + *                                                       selected operating system (`os`) + *                           LIBSYSCALLS_E_INVAL       - `datatype` is not a valid data type + *                           LIBSYSCALLS_E_NOSUCHTYPE  - `datatype` does not exist on the selected + *                                                       operating system (`os`) or architecture (`arch`) + *                           LIBSYSCALLS_E_ISNOTSTRUCT - `datatype` does not represent a structure or + *                                                       union, nor an array of structure or union + *  + * The function may complete successfully for some data + * types even if it for other data types would return + * LIBSYSCALLS_E_ARCHNOSUP + */ +#if 0 /* work in progress */ +LIBSYSCALLS_GCC_ATTRIBUTES_(__warn_unused_result__, __access__(__read_only__, 4, 5), __access__(__write_only__, 6)) +enum libsyscalls_error +libsyscalls_get_struct_description(enum libsyscalls_os, enum libsyscalls_arch, enum libsyscalls_datatype, +                                   const void *, size_t, struct libsyscalls_structure_description **); +#endif +  /* TODO add libsyscalls_get_struct_display_info */ diff --git a/libsyscalls/internal-begin.h b/libsyscalls/internal-begin.h index 0d89bb7..56faadf 100644 --- a/libsyscalls/internal-begin.h +++ b/libsyscalls/internal-begin.h @@ -8,6 +8,13 @@  #include <stddef.h> +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdocumentation" +# pragma clang diagnostic ignored "-Wunknown-attributes" +#endif + +  /**   * Opaque type for symbol printer function identification   */ diff --git a/libsyscalls/internal-end.h b/libsyscalls/internal-end.h index 2aeb2f0..e75a5ce 100644 --- a/libsyscalls/internal-end.h +++ b/libsyscalls/internal-end.h @@ -22,3 +22,8 @@  #undef LIBSYSCALLS_MAKE_ANNOTATED_NUMERICALS_  #undef LIBSYSCALLS_MAKE_STRUCTS_AND_UNIONS_  #undef LIBSYSCALLS_MAKE_FIXED_ARRAYS_ + + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif diff --git a/libsyscalls_get_datatype_description.c b/libsyscalls_get_datatype_description.c index 71a2170..a418eb3 100644 --- a/libsyscalls_get_datatype_description.c +++ b/libsyscalls_get_datatype_description.c @@ -1,56 +1,5 @@  /* See LICENSE file for copyright and license details. */  #include "common.h" -#include <stdlib.h> -#include <string.h> - - -#define COUNT_(...) 1 -#define COUNT(LIST_MACRO) (LIST_MACRO(COUNT_,, +,)) - -enum endian { -	Big, -	Little, -}; - -#define LIST_ARCH_SPECS(X, D) /*          byte  intptr  size_t  endian  sign  */\ -	/* -	X(LIBSYSCALLS_ARCH_ALPHA,         8,    64,     64,     TODO(bi), TWOS_COMPLEMENT) D\ -	*/\ -	X(LIBSYSCALLS_ARCH_AMD64,         8,    64,     64,     Little,   TWOS_COMPLEMENT) D\ -	X(LIBSYSCALLS_ARCH_AMD64_X32,     8,    32,     32,     Little,   TWOS_COMPLEMENT) D /* amd64, 32-bit convesion */\ -	/* -	X(LIBSYSCALLS_ARCH_ARM_OABI,      8,    TODO,   TODO,   TODO(bi), TWOS_COMPLEMENT) D / * TODO (alignment) * / \ -	X(LIBSYSCALLS_ARCH_ARM_EABI,      8,    TODO,   TODO,   TODO(bi), TWOS_COMPLEMENT) D / * TODO (alignment) * /\ -	X(LIBSYSCALLS_ARCH_IA64,          8,    64,     64,     TODO,     TWOS_COMPLEMENT) D\ -	*/\ -	X(LIBSYSCALLS_ARCH_M68K,          8,    32,     32,     Big,      TWOS_COMPLEMENT) D\ -	/* -	X(LIBSYSCALLS_ARCH_MICROBLAZE,    8,    TODO,   TODO,   TODO,     TODO) D / * TODO (alignment) * /\ -	X(LIBSYSCALLS_ARCH_MIPS_O32,      8,    32,     32,     TODO(bi), TWOS_COMPLEMENT) D / * mips32 * /\ -	X(LIBSYSCALLS_ARCH_MIPS_N32,      8,    32,     32,     TODO(bi), TWOS_COMPLEMENT) D / * TODO (alignment) * / / * mips64, 32-bit convention * /\ -	X(LIBSYSCALLS_ARCH_MIPS_N64,      8,    64,     64,     TODO(bi), TWOS_COMPLEMENT) D / * TODO (alignment) * / / * mips64 * /\ -	*/\ -	X(LIBSYSCALLS_ARCH_PARISC_32,     8,    32,     32,     Big,      TWOS_COMPLEMENT) D\ -	X(LIBSYSCALLS_ARCH_PARISC_64,     8,    64,     64,     Big,      TWOS_COMPLEMENT) D\ -	/* -	X(LIBSYSCALLS_ARCH_POWERPC_32,    8,    32,     32,     TODO(bi), TODO) D\ -	X(LIBSYSCALLS_ARCH_POWERPC_64,    8,    64,     64,     TODO(bi), TODO) D / * TODO (alignment) * /\ -	X(LIBSYSCALLS_ARCH_POWERPC_NOSPU, 8,    64,     64,     TODO(bi), TODO) D / * TODO (alignment) * /\ -	X(LIBSYSCALLS_ARCH_POWERPC_SPU,   8,    64,     64,     TODO(bi), TODO) D / * TODO (alignment) * /\ -	X(LIBSYSCALLS_ARCH_S390_32,       8,    32,     32,     Big,      TODO) D / * TODO (alignment) * /\ -	X(LIBSYSCALLS_ARCH_S390_64,       8,    64,     64,     Big,      TODO) D / * TODO (alignment) * /\ -	X(LIBSYSCALLS_ARCH_SH,            8,    32,     32,     TODO(bi), TODO) D / * TODO (alignment) * / / * not sh-5 * /\ -	*/\ -	X(LIBSYSCALLS_ARCH_SPARC_32,      8,    32,     32,     Big,      TWOS_COMPLEMENT) D\ -	/* -	X(LIBSYSCALLS_ARCH_SPARC_64,      8,    64,     64,     TODO(bi), TWOS_COMPLEMENT) D\ -	*/\ -	X(LIBSYSCALLS_ARCH_I386,          8,    32,     32,     Little,   TWOS_COMPLEMENT) D\ -	/* -	X(LIBSYSCALLS_ARCH_XTENSA,        8,    32,     32,     TODO,     TODO) -	*/ -	/* Don't forget to update SUPPORTED_ARCHES in Makefile */ -	/* TODO (alignment) means that it is missing in libsyscalls_get_integer_alignment.c and must also be added thither */  #include "generated/types.c" @@ -154,6 +103,14 @@ libsyscalls_get_datatype_description(enum libsyscalls_os os, enum libsyscalls_ar  			datatype = LIBSYSCALLS_TYPE_UINT32;  			description_out->array_size = 2;  			break; +		case LIBSYSCALLS_TYPE_BUFFER_9: +			datatype = LIBSYSCALLS_TYPE_CHAR; +			description_out->array_size = 9; +			break; +		case LIBSYSCALLS_TYPE_BUFFER_65: +			datatype = LIBSYSCALLS_TYPE_CHAR; +			description_out->array_size = 65; +			break;  		case LIBSYSCALLS_TYPE_FD_SET:  			goto os_dependent;  		default: @@ -190,7 +147,7 @@ libsyscalls_get_datatype_description(enum libsyscalls_os os, enum libsyscalls_ar  	} else if (datatype >= LIBSYSCALLS_TYPEOFFSET_UNANNOTATED_NUMERICALS) {  	unannotated: -		if (datatype - LIBSYSCALLS_TYPEOFFSET_UNANNOTATED_NUMERICALS >= COUNT(LIBSYSCALLS_LIST_UNANNOTATED_NUMERICALS)) { +		if (datatype - LIBSYSCALLS_TYPEOFFSET_UNANNOTATED_NUMERICALS >= COUNT_LIST(LIBSYSCALLS_LIST_UNANNOTATED_NUMERICALS)) {  			return LIBSYSCALLS_E_INVAL;  		} else if (datatype == LIBSYSCALLS_TYPE_MEMORY_ADDRESS) {  			datatype = LIBSYSCALLS_TYPE_INTPTR; @@ -206,7 +163,7 @@ libsyscalls_get_datatype_description(enum libsyscalls_os os, enum libsyscalls_ar  	} else if (datatype >= LIBSYSCALLS_TYPEOFFSET_COMPOSITE_PRIMITIVES) {  		if (class != LIBSYSCALLS_TYPEBITS_SCALAR)  			return LIBSYSCALLS_E_INVAL; -		if (datatype - LIBSYSCALLS_TYPEOFFSET_COMPOSITE_PRIMITIVES >= COUNT(LIBSYSCALLS_LIST_COMPOSITE_PRIMITIVES)) +		if (datatype - LIBSYSCALLS_TYPEOFFSET_COMPOSITE_PRIMITIVES >= COUNT_LIST(LIBSYSCALLS_LIST_COMPOSITE_PRIMITIVES))  			return LIBSYSCALLS_E_INVAL;  		datatype = LIBSYSCALLS_TYPE_MEMORY_ADDRESS;  		goto unannotated; @@ -376,8 +333,6 @@ not_os_dependent:  #if defined(__clang__)  # pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wunsafe-buffer-usage" -# pragma clang diagnostic push  # pragma clang diagnostic ignored "-Wimplicit-int-conversion" /* we don't want to hardcode the type here */  #elif defined(__GNUC__)  # pragma GCC diagnostic push @@ -406,23 +361,20 @@ not_os_dependent:  	if (half) {  		unsigned long long int bytemask, coverage = 0;  		unsigned long long int remmask; -		unsigned char bytebits = UCHAR_MAX; +		unsigned long long int bytebits; -		for (i = 0; !LIBSYSCALLS_IS_BYTEORDER_END(larger_type.byteorder[i]); i++) -			if (larger_type.byteorder[i] && larger_type.byteorder[i] < bytebits) -				bytebits = larger_type.byteorder[i]; +		bytebits = (unsigned long long int)larger_type.width_in_bits; +		for (i = 0; !LIBSYSCALLS_IS_BYTEORDER_END_AT(&larger_type, i); i++) +			if (larger_type.byteorder[i] && (unsigned long long int)larger_type.byteorder[i] < bytebits) +				bytebits = (unsigned long long int)larger_type.byteorder[i];  		bytemask = (1ULL << bytebits) - 1ULL;  		remmask = 0xFFFFFFFFULL; /* we known from the code above that we are working with 32-bit sections */ -		for (i = 0; !LIBSYSCALLS_IS_BYTEORDER_END(larger_type.byteorder[i]); i++) { +		for (i = 0; !LIBSYSCALLS_IS_BYTEORDER_END_AT(&larger_type, i); i++) {  			coverage |= (remmask & bytemask) << larger_type.byteorder[i];  			remmask >>= bytebits;  		} -#if defined(__clang__) -# pragma clang diagnostic pop -#endif -  		/* we known from the code above that we are working with split 64-bit integers */  		if (half == +1)  			coverage ^= 0xFFFFFFFFFFFFFFFFull; diff --git a/libsyscalls_get_integer_alignment.c b/libsyscalls_get_integer_alignment.c index 7802a06..1c2d10e 100644 --- a/libsyscalls_get_integer_alignment.c +++ b/libsyscalls_get_integer_alignment.c @@ -19,20 +19,50 @@ libsyscalls_get_integer_alignment(enum libsyscalls_os os, enum libsyscalls_arch  		break;  	case LIBSYSCALLS_ARCH_I386: +	case LIBSYSCALLS_ARCH_ARM_OABI_LE: /* https://wiki.debian.org/ArmEabiPort#A64-bit_data_type_alignment */ +	case LIBSYSCALLS_ARCH_ARM_OABI_BE: +	case LIBSYSCALLS_ARCH_SH_LE: /* https://www.st.com/resource/en/reference_manual/rm0197-sh4-generic-and-c-specific-application-binary-interface-stmicroelectronics.pdf (page 12) */ +	case LIBSYSCALLS_ARCH_SH_BE: +	case LIBSYSCALLS_ARCH_MICROBLAZE_32_LE: /* https://www.ecb.torontomu.ca/~courses/ee8205/Data-Sheets/sopc/MicroBlaze_DataSheet.pdf */ +	case LIBSYSCALLS_ARCH_MICROBLAZE_32_BE:  		maxalign = 32;  		break;  	case LIBSYSCALLS_ARCH_AMD64:  	case LIBSYSCALLS_ARCH_AMD64_X32: -	case LIBSYSCALLS_ARCH_ALPHA: /* https://static.lwn.net/images/pdf/LDD3/ch11.pdf */ -	case LIBSYSCALLS_ARCH_IA64: /* https://static.lwn.net/images/pdf/LDD3/ch11.pdf */ -	case LIBSYSCALLS_ARCH_MIPS_O32: /* https://static.lwn.net/images/pdf/LDD3/ch11.pdf */ +	case LIBSYSCALLS_ARCH_ARM_EABI_LE: /* https://wiki.debian.org/ArmEabiPort#A64-bit_data_type_alignment */ +	case LIBSYSCALLS_ARCH_ARM_EABI_BE: +	case LIBSYSCALLS_ARCH_ALPHA_LE: /* https://static.lwn.net/images/pdf/LDD3/ch11.pdf */ +	case LIBSYSCALLS_ARCH_ALPHA_BE: +	case LIBSYSCALLS_ARCH_IA64_LE: /* https://static.lwn.net/images/pdf/LDD3/ch11.pdf */ +	case LIBSYSCALLS_ARCH_IA64_BE: +	case LIBSYSCALLS_ARCH_IA64_P32_LE: +	case LIBSYSCALLS_ARCH_IA64_P32_BE: +	case LIBSYSCALLS_ARCH_MIPS_O32_LE: /* https://static.lwn.net/images/pdf/LDD3/ch11.pdf */ +	case LIBSYSCALLS_ARCH_MIPS_O32_BE: +	case LIBSYSCALLS_ARCH_MIPS_N32_LE: /* https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MD00083-2B-MIPS64INT-AFP-05.04.pdf (page 40) */ +	case LIBSYSCALLS_ARCH_MIPS_N32_BE: +	case LIBSYSCALLS_ARCH_MIPS_N64_LE: +	case LIBSYSCALLS_ARCH_MIPS_N64_BE:  	case LIBSYSCALLS_ARCH_PARISC_32: /* https://www.ece.lsu.edu/ee4720/doc/pa1.1.pdf (page 26) */  	case LIBSYSCALLS_ARCH_PARISC_64: -	case LIBSYSCALLS_ARCH_POWERPC_32: /* https://static.lwn.net/images/pdf/LDD3/ch11.pdf */ +	case LIBSYSCALLS_ARCH_POWERPC_32_LE: /* https://static.lwn.net/images/pdf/LDD3/ch11.pdf */ +	case LIBSYSCALLS_ARCH_POWERPC_32_BE: +	case LIBSYSCALLS_ARCH_POWERPC_64_LE: /* https://www.slac.stanford.edu/exp/npa/software/eabi_app.pdf */ +	case LIBSYSCALLS_ARCH_POWERPC_64_BE: +	case LIBSYSCALLS_ARCH_POWERPC_NOSPU_LE: +	case LIBSYSCALLS_ARCH_POWERPC_NOSPU_BE: +	case LIBSYSCALLS_ARCH_POWERPC_SPU_LE: +	case LIBSYSCALLS_ARCH_POWERPC_SPU_BE: +	case LIBSYSCALLS_ARCH_S390_32: /* https://refspecs.linuxbase.org/ELF/zSeries/lzsabi0_s390.html */ +	case LIBSYSCALLS_ARCH_S390_64:  	case LIBSYSCALLS_ARCH_SPARC_32: /* https://www.gaisler.com/doc/sparcv8.pdf (page 46) */ -	case LIBSYSCALLS_ARCH_SPARC_64: /* https://static.lwn.net/images/pdf/LDD3/ch11.pdf */ -	case LIBSYSCALLS_ARCH_XTENSA: /* https://loboris.eu/ESP32/Xtensa_lx%20Overview%20handbook.pdf (page 97) */ +	case LIBSYSCALLS_ARCH_SPARC_64_LE: /* https://static.lwn.net/images/pdf/LDD3/ch11.pdf */ +	case LIBSYSCALLS_ARCH_SPARC_64_BE: +	case LIBSYSCALLS_ARCH_XTENSA_LE: /* https://loboris.eu/ESP32/Xtensa_lx%20Overview%20handbook.pdf (page 97) */ +	case LIBSYSCALLS_ARCH_XTENSA_BE: +	case LIBSYSCALLS_ARCH_MICROBLAZE_64_LE: /* https://www.amd.com/content/dam/xilinx/support/documents/sw_manuals/xilinx2021_2/ug984-vivado-microblaze-ref.pdf */ +	case LIBSYSCALLS_ARCH_MICROBLAZE_64_BE:  		maxalign = 64;  		break; diff --git a/libsyscalls_get_struct_description.c b/libsyscalls_get_struct_description.c new file mode 100644 index 0000000..4d9ce68 --- /dev/null +++ b/libsyscalls_get_struct_description.c @@ -0,0 +1,656 @@ +/* See LICENSE file for copyright and license details. */ +#define REQUIRE_FLEXABLE_OR_NFIELDS +#include "common.h" + + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-macros" +#endif + +#define IN .input_field = 1 +#define OUT .output_field = 1 +#define ERR .error_field = 1 +#define SYM .symbolic_field = 1 +#define PARTIAL .partial_init_ok = 1 +#define INPTR .input_pointer = 1 +#define OUTPTR .output_pointer = 1 + +#define BITFIELD(N) .bitlength = (N) +#define FIELD(NAME, TYPE, ...)\ +	{.name = NAME,\ +	 .type = LIBSYSCALLS_TYPE_##TYPE\ +	 __VA_OPT__(, __VA_ARGS__)} +#define STRUCT(PRINTER, ...) STRUCT_OR_UNION_(0, PRINTER __VA_OPT__(, __VA_ARGS__)) +#define UNION(PRINTER, ...) STRUCT_OR_UNION_(1, PRINTER __VA_OPT__(, __VA_ARGS__)) +#define STRUCT_OR_UNION_(IS_UNION, PRINTER, ...)\ +	{.is_union = (IS_UNION),\ +	 .symbol_printer = (PRINTER),\ +	 .num_fields = COUNT_ARGS(__VA_ARGS__)\ +	 __VA_OPT__(, .fields = {__VA_ARGS__})} + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + +struct padding { +	unsigned short int size; +	unsigned short int after; +}; + + +static enum libsyscalls_error +get_struct_description(enum libsyscalls_os os, enum libsyscalls_arch arch, enum libsyscalls_datatype datatype, const void *data, +                       size_t data_size, struct libsyscalls_structure_description **description_out, size_t *extra_sizep); + + +static struct libsyscalls_structure_description * +copy_struct(const struct libsyscalls_structure_description *prototype) +{ +	struct libsyscalls_structure_description *ret; +	size_t size = offsetof(struct libsyscalls_structure_description, fields); +	size += prototype->num_fields * sizeof(*prototype->fields); +	ret = malloc(size); +	if (!ret) +		return NULL; +	memcpy(ret, prototype, size); +	return ret; +} + + +static struct libsyscalls_datatype_description * +create_pad_description(enum libsyscalls_os os, enum libsyscalls_arch arch, unsigned short int size) +{ +	struct libsyscalls_datatype_description *ret; +	ret = calloc(1, sizeof(*ret)); +	if (!ret) +		return NULL; +	if (libsyscalls_get_datatype_description(os, arch, LIBSYSCALLS_TYPE_CHAR, ret)) +		abort(); +	ret->width_in_bits = size; +	return ret; +} + + +static enum libsyscalls_error +shift_fields(enum libsyscalls_os os, enum libsyscalls_arch arch, struct libsyscalls_structure_description *description, +             const struct padding *paddings, size_t npaddings) +{ +	signed short int shift, map[description->num_fields], abs, rel; +	unsigned short int i, j, k; +	int is_struct; + +	/* Adjust field references */ +	for (i = 0; i < description->num_fields; i++) +		map[i] = (signed short int)i; +	shift = 0; +	i = j = 0; +	while (i < (unsigned short int)npaddings && j < description->num_fields) { +		if (j > paddings[i].after) +			shift += 1; +		map[j] += shift; +	} +	for (; j < description->num_fields; j++) +		map[j] += shift; +	for (i = 0; i < description->num_fields; i++) { +		is_struct = (description->fields[i].type & ~LIBSYSCALLS_TYPEBITSMASK) >= LIBSYSCALLS_TYPEOFFSET_STRUCTS_AND_UNIONS; + +		if (is_struct) { +			abs = description->fields[i].type_description.structure->absolute_position_of_size; +			rel = description->fields[i].type_description.structure->relative_position_of_size; +		} else { +			abs = description->fields[i].type_description.nonstructure->absolute_position_of_array_size; +			rel = description->fields[i].type_description.nonstructure->relative_position_of_array_size; +		} + +		if (abs >= 0) { +			if ((unsigned short int)abs > description->num_fields) +				abort(); +			abs = map[abs]; +		} +		if (rel != 0) { +			if (rel + (signed short int)i < 0) +				abort(); +			if ((unsigned short int)(rel + (signed short int)i) > description->num_fields) +				abort(); +			rel = map[rel + (signed short int)i]; +			rel -= (signed short int)i; +		} + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif +		if (is_struct) { +			description->fields[i].type_description.structure->absolute_position_of_size = abs; +			description->fields[i].type_description.structure->relative_position_of_size = rel; +		} else { +			description->fields[i].type_description.nonstructure->absolute_position_of_array_size = abs; +			description->fields[i].type_description.nonstructure->relative_position_of_array_size = rel; +		} +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif +	} + +	/* Insert padding fields */ +	i = description->num_fields; +	j = i + (unsigned short int)shift; +	k = (unsigned short int)(npaddings - 1); +	while (i) { +		memcpy(&description->fields[--j], &description->fields[--i], sizeof(*description->fields)); +		while (i <= paddings[k].after) { +			j--; +			description->size += paddings[k].size; +			memset(&description->fields[j], 0, sizeof(*description->fields)); +			description->fields[j].name = NULL; +			description->fields[j].type = LIBSYSCALLS_TYPE_VOID; +			description->fields[j].unused = 1; +			description->fields[j].type_description.nonstructure = create_pad_description(os, arch, paddings[k].size); +			if (!description->fields[j].type_description.structure) +				return LIBSYSCALLS_E_NOMEM; +			if (!k--) +				return LIBSYSCALLS_E_OK; +		} +	} + +	return LIBSYSCALLS_E_OK; +} + + +static void +free_subdescriptions(struct libsyscalls_structure_description *description) +{ +	unsigned short int i; +	for (i = 0; i < description->num_fields; i++) { +		if ((description->fields[i].type & ~LIBSYSCALLS_TYPEBITSMASK) >= LIBSYSCALLS_TYPEOFFSET_STRUCTS_AND_UNIONS) +			free_subdescriptions(description->fields[i].type_description.structure); +		free(description->fields[i].type_description.structure); +	} +} + + +static void +move_in_subdescriptions(char *buffer, size_t *offsetp, struct libsyscalls_structure_description *description, +                        int include_nonstructures, int include_structures) +{ +	unsigned short int i; +	size_t size; +	for (i = 0; i < description->num_fields; i++) { +		if ((description->fields[i].type & ~LIBSYSCALLS_TYPEBITSMASK) >= LIBSYSCALLS_TYPEOFFSET_STRUCTS_AND_UNIONS) { +			move_in_subdescriptions(buffer, offsetp, description->fields[i].type_description.structure, +			                        include_nonstructures, include_structures); +			if (!include_structures) +				continue; +			size = (size_t)description->fields[i].type_description.structure->num_fields; +			size *= sizeof(*description->fields[i].type_description.structure->fields); +			size += offsetof(struct libsyscalls_structure_description, fields); +		} else { +			if (!include_nonstructures) +				continue; +			size = sizeof(struct libsyscalls_datatype_description); +		} +		memcpy(&buffer[*offsetp], description->fields[i].type_description.structure, size); +		free(description->fields[i].type_description.structure); +		description->fields[i].type_description.structure = (void *)&buffer[*offsetp]; +		*offsetp += size; +	} +} + + +static unsigned long long int +read_field(const void *data, size_t data_size, const size_t *field_offsets, +           const struct libsyscalls_structure_description *description, unsigned short int i, int *undetermined_out) +{ +	size_t offset, width, bits = 0; +	enum libsyscalls_datatype type = description->fields[i].type; +	const struct libsyscalls_datatype_description *type_description = description->fields[i].type_description.nonstructure; +	unsigned splits, expected_splits, end; +	unsigned long long int result = 0, subresult; +	signed long long int sresult; +	int is_negative; + +	if ((type & LIBSYSCALLS_TYPEBITSMASK) != LIBSYSCALLS_TYPEBITS_SCALAR) +		abort(); +	type ^= LIBSYSCALLS_TYPEBITS_SCALAR; +	if (type >= LIBSYSCALLS_TYPEOFFSET_SPLIT_PRIMITIVES && type < LIBSYSCALLS_TYPEOFFSET_COMPOSITE_PRIMITIVES) +		splits = LIBSYSCALLS_GET_SECTION_FRACTION(type_description->section); +	else if (type >= LIBSYSCALLS_TYPEOFFSET_UNANNOTATED_NUMERICALS && type < LIBSYSCALLS_TYPEOFFSET_FIXED_ARRAYS) +		splits = 1; +	else +		abort(); + +	expected_splits = splits; +	end = i + splits; +	goto start; + +	for (; i < end; i++) { +		if ((type & LIBSYSCALLS_TYPEBITSMASK) != LIBSYSCALLS_TYPEBITS_SCALAR) +			abort(); +		type ^= LIBSYSCALLS_TYPEBITS_SCALAR; +		if (type >= LIBSYSCALLS_TYPEOFFSET_SPLIT_PRIMITIVES && type < LIBSYSCALLS_TYPEOFFSET_COMPOSITE_PRIMITIVES) +			splits = LIBSYSCALLS_GET_SECTION_FRACTION(type_description->section); +		else if (type >= LIBSYSCALLS_TYPEOFFSET_UNANNOTATED_NUMERICALS && type < LIBSYSCALLS_TYPEOFFSET_FIXED_ARRAYS) +			splits = 1; +		else +			abort(); + +		if (splits != expected_splits) +			abort(); + +	start: +		offset = field_offsets[i]; +		width = (size_t)type_description->width_in_bits; +		bits += width; + +		if (offset > data_size || width > data_size - offset) +			goto undetermined; + +		if (libsyscalls_to_tracer_endian(&((const char *)data)[offset / CHAR_BIT], offset % CHAR_BIT, +		                                 type_description, &subresult)) +			abort(); + +		if (libsyscalls_unsection_value(subresult, width, type_description->section, &subresult)) +			abort(); + +		result |= subresult; +	} + +	if (type_description->is_signed) { +		if (libsyscalls_parse_signed_integer(result, type_description->sign_representation, bits, &result, &is_negative)) +			abort(); +		if (is_negative) { +#if LLONG_MIN == -LLONG_MAX +			if (result >> (bits - 1)) +				goto undetermined; +#endif +			sresult = (signed long long int)result; +			sresult = -sresult; +		} +		result = *(unsigned long long int *)&sresult; +	} + +	return result; + +undetermined: +	*undetermined_out = 1; +	return 0; +} + + +static enum libsyscalls_error +pad_structure_into_alignment(struct padding **paddingsp, size_t *npaddingsp, size_t *paddings_sizep, +                             const struct libsyscalls_structure_description *description) +{ +	unsigned short int alignment_mask, misalignment; +	void *new; + +	alignment_mask = (unsigned short int)(description->alignment - 1U); +	misalignment = (unsigned short int)(description->size & alignment_mask); +	if (misalignment) { +		if (*npaddingsp == *paddings_sizep) { +			*paddings_sizep += 16; +			new = realloc(*paddingsp, *paddings_sizep * sizeof(**paddingsp)); +			if (!new) +				return LIBSYSCALLS_E_NOMEM; +			*paddingsp = new; +		} +		(*paddingsp)[*npaddingsp].size = (unsigned short int)(description->alignment - misalignment); +		(*paddingsp)[*npaddingsp].after = description->num_fields; +		*npaddingsp += 1; +	} + +	return LIBSYSCALLS_E_OK; +} + + +static enum libsyscalls_error +pad_field_into_alignment(struct padding **paddingsp, size_t *npaddingsp, size_t *paddings_sizep, +                         const struct libsyscalls_structure_description *description, +			 unsigned short int i, unsigned subalign, size_t *field_offsetp) +{ +	unsigned short int misalignment, alignment_mask, padding; +	void *new; + +	alignment_mask = (unsigned short int)(subalign - 1U); +	misalignment = (unsigned short int)(description->size & alignment_mask); + +	if (misalignment) { +		if (i == 0) +			abort(); +		padding = (unsigned short int)(subalign - (unsigned)misalignment); +		if (*npaddingsp == *paddings_sizep) { +			*paddings_sizep += 16; +			new = realloc(*paddingsp, *paddings_sizep * sizeof(**paddingsp)); +			if (!new) +				return LIBSYSCALLS_E_NOMEM; +			*paddingsp = new; +		} +		*field_offsetp += (size_t)padding; +		(*paddingsp)[*npaddingsp].size = (unsigned short int)padding; +		(*paddingsp)[*npaddingsp].after = (unsigned short int)(i - 1U); +		*npaddingsp += 1; +	} + +	return LIBSYSCALLS_E_OK; +} + + +static enum libsyscalls_error +apply_padding(enum libsyscalls_os os, enum libsyscalls_arch arch, struct padding *paddings, +              size_t npaddings, struct libsyscalls_structure_description **descriptionp) +{ +	struct libsyscalls_structure_description *description = *descriptionp; +	enum libsyscalls_error r; +	size_t size; + +	if (npaddings && description->alignment > 1) { +		size = offsetof(struct libsyscalls_structure_description, fields); +		size += (size_t)(description->num_fields + npaddings) * sizeof(*description->fields); +		description = realloc(description, size); +		if (!description) +			return LIBSYSCALLS_E_NOMEM; +		*descriptionp = description; + +		r = shift_fields(os, arch, description, paddings, npaddings); +		if (r) +			return r; +	} + +	return LIBSYSCALLS_E_OK; +} + + +static enum libsyscalls_error +adjust_for_nonstruct(enum libsyscalls_os os, enum libsyscalls_arch arch, +                     const struct libsyscalls_datatype_description *int_description, +                     struct libsyscalls_structure_description *description, unsigned short int i, +                     unsigned *subsizep, unsigned *subalignp, size_t *extra_sizep) +{ +	enum libsyscalls_error r; + +	*subsizep = int_description->width_in_bits; +	/* TODO deal with array_size, keep in mind that it can be 0 because we have (type[]) or (type *); be aware of padding */ +	r = libsyscalls_get_integer_alignment(os, arch, *subsizep, subalignp); +	if (r) +		return r; + +	description->fields[i].type_description.nonstructure = malloc(sizeof(*int_description)); +	if (!description->fields[i].type_description.nonstructure) +		return LIBSYSCALLS_E_NOMEM; +	memcpy(description->fields[i].type_description.nonstructure, int_description, sizeof(*int_description)); +	*extra_sizep += sizeof(*int_description); + +	return LIBSYSCALLS_E_OK; +} + + +static enum libsyscalls_error +adjust_for_struct(enum libsyscalls_os os, enum libsyscalls_arch arch, const void *data, size_t data_size, +                  struct libsyscalls_structure_description *description, unsigned short int i, size_t *field_offsets, +                  unsigned *subsizep, unsigned *subalignp, size_t *extra_sizep, int *undetermined_sizep) +{ +	struct libsyscalls_structure_description *struct_description; +	size_t misalignment, subextra_size; +	unsigned long long int uvalue; +	unsigned short int field; +	enum libsyscalls_error r; + +	r = get_struct_description(os, arch, description->fields[i].type, &((const char *)data)[field_offsets[i]], +				   data_size < field_offsets[i] ? 0 : data_size - field_offsets[i], +				   &struct_description, extra_sizep); +	if (r) +		return r; + +	*subsizep = struct_description->size * struct_description->array_size; +	*subalignp = struct_description->alignment; +	if (struct_description->array_size <= 1) { +		if (struct_description->relative_position_of_size != 0) { +			if (struct_description->relative_position_of_size > 1) +				abort(); +			if ((unsigned short int)-struct_description->relative_position_of_size > i) +				abort(); +			field = (unsigned short int)((ssize_t)i + (ssize_t)struct_description->relative_position_of_size); +			goto set_subsize; +		} else if (struct_description->absolute_position_of_size != -1) { +			field = (unsigned short int)struct_description->absolute_position_of_size; +			if (field > i) +				abort(); +		set_subsize: +			uvalue = read_field(data, data_size, field_offsets, description, i, undetermined_sizep); +			/* TODO set `*subsizep` to `uvalue` (may overflow) */ +		} +		if (!struct_description->array_size) +			*subsizep *= struct_description->size; +	} +	if (!*subsizep) +		*undetermined_sizep = 1; + +	description->fields[i].type_description.structure = struct_description; +	subextra_size = offsetof(struct libsyscalls_structure_description, fields); +	subextra_size += (size_t)struct_description->num_fields * sizeof(*struct_description->fields); +	misalignment = subextra_size % alignof(struct libsyscalls_structure_description); +	misalignment = alignof(struct libsyscalls_structure_description) - misalignment; +	misalignment %= alignof(struct libsyscalls_structure_description); +	subextra_size += misalignment; +	*extra_sizep += subextra_size; + +	return LIBSYSCALLS_E_OK; +} + + +static enum libsyscalls_error +adjust_for_fields(enum libsyscalls_os os, enum libsyscalls_arch arch, const void *data, size_t data_size, +		  struct padding **paddingsp, size_t *npaddingsp, size_t *paddings_sizep, +                  struct libsyscalls_structure_description *description, int dont_align_fields, size_t *extra_sizep) +{ +	size_t field_offsets[description->num_fields]; +	struct libsyscalls_datatype_description int_description; +	int undetermined_size = 0; +	size_t field_offset = 0; +	unsigned short int i; +	unsigned subalign, subsize; +	enum libsyscalls_error r; + +	/* TODO deal with pointer fields */ +	for (i = 0; i < description->num_fields && !undetermined_size; i++) { +		field_offsets[i] = field_offset; +		r = libsyscalls_get_datatype_description(os, arch, description->fields[i].type, &int_description); +		if (r == LIBSYSCALLS_E_OK) +			r = adjust_for_nonstruct(os, arch, &int_description, description, i, +			                         &subsize, &subalign, extra_sizep); +		else if (r == LIBSYSCALLS_E_ISSTRUCT) +			r = adjust_for_struct(os, arch, data, data_size, description, i, field_offsets, +			                      &subsize, &subalign, extra_sizep, &undetermined_size); +		else +			return r; +		if (r) +			return r; + +		/* TODO deal with bitlength */ + +		if (!dont_align_fields) { +			r = pad_field_into_alignment(paddingsp, npaddingsp, paddings_sizep, +			                             description, i, subalign, &field_offset); +			if (r) +				return r; +		} + +		field_offset += subsize; +		description->size += (unsigned short int)subsize; +		if (!dont_align_fields) +			description->alignment = (unsigned short int)MAX(description->alignment, subalign); +	} + +	if (undetermined_size) +		description->size = 0; + +	return LIBSYSCALLS_E_OK; +} + + +#include "generated/structs.c" + + +static enum libsyscalls_error +get_struct_description(enum libsyscalls_os os, enum libsyscalls_arch arch, enum libsyscalls_datatype datatype, const void *data, +                       size_t data_size, struct libsyscalls_structure_description **description_out, size_t *extra_sizep) +{ +	unsigned char fill_is_known; +	unsigned short int array_size; +	signed short int relative_position_of_size; +	signed short int absolute_position_of_size; +	struct libsyscalls_structure_description *description = NULL; +	enum libsyscalls_error r; +	unsigned class; +	int dont_align_fields = 0; +	size_t npaddings = 0, paddings_size = 0; +	struct padding *paddings = NULL; + +	if ((unsigned)datatype & ~(LIBSYSCALLS_TYPEBITSMASK | (LIBSYSCALLS_TYPEBITSMASK - 1U))) +		return LIBSYSCALLS_E_INVAL; + +	datatype ^= class = datatype & LIBSYSCALLS_TYPEBITSMASK; + +	if (class == LIBSYSCALLS_TYPEBITS_SCALAR) { +		fill_is_known = 1; +		array_size = 1; +		relative_position_of_size = 0; +		absolute_position_of_size = -1; + +	} else if (class == LIBSYSCALLS_TYPEBITS_ARRAY) { +		fill_is_known = 1; +		array_size = 0; +		relative_position_of_size = +1; +		absolute_position_of_size = -1; + +	} else if (class == LIBSYSCALLS_TYPEBITS_ARRAY_UNKNOWN_FILL) { +		fill_is_known = 0; +		array_size = 0; +		relative_position_of_size = +1; +		absolute_position_of_size = -1; + +	} else { +		return LIBSYSCALLS_E_INVAL; +	} + +	if (datatype < LIBSYSCALLS_TYPEOFFSET_STRUCTS_AND_UNIONS) +		return LIBSYSCALLS_E_ISNOTSTRUCT; + +#define CASE(UPPERCASE, LOWERCASE)\ +	case LIBSYSCALLS_OS_##UPPERCASE:\ +		r = get_##LOWERCASE##_struct_description(arch, datatype, class, data, data_size,\ +		                                         &description, &dont_align_fields);\ +		break + +	switch ((int)os) { +	LIST_OSES(CASE, ;); +	default: +		r = LIBSYSCALLS_E_OSNOSUP; +		goto fail; +	} +	if (r) +		goto fail; + +#undef CASE + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif + +	description->fill_is_known = fill_is_known; +	description->array_size = array_size; +	description->relative_position_of_size = relative_position_of_size; +	description->absolute_position_of_size = absolute_position_of_size; +	description->alignment = MAX(description->alignment, 1); + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +	r = adjust_for_fields(os, arch, data, data_size, &paddings, &npaddings, &paddings_size, +	                      description, dont_align_fields, extra_sizep); +	if (r) +		goto fail; + +#define CASE(UPPERCASE, LOWERCASE)\ +	case LIBSYSCALLS_OS_##UPPERCASE:\ +		r = fix_##LOWERCASE##_struct_description(arch, datatype, class, data, data_size, description);\ +		break + +	switch ((int)os) { +	LIST_OSES(CASE, ;); +	default: +		abort(); +	} +	if (r) +		goto fail; + +#undef CASE + +	r = pad_structure_into_alignment(&paddings, &npaddings, &paddings_size, description); +	if (r) +		goto fail; + +	r = apply_padding(os, arch, paddings, npaddings, &description); +	if (r) +		goto fail; + +	free(paddings); +	*description_out = description; +	return LIBSYSCALLS_E_OK; + +fail: +	if (description) { +		free_subdescriptions(description); +		free(description); +	} +	free(paddings); +	return r; +} + + +enum libsyscalls_error +libsyscalls_get_struct_description(enum libsyscalls_os os, enum libsyscalls_arch arch, enum libsyscalls_datatype datatype, +                                   const void *data, size_t data_size, struct libsyscalls_structure_description **description_out) +{ +	struct libsyscalls_structure_description *description, *new; +	size_t extra_size = 0, size, nalign, salign, align; +	enum libsyscalls_error r; + +	if (!description_out) +		return LIBSYSCALLS_E_INVAL; + +	r = get_struct_description(os, arch, datatype, data, data_size, &description, &extra_size); +	if (r) +		return r; +	if (!extra_size) { +		*description_out = description; +		return LIBSYSCALLS_E_OK; +	} + +	nalign = alignof(struct libsyscalls_datatype_description); +	salign = alignof(struct libsyscalls_structure_description); +	align = MAX(nalign, salign); + +	size = offsetof(struct libsyscalls_structure_description, fields); +	size += (size_t)description->num_fields * sizeof(*description->fields); +	size += (align - (size & (align - 1))) & (align - 1); + +	new = realloc(description, size + extra_size); +	if (!new) { +		free_subdescriptions(description); +		free(description); +		return LIBSYSCALLS_E_NOMEM; +	} +	description = new; + +	move_in_subdescriptions((void *)description, &size, description, nalign == align, salign == align); +	if (nalign != align || salign != align) +		move_in_subdescriptions((void *)description, &size, description, nalign != align, salign != align); + +	*description_out = description; +	return LIBSYSCALLS_E_OK; +} diff --git a/libsyscalls_get_syscall.c b/libsyscalls_get_syscall.c index aea4d2f..e025e18 100644 --- a/libsyscalls_get_syscall.c +++ b/libsyscalls_get_syscall.c @@ -18,17 +18,8 @@ libsyscalls_get_syscall(enum libsyscalls_os os, enum libsyscalls_arch arch, long  		return LIBSYSCALLS_E_NOSUCHSYSCALL;  	/* buffer access is validated by libsyscalls_get_syscall_range() */ -#if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wunsafe-buffer-usage" -#endif -  	syscalldesc = libsyscalls_syscalls_tables_[os][arch][syscallnr - min]; -#if defined(__clang__) -# pragma clang diagnostic pop -#endif -  	if (!syscalldesc)  		return LIBSYSCALLS_E_NOSUCHSYSCALL;  	if (syscall_out) diff --git a/libsyscalls_get_syscall_display_info.c b/libsyscalls_get_syscall_display_info.c index f9bbf1a..60bbe8a 100644 --- a/libsyscalls_get_syscall_display_info.c +++ b/libsyscalls_get_syscall_display_info.c @@ -1,30 +1,53 @@  /* See LICENSE file for copyright and license details. */  #include "common.h" -#include <stdalign.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#if defined(__GNUC__) -# pragma GCC diagnostic ignored "-Winline" + +static inline signed char +trailing_zeroes(unsigned long long int x) +{ +#ifndef HAVE_BUILTIN_FFSLL +# if defined(__HAVE_BUILTIN_FFSLL) +#  define HAVE_BUILTIN_FFSLL +# elif defined(__clang__) +#  if __clang_major__ >= 5 +#   define HAVE_BUILTIN_FFSLL +#  endif +# elif defined(__GNUC__) +#   if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +#    define HAVE_BUILTIN_FFSLL +#   endif +# endif  #endif +#ifdef HAVE_BUILTIN_FFSLL +# if defined(__GNUC__) +#  pragma GCC diagnostic push +#  pragma GCC diagnostic ignored "-Wsign-conversion" +# endif -#define LOWEST_BIT(X) ((X) & ~((X) - 1)) -#define POST_HIGHEST_OF_CONSECUTIVELY_BITS(X) ((X) + LOWEST_BIT(X)) +	return (signed char)(__builtin_ffsll(x) - 1); /* the type for x has changed over time */ +# if defined(__GNUC__) +#  pragma GCC diagnostic pop +# endif +#else -static inline signed char -trailing_zeroes(unsigned long long int x) { /* TODO use builtin function if available */  	int r = 0;  	if (x == 0)  		return -1;  	for (; (x & 1) == 0; x >>= 1)  		r += 1;  	return (signed char)r; + +#endif  } +#define USE_INTERPOLATION_SEARCH /* TODO validate; should be configurable and (if good) default on systems with intrinsic divsion */ +#ifndef USE_INTERPOLATION_SEARCH + + +PURE_FUNCTION  static int  signed_named_number_cmp(const void *a_, const void *b_)  { @@ -33,6 +56,7 @@ signed_named_number_cmp(const void *a_, const void *b_)  } +PURE_FUNCTION  static int  unsigned_named_number_cmp(const void *a_, const void *b_)  { @@ -41,6 +65,106 @@ unsigned_named_number_cmp(const void *a_, const void *b_)  } +#else + + +/* convertion to unsigned is a modulo (unsigned maximum + 1) operation */ +#define DIFF(TYPE, A, B) ((unsigned TYPE)(A) - (unsigned TYPE)(B)) + +#define INTERPOL_SEARCH(KEY, BASE, N, READ)\ +	do {\ +		double guess_d;\ +		unsigned long long int guess;\ +		size_t h = (N);\ +		\ +		if (!h--)\ +			return NULL;\ +		\ +		if ((KEY) <= READ((BASE), 0))\ +			return (KEY) == READ((BASE), 0) ? (BASE) : NULL;\ +		if ((KEY) >= READ((BASE), h))\ +			return (KEY) == READ((BASE), h) ? (BASE) : NULL;\ +		if (READ((BASE), 0) == READ((BASE), h))\ +			return NULL;\ +		\ +		guess = DIFF(long long int, (KEY), READ((BASE), 0));\ +		if (h > ULLONG_MAX / guess)\ +			goto use_double;\ +		\ +		for (;;) {\ +			guess = DIFF(long long int, (KEY), READ((BASE), 0));\ +			guess *= (unsigned long long int)h;\ +			guess /= DIFF(long long int, READ((BASE), h), READ((BASE), 0));\ +			\ +			if (READ((BASE), guess) < (KEY)) {\ +				h -= guess += 1;\ +				(BASE) = &(BASE)[guess];\ +			} else if (READ((BASE), guess) > (KEY)) {\ +				h -= guess -= 1;\ +			} else {\ +				return &(BASE)[guess];\ +			}\ +			\ +			if (READ((BASE), 0) == READ((BASE), h))\ +				return (KEY) == READ((BASE), 0) ? (BASE) : NULL;\ +			if ((KEY) < READ((BASE), 0))\ +				return NULL;\ +			if ((KEY) > READ((BASE), h))\ +				return NULL;\ +		}\ +		\ +	use_double:\ +		for (;;) {\ +			guess = DIFF(long long int, (KEY), READ((BASE), 0));\ +			guess_d = (double)guess * (double)h;\ +			guess = DIFF(long long int, READ((BASE), h), READ((BASE), 0));\ +			guess_d /= (double)guess;\ +			guess = (unsigned long long int)guess_d;\ +			\ +			if (READ((BASE), guess) < (KEY)) {\ +				h -= guess += 1;\ +				(BASE) = &(BASE)[guess];\ +			} else if (READ((BASE), guess) > (KEY)) {\ +				h -= guess -= 1;\ +			} else {\ +				return &(BASE)[guess];\ +			}\ +			\ +			if (READ((BASE), 0) == READ((BASE), h))\ +				return (KEY) == READ((BASE), 0) ? (BASE) : NULL;\ +			if ((KEY) < READ((BASE), 0))\ +				return NULL;\ +			if ((KEY) > READ((BASE), h))\ +				return NULL;\ +		}\ +	} while (0) + + +PURE_FUNCTION +static const struct libsyscalls_named_number * +interpol_search_signed_named_number(signed long long int key, const struct libsyscalls_named_number *base, size_t n) +{ +#define X(ARR, I) ((ARR)[I].number.s) +	INTERPOL_SEARCH(key, base, n, X); +#undef X +} + + +PURE_FUNCTION +static const struct libsyscalls_named_number * +interpol_search_unsigned_named_number(unsigned long long int key, const struct libsyscalls_named_number *base, size_t n) +{ +#define X(ARR, I) ((ARR)[I].number.u) +	INTERPOL_SEARCH(key, base, n, X); +#undef X +} + +#undef DIFF + + +#endif + +  static const char *  extract_signal(enum libsyscalls_os os, enum libsyscalls_arch arch,                 unsigned long long int *valuep, char *fallback_out, int is_signed) @@ -52,23 +176,23 @@ extract_signal(enum libsyscalls_os os, enum libsyscalls_arch arch,  	if (libsyscalls_get_signals(os, arch, &signals, &nsignals))  		return NULL; -	found = bsearch(&key, signals, nsignals, sizeof(key), /* TODO interpolation search may be better */ +#ifndef USE_INTERPOLATION_SEARCH +	found = bsearch(&key, signals, nsignals, sizeof(key),  	                is_signed ? &signed_named_number_cmp : &unsigned_named_number_cmp); +#else +	found = is_signed ? interpol_search_signed_named_number(key.number.s, signals, nsignals) +	                  : interpol_search_unsigned_named_number(key.number.u, signals, nsignals); +#endif  	if (!found)  		return NULL; -	(void)fallback_out; +	(void) fallback_out;  	*valuep = 0;  	return found->name;  } -#if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wunsafe-buffer-usage" -#endif -  static struct libsyscalls_syscall_display_info *  build_syscall_display_info(void *data, size_t data_size, size_t data_align,                             libsyscalls_symbol_printer_function **funcs, @@ -123,24 +247,15 @@ build_syscall_display_info(void *data, size_t data_size, size_t data_align,  	return ret;  } -#if defined(__clang__) -# pragma clang diagnostic pop -#endif -  #include "generated/symbols.c" - -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic ignored "-Wnonnull-compare" /* Why should I trust that the user is using the same compiler? */ -#endif -  enum libsyscalls_error  libsyscalls_get_syscall_display_info(enum libsyscalls_os os, enum libsyscalls_arch arch,                                       const struct libsyscalls_syscall_abi *syscall,                                       long long int syscall_number, -                                     unsigned long long int *syscall_arguments, +                                     const unsigned long long int *syscall_arguments,                                       struct libsyscalls_syscall_display_info **info_out)  {  	struct libsyscalls_syscall_display_info *info; diff --git a/libsyscalls_make_signed_integer.c b/libsyscalls_make_signed_integer.c new file mode 100644 index 0000000..b4142dc --- /dev/null +++ b/libsyscalls_make_signed_integer.c @@ -0,0 +1,56 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +enum libsyscalls_error +libsyscalls_make_signed_integer(unsigned long long int value_in, int negative, +                                enum libsyscalls_datatype_sign_representation representation, +                                size_t bits, unsigned long long int *value_out) +{ +	unsigned long long int value = value_in, mask, highbit; + +	if (!bits || bits > sizeof(value) / sizeof(char) * CHAR_BIT) +		return LIBSYSCALLS_E_INVAL; + +	switch (representation) { +	case LIBSYSCALLS_SIGN_UNDETERMINED: +		break; + +	case LIBSYSCALLS_SIGN_TWOS_COMPLEMENT: +		if (negative && value) { +			highbit = 1ULL << (bits - 1U); +			mask = highbit - 1ULL; +			value = ~(value - 1) & mask; +			value |= highbit; +		} +		break; + +	case LIBSYSCALLS_SIGN_ONES_COMPLEMENT: +		if (negative) { +			highbit = 1ULL << (bits - 1U); +			mask = highbit - 1ULL; +			value = ~value & mask; +			value |= highbit; +		} +		break; + +	case LIBSYSCALLS_SIGN_SIGN_MAGNITUDE: +		if (negative) +			value ^= 1ULL << (bits - 1); +		break; + +	case LIBSYSCALLS_SIGN_EXCESS_HALF: +		if (!negative) +			value ^= 1ULL << (bits - 1); +		else +			value = (1ULL << (bits - 1)) - value; +		break; + +	default: +		return LIBSYSCALLS_E_INVAL; +	} + +	if (value_out) +		*value_out = value; +	return LIBSYSCALLS_E_OK; +} diff --git a/libsyscalls_parse_signed_integer.c b/libsyscalls_parse_signed_integer.c new file mode 100644 index 0000000..da6f078 --- /dev/null +++ b/libsyscalls_parse_signed_integer.c @@ -0,0 +1,61 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +enum libsyscalls_error +libsyscalls_parse_signed_integer(unsigned long long int value_in, enum libsyscalls_datatype_sign_representation representation, +                                 size_t bits, unsigned long long int *value_out, int *negative_out) +{ +	unsigned long long int value = value_in, mask; +	int negative = 0; + +	if (!bits || bits > sizeof(value) / sizeof(char) * CHAR_BIT) +		return LIBSYSCALLS_E_INVAL; + +	switch (representation) { +	case LIBSYSCALLS_SIGN_UNDETERMINED: +		break; + +	case LIBSYSCALLS_SIGN_TWOS_COMPLEMENT: +		if (value >> (bits - 1U)) { +			mask = (1ULL << (bits - 1U)) - 1ULL; +			value = ~value & mask; +			value += 1ULL; +			negative = 1; +		} +		break; + +	case LIBSYSCALLS_SIGN_ONES_COMPLEMENT: +		if (value >> (bits - 1U)) { +			mask = (1ULL << (bits - 1U)) - 1ULL; +			value = ~value & mask; +			negative = 1; +		} +		break; + +	case LIBSYSCALLS_SIGN_SIGN_MAGNITUDE: +		if (value >> (bits - 1U)) { +			value ^= 1ULL << (bits - 1U); +			negative = 1; +		} +		break; + +	case LIBSYSCALLS_SIGN_EXCESS_HALF: +		if (value >> (bits - 1U)) { +			value ^= 1ULL << (bits - 1U); +		} else { +			value = (1ULL << (bits - 1U)) - value; +			negative = 1; +		} +		break; + +	default: +		return LIBSYSCALLS_E_INVAL; +	} + +	if (value_out) +		*value_out = value; +	if (negative_out) +		*negative_out = negative; +	return LIBSYSCALLS_E_OK; +} diff --git a/libsyscalls_perror.c b/libsyscalls_perror.c index 4b110c7..f4f3a0c 100644 --- a/libsyscalls_perror.c +++ b/libsyscalls_perror.c @@ -1,8 +1,6 @@  /* See LICENSE file for copyright and license details. */  #include "common.h" -#include <stdio.h> -  void  libsyscalls_perror(const char *prefix, enum libsyscalls_error error) diff --git a/libsyscalls_section_value.c b/libsyscalls_section_value.c new file mode 100644 index 0000000..dc86cca --- /dev/null +++ b/libsyscalls_section_value.c @@ -0,0 +1,74 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +enum libsyscalls_error +libsyscalls_section_value(unsigned long long int value_in, size_t bits, +                          enum libsyscalls_datatype_section section, unsigned long long int *value_out) +{ +	unsigned long long int value = value_in, mask, shift; + +	if (bits > sizeof(value) / sizeof(char) * CHAR_BIT || +	    bits & (size_t)(LIBSYSCALLS_GET_SECTION_FRACTION(section) - 1U)) +		return LIBSYSCALLS_E_INVAL; + +	bits /= (size_t)LIBSYSCALLS_GET_SECTION_FRACTION(section); +	if (bits == sizeof(mask) / sizeof(char) * CHAR_BIT) +		mask = ~0ULL; +	else +		mask = (1ULL << bits) - 1ULL; + +	switch (section) { +	case LIBSYSCALLS_SECTION_UNDETERMINED: +	case LIBSYSCALLS_SECTION_WHOLE: /* 0xFFFFFFFFFFFFFFFF */ +	case LIBSYSCALLS_SECTION_LOWER_HALF: /* 0x00000000FFFFFFFF */ +	case LIBSYSCALLS_SECTION_LOWER_QUARTER: /* 0x000000000000FFFF */ +		break; + +	case LIBSYSCALLS_SECTION_UPPER_HALF: /* 0xFFFFFFFF00000000 */ +	case LIBSYSCALLS_SECTION_LOWER_MID_QUARTER: /* 0x00000000FFFF0000 */ +		value >>= bits; +		break; + +	case LIBSYSCALLS_SECTION_INNER_HALF: /* 0x0000FFFFFFFF0000 */ +		value >>= bits / 2; +		break; + +	case LIBSYSCALLS_SECTION_UPPER_MID_QUARTER: /* 0x0000FFFF00000000 */ +		value >>= bits * 2; +		break; + +	case LIBSYSCALLS_SECTION_UPPER_QUARTER: /* 0xFFFF000000000000 */ +		value >>= bits * 3; +		break; + +	case LIBSYSCALLS_SECTION_OUTER_HALF: /* 0xFFFF00000000FFFF */ +		value = ((value & (mask << (bits + bits / 2))) >> bits) | (value & (mask >> (bits / 2))); +		break; + +	case LIBSYSCALLS_SECTION_ODD_QUARTERS_AS_HALF: /* 0xFFFF0000FFFF0000 */ +		value >>= bits / 2; +		/* fall through */ +	case LIBSYSCALLS_SECTION_EVEN_QUARTERS_AS_HALF: /* 0x0000FFFF0000FFFF */ +		value = ((value & (mask << (bits / 2)) & ~mask) >> (bits / 2)) | (value & (mask >> (bits / 2))); +		break; + +	case LIBSYSCALLS_SECTION_ODD_BYTES_AS_HALF: /* 0xFF00FF00FF00FF00 */ +		value_in >>= 8; +		/* fall through */ +	case LIBSYSCALLS_SECTION_EVEN_BYTES_AS_HALF: /* 0x00FF00FF00FF00FF */ +		value = 0; +		for (shift = 0; value_in; value_in >>= 16, shift += 8) +			value |= (value_in & 0x00FFU) << shift; +		break; + +	default: +		return LIBSYSCALLS_E_INVAL; +	} + +	value &= mask; +masked: +	if (value_out) +		*value_out = value; +	return LIBSYSCALLS_E_OK; +} diff --git a/libsyscalls_to_tracee_endian.c b/libsyscalls_to_tracee_endian.c new file mode 100644 index 0000000..94c44eb --- /dev/null +++ b/libsyscalls_to_tracee_endian.c @@ -0,0 +1,60 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/* XXX be smarter if using the same byteorder */ +enum libsyscalls_error +libsyscalls_to_tracee_endian(unsigned long long int value_in, +                             const struct libsyscalls_datatype_description *type, +                             void *value_out, size_t out_offset) +{ +	const unsigned long long int limit = sizeof(long long int) / sizeof(char) * CHAR_BIT; +	unsigned long long int bytebits, bytemask, byte, remmask, valmask; +	unsigned long long int offset, offset_byte, offset_bit; +	unsigned char *out = value_out; +	size_t i; + +	if (!type || !value_out || (unsigned long long int)type->width_in_bits > limit) +		return LIBSYSCALLS_E_INVAL; + +	bytebits = (unsigned long long int)type->width_in_bits; +	for (i = 0; !LIBSYSCALLS_IS_BYTEORDER_END_AT(type, i); i++) +		if (type->byteorder[i] && (unsigned long long int)type->byteorder[i] < bytebits) +			bytebits = (unsigned long long int)type->byteorder[i]; + +	if (bytebits > limit) +		return LIBSYSCALLS_E_INVAL; +	else if (bytebits == limit) +		bytemask = ~0ULL; +	else +		bytemask = (1ULL << bytebits) - 1ULL; + +	if ((unsigned long long int)type->width_in_bits == limit) +		valmask = ~0ULL; +	else +		valmask = (1ULL << (unsigned long long int)type->width_in_bits) - 1ULL; + +	for (i = 0; !LIBSYSCALLS_IS_BYTEORDER_END_AT(type, i); i++) { +		offset = out_offset; +		offset_byte = offset / CHAR_BIT; +		offset_bit = offset % CHAR_BIT; +		out_offset += bytebits; + +		byte = value_in >> type->byteorder[i]; +		byte &= remmask = bytemask & (valmask >> type->byteorder[i]); + +		out[offset_byte] &= (unsigned char)~(remmask << offset_bit); +		out[offset_byte] |= (unsigned char)(byte << offset_bit); +		if (bytebits != limit && (remmask >>= CHAR_BIT - offset_bit)) { +			byte >>= CHAR_BIT - offset_bit; +			do { +				offset_byte += 1; +				out[offset_byte] &= (unsigned char)~remmask; +				out[offset_byte] |= (unsigned char)byte; +				byte >>= CHAR_BIT; +			} while (remmask >>= CHAR_BIT); +		} +	} + +	return LIBSYSCALLS_E_OK; +} diff --git a/libsyscalls_to_tracer_endian.c b/libsyscalls_to_tracer_endian.c new file mode 100644 index 0000000..ad3b8a4 --- /dev/null +++ b/libsyscalls_to_tracer_endian.c @@ -0,0 +1,59 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/* XXX be smarter if using the same byteorder */ +enum libsyscalls_error +libsyscalls_to_tracer_endian(const void *value_in, size_t in_offset, +                             const struct libsyscalls_datatype_description *type, +                             unsigned long long int *value_out) +{ +	const unsigned long long int limit = sizeof(long long int) / sizeof(char) * CHAR_BIT; +	unsigned long long int bytebits, bytemask, byte, value, remmask, valmask; +	unsigned long long int offset = in_offset, offset_byte, offset_bit, off; +	const unsigned char *in = value_in; +	size_t i; + +	if (!type || !value_in || !value_out || (unsigned long long int)type->width_in_bits > limit) +		return LIBSYSCALLS_E_INVAL; + +	bytebits = (unsigned long long int)type->width_in_bits; +	for (i = 0; !LIBSYSCALLS_IS_BYTEORDER_END_AT(type, i); i++) +		if (type->byteorder[i] && (unsigned long long int)type->byteorder[i] < bytebits) +			bytebits = (unsigned long long int)type->byteorder[i]; + +	if (bytebits > limit) +		return LIBSYSCALLS_E_INVAL; +	else if (bytebits == limit) +		bytemask = ~0ULL; +	else +		bytemask = (1ULL << bytebits) - 1ULL; + +	if ((unsigned long long int)type->width_in_bits == limit) +		valmask = ~0ULL; +	else +		valmask = (1ULL << (unsigned long long int)type->width_in_bits) - 1ULL; + +	value = 0; +	for (i = 0; !LIBSYSCALLS_IS_BYTEORDER_END_AT(type, i); i++) { +		offset_byte = offset / CHAR_BIT; +		offset_bit = offset % CHAR_BIT; +		offset += bytebits; + +		byte = (unsigned long long int)in[offset_byte] >> offset_bit; +		remmask = bytemask & (valmask >> type->byteorder[i]); + +		off = CHAR_BIT - offset_bit; +		if (remmask >>= off) { +			do { +				byte |= (unsigned long long int)in[++offset_byte] << off; +				off += CHAR_BIT; +			} while (remmask >>= CHAR_BIT); +		} +		byte &= bytemask & (valmask >> type->byteorder[i]); +		value |= byte << type->byteorder[i]; +	} + +	*value_out = value; +	return LIBSYSCALLS_E_OK; +} diff --git a/libsyscalls_unsection_value.c b/libsyscalls_unsection_value.c new file mode 100644 index 0000000..6cd797e --- /dev/null +++ b/libsyscalls_unsection_value.c @@ -0,0 +1,76 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +enum libsyscalls_error +libsyscalls_unsection_value(unsigned long long int value_in, size_t bits, +                            enum libsyscalls_datatype_section section, unsigned long long int *value_out) +{ +	unsigned long long int value = value_in, mask, shift; + +	if (bits > sizeof(value) / sizeof(char) * CHAR_BIT || +	    bits & (size_t)(LIBSYSCALLS_GET_SECTION_FRACTION(section) - 1U)) +		return LIBSYSCALLS_E_INVAL; + +	switch (section) { +	case LIBSYSCALLS_SECTION_UNDETERMINED: +	case LIBSYSCALLS_SECTION_WHOLE: /* 0xFFFFFFFFFFFFFFFF */ +		break; + +	case LIBSYSCALLS_SECTION_LOWER_HALF: /* 0x00000000FFFFFFFF */ +	case LIBSYSCALLS_SECTION_LOWER_QUARTER: /* 0x000000000000FFFF */ +		break; + +	case LIBSYSCALLS_SECTION_UPPER_HALF: /* 0xFFFFFFFF00000000 */ +	case LIBSYSCALLS_SECTION_LOWER_MID_QUARTER: /* 0x00000000FFFF0000 */ +		value <<= bits; +		break; + +	case LIBSYSCALLS_SECTION_INNER_HALF: /* 0x0000FFFFFFFF0000 */ +		value <<= bits / 2; +		break; + +	case LIBSYSCALLS_SECTION_UPPER_MID_QUARTER: /* 0x0000FFFF00000000 */ +		value <<= bits * 2; +		break; + +	case LIBSYSCALLS_SECTION_UPPER_QUARTER: /* 0xFFFF000000000000 */ +		value <<= bits * 3; +		break; + +	case LIBSYSCALLS_SECTION_OUTER_HALF: /* 0xFFFF00000000FFFF */ +		mask = (1ULL << (bits / 2)) - 1ULL; +		value = ((value & ~mask) << bits) | (value & mask); +		break; + +	case LIBSYSCALLS_SECTION_EVEN_QUARTERS_AS_HALF: /* 0x0000FFFF0000FFFF */ +		mask = (1ULL << (bits / 2)) - 1ULL; +		value = ((value & ~mask) << (bits / 2)) | (value & mask); +		break; + +	case LIBSYSCALLS_SECTION_ODD_QUARTERS_AS_HALF: /* 0xFFFF0000FFFF0000 */ +		mask = (1ULL << (bits / 2)) - 1ULL; +		value = ((value & ~mask) << (bits / 2)) | (value & mask); +		value <<= bits / 2; +		break; + +	case LIBSYSCALLS_SECTION_EVEN_BYTES_AS_HALF: /* 0x00FF00FF00FF00FF */ +		value = 0; +		for (shift = 0; value_in; value_in >>= 8, shift += 16) +			value |= (value_in & 0xFFU) << shift; +		break; + +	case LIBSYSCALLS_SECTION_ODD_BYTES_AS_HALF: /* 0xFF00FF00FF00FF00 */ +		value = 0; +		for (shift = 8; value_in; value_in >>= 8, shift += 16) +			value |= (value_in & 0xFFU) << shift; +		break; + +	default: +		return LIBSYSCALLS_E_INVAL; +	} + +	if (value_out) +		*value_out = value; +	return LIBSYSCALLS_E_OK; +} | 
