/* Copyright (c) 2019-2024 Griefer@Work                                       *
 *                                                                            *
 * This software is provided 'as-is', without any express or implied          *
 * warranty. In no event will the authors be held liable for any damages      *
 * arising from the use of this software.                                     *
 *                                                                            *
 * Permission is granted to anyone to use this software for any purpose,      *
 * including commercial applications, and to alter it and redistribute it     *
 * freely, subject to the following restrictions:                             *
 *                                                                            *
 * 1. The origin of this software must not be misrepresented; you must not    *
 *    claim that you wrote the original software. If you use this software    *
 *    in a product, an acknowledgement (see the following) in the product     *
 *    documentation is required:                                              *
 *    Portions Copyright (c) 2019-2024 Griefer@Work                           *
 * 2. Altered source versions must be plainly marked as such, and must not be *
 *    misrepresented as being the original software.                          *
 * 3. This notice may not be removed or altered from any source distribution. *
 */

BEGIN
	/* libdl should always be optimized for size, rather than speed! */
	REMOVE_GCC_FLAGS({ "-O2", "-O3" })

	GCC_FLAGS({
		"-Os", "-fno-rtti", "-ffreestanding",

		/* Don't make  assumptions  based  on  __attribute__((nonnull))
		 * We  use the attribute on API functions for stuff like module
		 * handles, but user-code of the library might still pass NULL,
		 * which we need to handle via `dlerror()'
		 *
		 * However, we _do_ still want to use the attribute so we're
		 * getting  compiler warnings for invalid internal usage, as
		 * well as when compiling programs with invalid usage. */
		"-fno-delete-null-pointer-checks",
	})

	if (TARGET_ARCH == "x86_64") {
		CC_FLAGS({
			"-mno-red-zone",  /* Don't require the hosted application to comply with the red zone. */
		})
	}

	/* Disable FPU access for libdl (it's not needed, but would add additional
	 * overhead  for  saving FPU  registers  to varargs  functions  on x86_64) */
	if (TARGET_ARCH in ["i386", "x86_64"]) {
		CC_FLAGS({
			"-mno-sse",
			"-mno-sse2",
			"-mno-sse3",
			"-mno-sse4",
			"-mno-ssse3",
			"-mno-mmx",
			"-mno-3dnow",
			"-mno-avx"
		})
		/* Indicate to hosted code that the FPU is disabled. */
		DEFINE({ "__NO_FPU" : "1" });
	}


	LD_FLAGS({ "--gc-sections" })
	SET_NOSTDLIB()
	SET_SHARED()

	/* Pull in the actual source files for libdl/RTLD */
	BEGIN GROUP("libs.libdl") { COMPILE };
		/* Arch-specific files */
		SET_LANGUAGE("assembler-with-cpp")
		SOURCE({ "arch/" + XARCH_ASMFILES })
	END

	BEGIN GROUP("libs.libdl") { COMPILE_WITH_ASM_FILTER((infp, outfp) -> {
		local indat = infp.read();
		local flushStart = 0, i = 0, len = #indat;
		while (i < len) {
			while (i < len && indat.isspace(i))
				++i;
			local eol = indat.find("\n", i);
			if (eol < 0)
				break;
			local line = indat[i:eol];
			/* Fix-up ".cfi_personality" to always reference locally. */
			if (line.startswith(".cfi_personality")) {
				outfp.write(indat[flushStart:i]);
				/* 0x1b == DW_EH_PE_pcrel|DW_EH_PE_sdata4 */
				outfp.write(".cfi_personality 0x1b,__gxx_personality_v0");
				flushStart = eol;
			}
			i = eol + 1;
		}
		outfp.write(indat[flushStart:]);
	}) };
		/* Define this macro so dlmalloc gets configured correctly. */
		DEFINE({ "__BUILTIN_LIBDL" : "1" })

		SOURCE({
			f"arch/{XARCH}/*.c",
			"./*.c",
			/* Use dlmalloc to get an malloc() function. */
			"../libdlmalloc/*.c",
		})
	END

	BEGIN GROUP("libs.libdl") { LINK, MCOPY, ELF_FLATTEN };
		options[OPT_ELF_FLATTEN_OUTPUT]                 = TARGET_BINPATH + DISK_LIBPATH + "libdl.rtld-flat.bin";
		options[OPT_ELF_FLATTEN_PHYS_BASE]              = 0;
		options[OPT_ELF_FLATTEN_VIRT_BASE]              = 0;
		options[OPT_ELF_FLATTEN_KEEP_BSS]               = true;
		options[OPT_ELF_FLATTEN_OVERRIDE_ENTRY]         = false;
		options[OPT_ELF_FLATTEN_ALLOWED_HEADERS]        = [0, 1]; /* text + data (don't include rel + dynamic) */
		options[OPT_ELF_FLATTEN_REQUIRE_NO_RELOCATIONS] = true;
		STATIC_USELIBGCC()
		LINKER_SCRIPT("_rtld.ld")
		SET_OUTPUT_LIBDLL("libdl")
		SOURCE({
			"./*.c",
			f"arch/{XARCH}/*.c",
			"arch/" + XARCH_ASMFILES,
			"../libdlmalloc/dlmalloc.c",
		})
	END


END
