#include <stdint.h>
#include <stdbool.h>

void preserce_ptr_sz_fn(long x) {}

#define __bpf_aligned __attribute__((aligned(8)))

/*
 * KERNEL
 */

struct core_reloc_kernel_output {
	int valid[10];
	char comm[sizeof("test_progs")];
	int comm_len;
};

/*
 * FLAVORS
 */
struct core_reloc_flavors {
	int a;
	int b;
	int c;
};

/* this is not a flavor, as it doesn't have triple underscore */
struct core_reloc_flavors__err_wrong_name {
	int a;
	int b;
	int c;
};

/*
 * NESTING
 */
/* original set up, used to record relocations in BPF program */
struct core_reloc_nesting_substruct {
	int a;
};

union core_reloc_nesting_subunion {
	int b;
};

struct core_reloc_nesting {
	union {
		struct core_reloc_nesting_substruct a;
	} a;
	struct {
		union core_reloc_nesting_subunion b;
	} b;
};

/* inlined anonymous struct/union instead of named structs in original */
struct core_reloc_nesting___anon_embed {
	int __just_for_padding;
	union {
		struct {
			int a;
		} a;
	} a;
	struct {
		union {
			int b;
		} b;
	} b;
};

/* different mix of nested structs/unions than in original */
struct core_reloc_nesting___struct_union_mixup {
	int __a;
	struct {
		int __a;
		union {
			char __a;
			int a;
		} a;
	} a;
	int __b;
	union {
		int __b;
		union {
			char __b;
			int b;
		} b;
	} b;
};

/* extra anon structs/unions, but still valid a.a.a and b.b.b accessors */
struct core_reloc_nesting___extra_nesting {
	int __padding;
	struct {
		struct {
			struct {
				struct {
					union {
						int a;
					} a;
				};
			};
		} a;
		int __some_more;
		struct {
			union {
				union {
					union {
						struct {
							int b;
						};
					} b;
				};
			} b;
		};
	};
};

/* three flavors of same struct with different structure but same layout for
 * a.a.a and b.b.b, thus successfully resolved and relocatable */
struct core_reloc_nesting___dup_compat_types {
	char __just_for_padding;
	/* 3 more bytes of padding */
	struct {
		struct {
			int a; /* offset 4 */
		} a;
	} a;
	long long __more_padding;
	struct {
		struct {
			int b; /* offset 16 */
		} b;
	} b;
};

struct core_reloc_nesting___dup_compat_types__2 {
	int __aligned_padding;
	struct {
		int __trickier_noop[0];
		struct {
			char __some_more_noops[0];
			int a; /* offset 4 */
		} a;
	} a;
	int __more_padding;
	struct {
		struct {
			struct {
				int __critical_padding;
				int b; /* offset 16 */
			} b;
			int __does_not_matter;
		};
	} b;
	int __more_irrelevant_stuff;
};

struct core_reloc_nesting___dup_compat_types__3 {
	char __correct_padding[4];
	struct {
		struct {
			int a; /* offset 4 */
		} a;
	} a;
	/* 8 byte padding due to next struct's alignment */
	struct {
		struct {
			int b;
		} b;
	} b __attribute__((aligned(16)));
};

/* b.b.b field is missing */
struct core_reloc_nesting___err_missing_field {
	struct {
		struct {
			int a;
		} a;
	} a;
	struct {
		struct {
			int x;
		} b;
	} b;
};

/* b.b.b field is an array of integers instead of plain int */
struct core_reloc_nesting___err_array_field {
	struct {
		struct {
			int a;
		} a;
	} a;
	struct {
		struct {
			int b[1];
		} b;
	} b;
};

/* middle b container is missing */
struct core_reloc_nesting___err_missing_container {
	struct {
		struct {
			int a;
		} a;
	} a;
	struct {
		int x;
	} b;
};

