// SPDX-FileCopyrightText: 2026 bubblepipe <bubblepipe42@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only

/**
 * Verification helper: glibc heap struct layout extractor.
 *
 * Struct definitions are extracted from the glibc source tree at
 * build time by extract_glibc_structs.py and placed in the generated
 * header glibc_structs.h.  This file walks those structs with
 * sizeof / offsetof and prints the results.
 *
 * Build:
 *   meson setup build -Dglibc_dir=/path/to/glibc-source
 *   meson compile -C build
 *
 * Run:
 *   ./build/extract_glibc_layouts
 */

#include <stdio.h>
#include <stddef.h>

#include "glibc_structs.h" /* generated by extract_glibc_structs.py */

int main(void) {
	const char *bits = (sizeof(void *) == 8) ? "64" : "32";

	printf("// =============================================================================\n");
	printf("// glibc heap layout extraction (structs from source)\n");
	printf("// =============================================================================\n");
	printf("//   glibc source version : %s\n", GLIBC_SRC_VERSION);
	printf("//   glibc source dir     : %s\n", GLIBC_SRC_DIR);
	printf("//   sizeof(void*)        : %zu\n", sizeof(void *));
	printf("//   sizeof(size_t)       : %zu\n", sizeof(size_t));
	printf("//\n\n");

	/* ------------------------------------------------------------ */
	/* MallocStateLayout                                             */
	/* ------------------------------------------------------------ */
	printf("// --- malloc_state %s ---\n", GLIBC_MALLOC_STATE_TAG);
	printf("static const MallocStateLayout rz_glibc_malloc_state_%d_%s = {\n",
		GLIBC_SRC_VERSION_ID, bits);
	printf("\t.mutex = %zu,\n",            (size_t)offsetof(struct malloc_state, mutex));
	printf("\t.flags = %zu,\n",            (size_t)offsetof(struct malloc_state, flags));
#if GLIBC_HAS_FAST_CHUNKS
	printf("\t.have_fast_chunks = %zu,\n", (size_t)offsetof(struct malloc_state, have_fastchunks));
#else
	printf("\t.have_fast_chunks = 0, // not present\n");
#endif
	printf("\t.fastbinsY = %zu,\n",        (size_t)offsetof(struct malloc_state, fastbinsY));
	printf("\t.top = %zu,\n",              (size_t)offsetof(struct malloc_state, top));
	printf("\t.last_remainder = %zu,\n",   (size_t)offsetof(struct malloc_state, last_remainder));
	printf("\t.bins = %zu,\n",             (size_t)offsetof(struct malloc_state, bins));
	printf("\t.binmap = %zu,\n",           (size_t)offsetof(struct malloc_state, binmap));
	printf("\t.next = %zu,\n",             (size_t)offsetof(struct malloc_state, next));
	printf("\t.next_free = %zu,\n",        (size_t)offsetof(struct malloc_state, next_free));
#if GLIBC_HAS_ATT_THREADS
	printf("\t.attached_threads = %zu,\n", (size_t)offsetof(struct malloc_state, attached_threads));
#else
	printf("\t.attached_threads = 0, // not present\n");
#endif
	printf("\t.system_mem = %zu,\n",       (size_t)offsetof(struct malloc_state, system_mem));
	printf("\t.max_system_mem = %zu,\n",   (size_t)offsetof(struct malloc_state, max_system_mem));
	printf("\t.struct_size = %zu,\n",      (size_t)sizeof(struct malloc_state));
	printf("\t.has_have_fast_chunks = %s,\n", GLIBC_HAS_FAST_CHUNKS ? "true" : "false");
	printf("\t.has_attached_threads = %s,\n", GLIBC_HAS_ATT_THREADS ? "true" : "false");
	printf("};\n\n");

	/* ------------------------------------------------------------ */
	/* RzHeapChunkLayout                                           */
	/* ------------------------------------------------------------ */
	printf("static const RzHeapChunkLayout rz_glibc_chunk_layout_%d_%s = {\n",
		GLIBC_SRC_VERSION_ID, bits);
	printf("\t.prev_size = %zu\n", (size_t)offsetof(struct malloc_chunk, mchunk_prev_size));
	printf("\t.size = %zu\n", (size_t)offsetof(struct malloc_chunk, mchunk_size));
	printf("\t.fd = %zu\n", (size_t)offsetof(struct malloc_chunk, fd));
	printf("\t.bk = %zu\n", (size_t)offsetof(struct malloc_chunk, bk));
	printf("\t.fd_nextsize = %zu\n", (size_t)offsetof(struct malloc_chunk, fd_nextsize));
	printf("\t.bk_nextsize = %zu\n", (size_t)offsetof(struct malloc_chunk, bk_nextsize));
	printf("\t.struct_size = %zu\n", (size_t)sizeof(struct malloc_chunk));
	printf("};\n\n");
	
	/* ------------------------------------------------------------ */
	/* RzHeapHeapInfoLayout                                        */
	/* ------------------------------------------------------------ */
	printf("static const RzHeapHeapInfoLayout rz_glibc_heap_info_layout_%d_%s = {\n",
		GLIBC_SRC_VERSION_ID, bits);
	printf("\t.ar_ptr = %zu\n", (size_t)offsetof(heap_info, ar_ptr));
	printf("\t.prev = %zu\n", (size_t)offsetof(heap_info, prev));
	printf("\t.size = %zu\n", (size_t)offsetof(heap_info, size));
	printf("\t.mprotect_size = %zu\n", (size_t)offsetof(heap_info, mprotect_size));
	printf("\t.struct_size   = %zu\n", (size_t)sizeof(heap_info));
	printf("};\n\n");

#if GLIBC_HAS_TCACHE
	/* ------------------------------------------------------------ */
	/* RzHeapTcacheEntryLayout                                        */
	/* ------------------------------------------------------------ */
	printf("static const RzHeapTcacheEntryLayout rz_glibc_tcache_entry_layout_%d_%s = {\n",
		GLIBC_SRC_VERSION_ID, bits);
	printf("\t.next        = %zu\n", (size_t)offsetof(tcache_entry, next));
#if GLIBC_HAS_TCACHE_KEY
	printf("\t.key         = %zu\n", (size_t)offsetof(tcache_entry, key));
	printf("\t.struct_size = %zu\n", (size_t)sizeof(tcache_entry));
	printf("\t.has_key     = true\n");
#else
	printf("\t.key         = 0  // not present\n");
	printf("\t.has_key     = false\n");
#endif
	printf("};\n\n");


	/* ------------------------------------------------------------ */
	/* RzHeapTcacheLayout                                        */
	/* ------------------------------------------------------------ */
	printf("static const RzHeapTcacheLayout rz_glibc_tcache_layout_%d_%s = {\n",
		GLIBC_SRC_VERSION_ID, bits);
	// printf("// RzHeapTcacheLayout %s { count_size = %zu, ptr_size = %zu }\n",
	// 	sizeof(((tcache_perthread_struct *)0)->TCACHE_COUNTS_FIELD[0]),
	// 	sizeof(void *));
	printf("\t.counts          = %zu\n",
		(size_t)offsetof(tcache_perthread_struct, TCACHE_COUNTS_FIELD));
	printf("\t.entries         = %zu\n",
		(size_t)offsetof(tcache_perthread_struct, entries));
	printf("\t.struct_size     = %zu\n",
		(size_t)sizeof(tcache_perthread_struct));
	printf("\t.count_type_size = %zu\n",
		sizeof(((tcache_perthread_struct *)0)->TCACHE_COUNTS_FIELD[0]));
	printf("\t.num_bins        = %zu\n", (size_t)TCACHE_MAX_BINS);
#else
	printf("// RzHeapTcacheEntryLayout: not present (glibc < 2.26)\n");
	printf("// RzHeapTcacheLayout: not present (glibc < 2.26)\n\n");
#endif
	printf("};\n\n");

	return 0;
}
