From 38251801df5ce09759f455c617d9787209680b08 Mon Sep 17 00:00:00 2001 From: Alula Date: Wed, 24 Jun 2026 20:50:41 +0200 Subject: [PATCH] loader/strat: fix/refactor zstd-zbic integration --- .../stratosphere/util/util_compression.hpp | 8 ++-- .../source/util/util_compression_lz4.cpp | 41 ++++++++++++++++++ ...pression.cpp => util_compression_zbic.cpp} | 43 ++++++++----------- .../source/util/{zstd.c => zstd.inc} | 0 .../loader/source/ldr_process_creation.cpp | 5 +-- 5 files changed, 66 insertions(+), 31 deletions(-) create mode 100644 libraries/libstratosphere/source/util/util_compression_lz4.cpp rename libraries/libstratosphere/source/util/{util_compression.cpp => util_compression_zbic.cpp} (68%) rename libraries/libstratosphere/source/util/{zstd.c => zstd.inc} (100%) diff --git a/libraries/libstratosphere/include/stratosphere/util/util_compression.hpp b/libraries/libstratosphere/include/stratosphere/util/util_compression.hpp index a9f03641b..b22f3441e 100644 --- a/libraries/libstratosphere/include/stratosphere/util/util_compression.hpp +++ b/libraries/libstratosphere/include/stratosphere/util/util_compression.hpp @@ -21,11 +21,13 @@ namespace ams::util { /* Compression utilities. */ int CompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size); - size_t CompressZstd(void *dst, size_t dst_size, const void *src, size_t src_size); + size_t CompressZbic(void *dst, size_t dst_size, const void *src, size_t src_size); + + constexpr size_t ZstdDctxWorkspaceSize = 0x176E8; /* Decompression utilities. */ int DecompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size); - size_t DecompressZstd(void *dst, size_t dst_size, const void *src, size_t src_size); - bool DecompressZstdForLoader(void* workspace, size_t workspace_size, void *dst, size_t dst_size, size_t expected_dec_size, const void *src, size_t src_size); + size_t DecompressZbic(void *dst, size_t dst_size, const void *src, size_t src_size); + bool DecompressZbicForLoader(void* workspace, size_t workspace_size, void *dst, size_t dst_size, size_t expected_dec_size, const void *src, size_t src_size); } \ No newline at end of file diff --git a/libraries/libstratosphere/source/util/util_compression_lz4.cpp b/libraries/libstratosphere/source/util/util_compression_lz4.cpp new file mode 100644 index 000000000..2f5704f47 --- /dev/null +++ b/libraries/libstratosphere/source/util/util_compression_lz4.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "lz4.h" + +namespace ams::util { + + /* Compression utilities. */ + int CompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size) { + /* Size checks. */ + AMS_ABORT_UNLESS(dst_size <= std::numeric_limits::max()); + AMS_ABORT_UNLESS(src_size <= std::numeric_limits::max()); + + /* This is just a thin wrapper around LZ4. */ + return LZ4_compress_default(reinterpret_cast(src), reinterpret_cast(dst), static_cast(src_size), static_cast(dst_size)); + } + + /* Decompression utilities. */ + int DecompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size) { + /* Size checks. */ + AMS_ABORT_UNLESS(dst_size <= std::numeric_limits::max()); + AMS_ABORT_UNLESS(src_size <= std::numeric_limits::max()); + + /* This is just a thin wrapper around LZ4. */ + return LZ4_decompress_safe(reinterpret_cast(src), reinterpret_cast(dst), static_cast(src_size), static_cast(dst_size)); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/util/util_compression.cpp b/libraries/libstratosphere/source/util/util_compression_zbic.cpp similarity index 68% rename from libraries/libstratosphere/source/util/util_compression.cpp rename to libraries/libstratosphere/source/util/util_compression_zbic.cpp index f17021024..69743bf2d 100644 --- a/libraries/libstratosphere/source/util/util_compression.cpp +++ b/libraries/libstratosphere/source/util/util_compression_zbic.cpp @@ -17,21 +17,19 @@ #include "lz4.h" #define ZSTD_STATIC_LINKING_ONLY #define ZSTD_ZBIC_SUPPORT 1 + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#define ZSTDLIB_VISIBLE static +#define ZSTDLIB_HIDDEN static #include "zstd.h" +#include "zstd.inc" +#pragma GCC diagnostic pop namespace ams::util { + static_assert(sizeof(ZSTD_DCtx) <= ZstdDctxWorkspaceSize); - /* Compression utilities. */ - int CompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size) { - /* Size checks. */ - AMS_ABORT_UNLESS(dst_size <= std::numeric_limits::max()); - AMS_ABORT_UNLESS(src_size <= std::numeric_limits::max()); - - /* This is just a thin wrapper around LZ4. */ - return LZ4_compress_default(reinterpret_cast(src), reinterpret_cast(dst), static_cast(src_size), static_cast(dst_size)); - } - - size_t CompressZstd(void *dst, size_t dst_size, const void *src, size_t src_size) { + size_t CompressZbic(void *dst, size_t dst_size, const void *src, size_t src_size) { /* Basic size checks. */ AMS_ABORT_UNLESS(dst_size <= std::numeric_limits::max()); AMS_ABORT_UNLESS(src_size <= std::numeric_limits::max()); @@ -47,18 +45,8 @@ namespace ams::util { /* This is just a wrapper around Zstd. */ return ZSTD_compress(dst, dst_size, src, src_size, compressionLevel); } - - /* Decompression utilities. */ - int DecompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size) { - /* Size checks. */ - AMS_ABORT_UNLESS(dst_size <= std::numeric_limits::max()); - AMS_ABORT_UNLESS(src_size <= std::numeric_limits::max()); - - /* This is just a thin wrapper around LZ4. */ - return LZ4_decompress_safe(reinterpret_cast(src), reinterpret_cast(dst), static_cast(src_size), static_cast(dst_size)); - } - size_t DecompressZstd(void *dst, size_t dst_size, const void *src, size_t src_size) { + size_t DecompressZbic(void *dst, size_t dst_size, const void *src, size_t src_size) { /* Basic size checks. */ AMS_ABORT_UNLESS(dst_size <= std::numeric_limits::max()); AMS_ABORT_UNLESS(src_size <= std::numeric_limits::max()); @@ -72,10 +60,12 @@ namespace ams::util { return ZSTD_decompress(dst, dst_size, src, src_size); } - bool DecompressZstdForLoader(void* workspace, size_t workspace_size, void *dst, size_t dst_size, size_t expected_dec_size, const void *src, size_t src_size) { + bool DecompressZbicForLoader(void* workspace, size_t workspace_size, void *dst, size_t dst_size, size_t expected_dec_size, const void *src, size_t src_size) { /* Check decompression margin. */ auto margin = ZSTD_decompressionMargin(src, src_size); if (ZSTD_isError(margin)) { + auto ec = ZSTD_getErrorCode(margin); + AMS_LOG("[ldr] can't determine decompression margin: %u (%s)\n", ec, ZSTD_getErrorString(ec)); return false; } @@ -86,17 +76,20 @@ namespace ams::util { /* Make sure we fit in the destination buffer. */ if (margin + expected_dec_size > dst_size) { + AMS_LOG("[ldr] not enough space for decompression %lu + %lu > %lu\n", margin, expected_dec_size, dst_size); return false; } - /* This is a runtime assert in Loader code. We replicate it here. */ - AMS_ABORT_UNLESS(ZSTD_estimateDCtxSize() == workspace_size); + /* This is a runtime assert in Loader code. Note that Nintendo does == comparison here. */ + AMS_ABORT_UNLESS(ZSTD_estimateDCtxSize() <= workspace_size); /* Decompress using a static decompression context. */ auto dctx = ZSTD_initStaticDCtx(workspace, workspace_size); size_t dec_size = ZSTD_decompressDCtx(dctx, dst, dst_size, src, src_size); if (ZSTD_isError(dec_size)) { + auto ec = ZSTD_getErrorCode(dec_size); + AMS_LOG("[ldr] decompression failed: %u (%s)\n", ec, ZSTD_getErrorString(ec)); return false; } diff --git a/libraries/libstratosphere/source/util/zstd.c b/libraries/libstratosphere/source/util/zstd.inc similarity index 100% rename from libraries/libstratosphere/source/util/zstd.c rename to libraries/libstratosphere/source/util/zstd.inc diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index 276484824..51e69ebdd 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -121,8 +121,7 @@ namespace ams::ldr { NsoHeader g_nso_headers[Nso_Count]; /* Global Zstd decompression context. */ - constexpr size_t ZstdDctxWorkspaceSize = 0x176E8; - alignas(8) u8 g_zstd_dctx_workspace[ZstdDctxWorkspaceSize]; + alignas(8) u8 g_zstd_dctx_workspace[util::ZstdDctxWorkspaceSize]; Result ValidateProgramVersion(ncm::ProgramId program_id, u32 version) { /* No version verification is done before 8.1.0. */ @@ -656,7 +655,7 @@ namespace ams::ldr { auto compressed_data_buf = reinterpret_cast(load_address); if (is_zstd) { - bool decompressed = util::DecompressZstdForLoader(reinterpret_cast(g_zstd_dctx_workspace), ZstdDctxWorkspaceSize, reinterpret_cast(map_base), static_cast(map_end - map_base), segment_size, compressed_data_buf, file_size); + bool decompressed = util::DecompressZbicForLoader(reinterpret_cast(g_zstd_dctx_workspace), sizeof(g_zstd_dctx_workspace), reinterpret_cast(map_base), static_cast(map_end - map_base), segment_size, compressed_data_buf, file_size); R_UNLESS(decompressed, ldr::ResultInvalidNso()); } else { bool decompressed = (util::DecompressLZ4(reinterpret_cast(map_base), segment_size, compressed_data_buf, file_size) == static_cast(segment_size));