/* middle b container is referenced through pointer instead of being embedded */
struct core_reloc_nesting___err_nonstruct_container {
	struct {
		struct {
			int a;
		} a;
	} a;
	struct {
		struct {
			int b;
		} *b;
	} b;
};

/* middle b container is an array of structs instead of plain struct */
struct core_reloc_nesting___err_array_container {
	struct {
		struct {
			int a;
		} a;
	} a;
	struct {
		struct {
			int b;
		} b[1];
	} b;
};

/* two flavors of same struct with incompatible layout for b.b.b */
struct core_reloc_nesting___err_dup_incompat_types__1 {
	struct {
		struct {
			int a; /* offset 0 */
		} a;
	} a;
	struct {
		struct {
			int b; /* offset 4 */
		} b;
	} b;
};

struct core_reloc_nesting___err_dup_incompat_types__2 {
	struct {
		struct {
			int a; /* offset 0 */
		} a;
	} a;
	int __extra_padding;
	struct {
		struct {
			int b; /* offset 8 (!) */
		} b;
	} b;
};

/* two flavors of same struct having one of a.a.a and b.b.b, but not both */
struct core_reloc_nesting___err_partial_match_dups__a {
	struct {
		struct {
			int a;
		} a;
	} a;
};

struct core_reloc_nesting___err_partial_match_dups__b {
	struct {
		struct {
			int b;
		} b;
	} b;
};

struct core_reloc_nesting___err_too_deep {
	struct {
		struct {
			int a;
		} a;
	} a;
	/* 65 levels of nestedness for b.b.b */
	struct {
		struct {
			struct { struct { struct { struct { struct {
			struct { struct { struct { struct { struct {
			struct { struct { struct { struct { struct {
			struct { struct { struct { struct { struct {
			struct { struct { struct { struct { struct {
			struct { struct { struct { struct { struct {
			struct { struct { struct { struct { struct {
			struct { struct { struct { struct { struct {
			struct { struct { struct { struct { struct {
			struct { struct { struct { struct { struct {
			struct { struct { struct { struct { struct {
			struct { struct { struct { struct { struct {
				/* this one is one too much */
				struct {
					int b;
				};
			}; }; }; }; };
			}; }; }; }; };
			}; }; }; }; };
			}; }; }; }; };
			}; }; }; }; };
			}; }; }; }; };
			}; }; }; }; };
			}; }; }; }; };
			}; }; }; }; };
			}; }; }; }; };
			}; }; }; }; };
			}; }; }; }; };
		} b;
	} b;
};

/*
 * ARRAYS
 */
struct core_reloc_arrays_output {
	int a2;
	char b123;
	int c1c;
	int d00d;
	int f10c;
};

struct core_reloc_arrays_substruct {
	int c;
	int d;
};

struct core_reloc_arrays {
	int a[5];
	char b[2][3][4];
	struct core_reloc_arrays_substruct c[3];
	struct core_reloc_arrays_substruct d[1][2];
	struct core_reloc_arrays_substruct f[][2];
};

/* bigger array dimensions */
struct core_reloc_arrays___diff_arr_dim {
	int a[7];
	char b[3][4][5];
	struct core_reloc_arrays_substruct c[4];
	struct core_reloc_arrays_substruct d[2][3];
	struct core_reloc_arrays_substruct f[1][3];
};

/* different size of array's value (struct) */
struct core_reloc_arrays___diff_arr_val_sz {
	int a[5];
	char b[2][3][4];
	struct {
		int __padding1;
		int c;
		int __padding2;
	} c[3];
	struct {
		int __padding1;
		int d;
		int __padding2;
	} d[1][2];
	struct {
		int __padding1;
		int c;
		int __padding2;
	} f[][2];
};

struct core_reloc_arrays___equiv_zero_sz_arr {
	int a[5];
	char b[2][3][4];
	struct core_reloc_arrays_substruct c[3];
	struct core_reloc_arrays_substruct d[1][2];
	/* equivalent to flexible array */
	struct core_reloc_arrays_substruct f[][2];
};

