Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@ tests/esp32p4/embed/
tests/esp32p4/fw.elf
tests/esp32p4/*.log
contrib/esp32p4/smoke_test.elf
contrib/esp32p4/idf-benchmark/main/embed/
contrib/esp32p4/idf-benchmark/main/embed_list.inc
contrib/esp32p4/idf-benchmark/main/embed_includes.inc
contrib/esp32p4/idf-benchmark/build/
contrib/esp32p4/idf-benchmark/sdkconfig
contrib/esp32p4/idf-benchmark/sdkconfig.old
26 changes: 26 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ if (WITH_SYSTEM_ZLIB)
else()
if(NOT TARGET miniz)
add_subdirectory(deps/miniz-3.1.2 EXCLUDE_FROM_ALL)
# ESP ROMs export an older miniz that would otherwise capture this one
# at link time; applied here rather than inside deps/ so a miniz bump
# can't drop it. No-op off ESP-IDF.
include(${CMAKE_CURRENT_LIST_DIR}/cmake/EspRomMinizWorkaround.cmake)
libchdr_apply_esp_rom_miniz_workaround(miniz)
set(CHDR_NEEDS_MINIZ_RENAME TRUE)
endif()
list(APPEND CHDR_LIBS miniz)
endif()
Expand Down Expand Up @@ -88,6 +94,17 @@ else()
endif()

if(CHDR_VERIFY_BLOCK_CRC)
# The per-hunk CRC chdman stores covers the fully reconstituted hunk, ECC and
# sync header included, so a build that skips ecc_generate() cannot match it.
# The failure is content-dependent - hunks holding only audio frames have no
# ECC to regenerate and still verify - which makes it look like sporadic file
# corruption rather than a build misconfiguration. Refuse the combination.
if(NOT CHDR_WANT_RAW_DATA_SECTOR)
message(FATAL_ERROR
"CHDR_VERIFY_BLOCK_CRC=ON requires CHDR_WANT_RAW_DATA_SECTOR=ON: without the "
"regenerated ECC and sync header the decoded hunk cannot match the CRC stored "
"in the file. Turn CHDR_VERIFY_BLOCK_CRC off as well to build without raw sectors.")
endif()
list(APPEND CHDR_DEFINES VERIFY_BLOCK_CRC=1)
else()
list(APPEND CHDR_DEFINES VERIFY_BLOCK_CRC=0)
Expand Down Expand Up @@ -124,6 +141,12 @@ set(CHDR_SOURCES
add_library(chdr-static STATIC ${CHDR_SOURCES})
target_include_directories(chdr-static INTERFACE include)
target_link_libraries(chdr-static PRIVATE ${CHDR_LIBS} ${PLATFORM_LIBS})
# libchdr_codec_zlib.c calls tinfl_decompress() directly, so it must be renamed
# alongside miniz's definition - otherwise on ESP-IDF this call binds to the
# ROM's older copy and we are back to the split decoder this branch fixed.
if(CHDR_NEEDS_MINIZ_RENAME)
libchdr_apply_esp_rom_miniz_workaround(chdr-static)
endif()
target_compile_definitions(chdr-static PRIVATE ${CHDR_DEFINES})

if(MSVC)
Expand All @@ -140,6 +163,9 @@ if (BUILD_SHARED_LIBS)
add_library(chdr SHARED ${CHDR_SOURCES})
target_include_directories(chdr INTERFACE include)
target_link_libraries(chdr PRIVATE ${CHDR_LIBS} ${PLATFORM_LIBS})
if(CHDR_NEEDS_MINIZ_RENAME)
libchdr_apply_esp_rom_miniz_workaround(chdr)
endif()
target_compile_definitions(chdr PRIVATE ${CHDR_DEFINES})

if(MSVC)
Expand Down
70 changes: 70 additions & 0 deletions cmake/EspRomMinizWorkaround.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Work around Espressif ROMs exporting their own, older miniz.
#
# ESP32 ROMs (S3/C3/C6/P4/...) bake in an older miniz and export its tinfl
# entry points from the target's ROM linker script as *absolute* symbols -
# see the "Group miniz" block in
# $IDF_PATH/components/esp_rom/<target>/ld/<target>.rom.ld, e.g.
#
# tinfl_decompress = 0x4fc000f8;
#
# A linker-script assignment outranks an ordinary object definition, so an
# ESP-IDF link silently binds those names to ROM and drops the copies
# compiled from deps/miniz-3.1.2/miniz.c - even though both are present in
# the archive. The result is a *split decoder*: mz_inflateInit2()/mz_inflate()
# from miniz 3.1.2 build and interpret a 3.1.2-layout tinfl_decompressor, then
# hand it to a ROM tinfl_decompress() that lays that struct out differently
# (miniz 3.0 reworked the Huffman tables from tinfl_huff_table m_tables[3] to
# the flattened m_look_up/m_tree_N form, changing field offsets and total
# size). The ROM decoder writes past the end of the smaller m_decomp and
# corrupts the enclosing inflate_state.
#
# Observed on an ESP32-P4 (rev v3.1) against a 128-file CHD corpus: the first
# inflate of a stream mostly survives, then every later one fails, because the
# overrun lands on inflate_state::m_window_bits (which sits just before
# m_dict[32768]). mz_inflate() then sees m_window_bits > 0, sets
# TINFL_FLAG_PARSE_ZLIB_HEADER on a raw-deflate stream opened with
# inflateInit2(..., -MAX_WBITS), consumes exactly 2 bytes on the CMF/FLG check
# and returns MZ_DATA_ERROR. It presents as CHDERR_DECOMPRESSION_ERROR and
# looks exactly like corrupt input or a silicon/codegen bug.
#
# Renaming the colliding symbols keeps miniz.c's own definitions reachable.
# Only miniz.c references these names, so applying the defines to whatever
# target compiles miniz.c is sufficient. mz_free matters independently of the
# decoder mismatch: bound to ROM it would hand ESP-IDF-heap pointers to the
# ROM allocator. mz_adler32 is benign but renamed for consistency.
#
# Deliberately NOT patched into deps/miniz-3.1.2/miniz.h - that tree is
# vendored verbatim so it can be re-synced from upstream, and a local edit
# there would be silently dropped by the next version bump. Keep this file as
# the single definition; both build paths below include it.
#
# Regression check (cheap, no flashing) - this must print nothing:
#
# grep -hoE '^[A-Za-z_][A-Za-z0-9_]* = 0x' \
# "$IDF_PATH"/components/esp_rom/<target>/ld/<target>.rom*.ld \
# | sed 's/ = 0x//' | sort -u > /tmp/rom_syms.txt
# <target>-nm <libchdr archive> \
# | awk '$2 ~ /^[TDBR]$/ {print $3}' | sort -u > /tmp/chdr_syms.txt
# comm -12 /tmp/rom_syms.txt /tmp/chdr_syms.txt
#
# Re-run it after any miniz bump, and after adding any dep the ROM also
# ships - the rom.ld files list them by group.

set(LIBCHDR_ESP_ROM_MINIZ_COLLISIONS
tinfl_decompress
tinfl_decompress_mem_to_heap
tinfl_decompress_mem_to_mem
tinfl_decompress_mem_to_callback
mz_adler32
mz_free
)

# Apply the renames to a target that compiles miniz.c. No-op off ESP-IDF.
function(libchdr_apply_esp_rom_miniz_workaround target)
if(NOT ESP_PLATFORM)
return()
endif()
foreach(sym IN LISTS LIBCHDR_ESP_ROM_MINIZ_COLLISIONS)
target_compile_definitions(${target} PRIVATE "${sym}=libchdr_${sym}")
endforeach()
endfunction()
3 changes: 3 additions & 0 deletions contrib/esp32p4/idf-benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(libchdr_esp32p4_benchmark)
Loading