struct core_reloc_arrays___fixed_arr {
	int a[5];
	char b[2][3][4];
	struct core_reloc_arrays_substruct c[3];
	struct core_reloc_arrays_substruct d[1][2];
	/* not a flexible array anymore, but within access bounds */
	struct core_reloc_arrays_substruct f[1][2];
};

struct core_reloc_arrays___err_too_small {
	int a[2]; /* this one is too small */
	char b[2][3][4];
	struct core_reloc_arrays_substruct c[3];
	struct core_reloc_arrays_substruct d[1][2];
	struct core_reloc_arrays_substruct f[][2];
};

struct core_reloc_arrays___err_too_shallow {
	int a[5];
	char b[2][3]; /* this one lacks one dimension */
	struct core_reloc_arrays_substruct c[3];
	struct core_reloc_arrays_substruct d[1][2];
	struct core_reloc_arrays_substruct f[][2];
};

struct core_reloc_arrays___err_non_array {
	int a; /* not an array */
	char b[2][3][4];
	struct core_reloc_arrays_substruct c[3];
	struct core_reloc_arrays_substruct d[1][2];
	struct core_reloc_arrays_substruct f[][2];
};

struct core_reloc_arrays___err_wrong_val_type {
	int a[5];
	char b[2][3][4];
	int c[3]; /* value is not a struct */
	struct core_reloc_arrays_substruct d[1][2];
	struct core_reloc_arrays_substruct f[][2];
};

struct core_reloc_arrays___err_bad_zero_sz_arr {
	/* zero-sized array, but not at the end */
	struct core_reloc_arrays_substruct f[0][2];
	int a[5];
	char b[2][3][4];
	struct core_reloc_arrays_substruct c[3];
	struct core_reloc_arrays_substruct d[1][2];
};

/*
 * PRIMITIVES
 */
enum core_reloc_primitives_enum {
	A = 0,
	B = 1,
};

struct core_reloc_primitives {
	char a;
	int b;
	enum core_reloc_primitives_enum c;
	void *d __bpf_aligned;
	int (*f)(const char *) __bpf_aligned;
};

struct core_reloc_primitives___diff_enum_def {
	char a;
	int b;
	void *d __bpf_aligned;
	int (*f)(const char *) __bpf_aligned;
	enum {
		X = 100,
		Y = 200,
	} c __bpf_aligned; /* inline enum def with differing set of values */
};

struct core_reloc_primitives___diff_func_proto {
	void (*f)(int) __bpf_aligned; /* incompatible function prototype */
	void *d __bpf_aligned;
	enum core_reloc_primitives_enum c __bpf_aligned;
	int b;
	char a;
};

struct core_reloc_primitives___diff_ptr_type {
	const char * const d __bpf_aligned; /* different pointee type + modifiers */
	char a __bpf_aligned;
	int b;
	enum core_reloc_primitives_enum c;
	int (*f)(const char *) __bpf_aligned;
};

struct core_reloc_primitives___err_non_enum {
	char a[1];
	int b;
	int c; /* int instead of enum */
	void *d __bpf_aligned;
	int (*f)(const char *) __bpf_aligned;
};

struct core_reloc_primitives___err_non_int {
	char a[1];
	int *b __bpf_aligned; /* ptr instead of int */
	enum core_reloc_primitives_enum c __bpf_aligned;
	void *d __bpf_aligned;
	int (*f)(const char *) __bpf_aligned;
};

struct core_reloc_primitives___err_non_ptr {
	char a[1];
	int b;
	enum core_reloc_primitives_enum c;
	int d; /* int instead of ptr */
	int (*f)(const char *) __bpf_aligned;
};

/*
 * MODS
 */
struct core_reloc_mods_output {
	int a, b, c, d, e, f, g, h;
};

typedef const int int_t;
typedef const char *char_ptr_t __bpf_aligned;
typedef const int arr_t[7];

struct core_reloc_mods_substruct {
	int x;
	int y;
};

typedef struct {
	int x;
	int y;
} core_reloc_mods_substruct_t;

struct core_reloc_mods {
	int a;
	int_t b;
	char *c __bpf_aligned;
	char_ptr_t d;
	int e[3] __bpf_aligned;
	arr_t f;
	struct core_reloc_mods_substruct g;
	core_reloc_mods_substruct_t h;
};

/* a/b, c/d, e/f, and g/h pairs are swapped */
struct core_reloc_mods___mod_swap {
	int b;
	int_t a;
	char *d __bpf_aligned;
	char_ptr_t c;
	int f[3] __bpf_aligned;
	arr_t e;
	struct {
		int y;
		int x;
	} h;
	core_reloc_mods_substruct_t g;
};

typedef int int1_t;
typedef int1_t int2_t;
typedef int2_t int3_t;

typedef int arr1_t[5];
typedef arr1_t arr2_t;
typedef arr2_t arr3_t;
typedef arr3_t arr4_t;

typedef const char * const volatile fancy_char_ptr_t __bpf_aligned;

typedef core_reloc_mods_substruct_t core_reloc_mods_substruct_tt;

/* we need more typedefs */
struct core_reloc_mods___typedefs {
	core_reloc_mods_substruct_tt g;
	core_reloc_mods_substruct_tt h;
	arr4_t f;
	arr4_t e;
	fancy_char_ptr_t d;
	fancy_char_ptr_t c;
	int3_t b __bpf_aligned;
	int3_t a;
};

/*
 * PTR_AS_ARR
 */
struct core_reloc_ptr_as_arr {
	int a;
};

struct core_reloc_ptr_as_arr___diff_sz {
	int :32; /* padding */
	char __some_more_padding;
	int a;
};

/*
 * INTS
 */
struct core_reloc_ints {
	uint8_t		u8_field;
	int8_t		s8_field;
	uint16_t	u16_field;
	int16_t		s16_field;
	uint32_t	u32_field;
	int32_t		s32_field;
	uint64_t	u64_field;
	int64_t		s64_field;
};

/* signed/unsigned types swap */
struct core_reloc_ints___reverse_sign {
	int8_t		u8_field;
	uint8_t		s8_field;
	int16_t		u16_field;
	uint16_t	s16_field;
	int32_t		u32_field;
	uint32_t	s32_field;
	int64_t		u64_field;
	uint64_t	s64_field;
};

struct core_reloc_ints___bool {
	bool		u8_field; /* bool instead of uint8 */
	int8_t		s8_field;
	uint16_t	u16_field;
	int16_t		s16_field;
	uint32_t	u32_field;
	int32_t		s32_field;
	uint64_t	u64_field;
	int64_t		s64_field;
};

/*
 * MISC
 */
struct core_reloc_misc_output {
	int a, b, c;
};

struct core_reloc_misc___a {
	int a1;
	int a2;
};

struct core_reloc_misc___b {
	int b1;
	int b2;
};

/* this one extends core_reloc_misc_extensible struct from BPF prog */
struct core_reloc_misc_extensible {
	int a;
	int b;
	int c;
	int d;
};

/*
 * EXISTENCE
 */
struct core_reloc_existence_output {
	int a_exists;
	int a_value;
	int b_exists;
	int b_value;
	int c_exists;
	int c_value;
	int arr_exists;
	int arr_value;
	int s_exists;
	int s_value;
};

struct core_reloc_existence {
	int a;
	struct {
		int b;
	};
	int c;
	int arr[1];
	struct {
		int x;
	} s;
};

struct core_reloc_existence___minimal {
	int a;
};

struct core_reloc_existence___err_wrong_int_sz {
	short a;
};

struct core_reloc_existence___err_wrong_int_type {
	int b[1];
};

struct core_reloc_existence___err_wrong_int_kind {
	struct{ int x; } c;
};

struct core_reloc_existence___err_wrong_arr_kind {
	int arr;
};

struct core_reloc_existence___err_wrong_arr_value_type {
	short arr[1];
};

struct core_reloc_existence___err_wrong_struct_type {
	int s;
};

/*
 * BITFIELDS
 */
/* bitfield read results, all as plain integers */
struct core_reloc_bitfields_output {
	int64_t		ub1;
	int64_t		ub2;
	int64_t		ub7;
	int64_t		sb4;
	int64_t		sb20;
	int64_t		u32;
	int64_t		s32;
};

struct core_reloc_bitfields {
	/* unsigned bitfields */
	uint8_t		ub1: 1;
	uint8_t		ub2: 2;
	uint32_t	ub7: 7;
	/* signed bitfields */
	int8_t		sb4: 4;
	int32_t		sb20: 20;
	/* non-bitfields */
	uint32_t	u32;
	int32_t		s32;
};

/* different bit sizes (both up and down) */
struct core_reloc_bitfields___bit_sz_change {
	/* unsigned bitfields */
	uint16_t	ub1: 3;		/*  1 ->  3 */
	uint32_t	ub2: 20;	/*  2 -> 20 */
	uint8_t		ub7: 1;		/*  7 ->  1 */
	/* signed bitfields */
	int8_t		sb4: 1;		/*  4 ->  1 */
	int32_t		sb20: 30;	/* 20 -> 30 */
	/* non-bitfields */
	uint16_t	u32;			/* 32 -> 16 */
	int64_t		s32 __bpf_aligned;	/* 32 -> 64 */
};

/* turn bitfield into non-bitfield and vice versa */
struct core_reloc_bitfields___bitfield_vs_int {
	uint64_t	ub1;		/*  3 -> 64 non-bitfield */
	uint8_t		ub2;		/* 20 ->  8 non-bitfield */
	int64_t		ub7 __bpf_aligned;	/*  7 -> 64 non-bitfield signed */
	int64_t		sb4 __bpf_aligned;	/*  4 -> 64 non-bitfield signed */
	uint64_t	sb20 __bpf_aligned;	/* 20 -> 16 non-bitfield unsigned */
	int32_t		u32: 20;		/* 32 non-bitfield -> 20 bitfield */
	uint64_t	s32: 60 __bpf_aligned;	/* 32 non-bitfield -> 60 bitfield */
};

struct core_reloc_bitfields___just_big_enough {
	uint64_t	ub1: 4;
	uint64_t	ub2: 60; /* packed tightly */
	uint32_t	ub7;
	uint32_t	sb4;
	uint32_t	sb20;
	uint32_t	u32;
	uint32_t	s32;
} __attribute__((packed)) ;

struct core_reloc_bitfields___err_too_big_bitfield {
	uint64_t	ub1: 4;
	uint64_t	ub2: 61; /* packed tightly */
	uint32_t	ub7;
	uint32_t	sb4;
	uint32_t	sb20;
	uint32_t	u32;
	uint32_t	s32;
} __attribute__((packed)) ;

/*
 * SIZE
 */
struct core_reloc_size_output {
	int int_sz;
	int struct_sz;
	int union_sz;
	int arr_sz;
	int arr_elem_sz;
	int ptr_sz;
	int enum_sz;
};

struct core_reloc_size {
	int int_field;
	struct { int x; } struct_field;
	union { int x; } union_field;
	int arr_field[4];
	void *ptr_field;
	enum { VALUE = 123 } enum_field;
};

struct core_reloc_size___diff_sz {
	uint64_t int_field;
	struct { int x; int y; int z; } struct_field;
	union { int x; char bla[123]; } union_field;
	char arr_field[10];
	void *ptr_field;
	enum { OTHER_VALUE = 0xFFFFFFFFFFFFFFFF } enum_field;
};
