From 5ddf0eb879467d7b6cd0aade9046db6e7961e1b0 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sun, 28 Jul 2024 01:44:39 +0300 Subject: [PATCH] elf.c: section limit inference for linux, fixes for stb_ds.h hashing, more compilation flags --- CMakeLists.txt | 25 ++++++++++++--- src/audio.c | 2 +- src/game/scenes/ingame.c | 1 + src/game/scenes/title.c | 1 + src/private/rendering.h | 1 - src/system/linux/elf.c | 68 ++++++++++++++++++++++++++++++++++++++++ src/system/linux/elf.h | 10 ++++++ src/textures.c | 56 +++++++++++++++++++-------------- third-party/stb/stb_ds.h | 6 ++-- 9 files changed, 137 insertions(+), 33 deletions(-) create mode 100644 src/system/linux/elf.c create mode 100644 src/system/linux/elf.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 79e9a8a..d834843 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,14 @@ add_subdirectory(third-party/physfs) add_subdirectory(third-party/libxm) +if(UNIX) + set(SYSTEM_SOURCE_FILES + src/system/linux/elf.c + ) +else() + set(SYSTEM_SOURCE_FILES) +endif() + set(SOURCE_FILES third-party/physfs/extras/physfsrwops.c third-party/stb/stb_vorbis.c @@ -49,6 +57,8 @@ set(SOURCE_FILES src/game/scenes/scene.c src/game/scenes/scene.h src/game/scenes/title.c src/game/scenes/title.h src/game/scenes/ingame.c src/game/scenes/ingame.h + + ${SYSTEM_SOURCE_FILES} ) # target @@ -90,19 +100,24 @@ else() # these SHOULDN'T break anything... -fno-math-errno -ffp-contract=fast - -fno-signed-zeros - -fno-trapping-math - -freciprocal-math) + -fno-signed-zeros) set(BUILD_FLAGS_RELEASE + -O3 -flto -Wl,-gc-sections -fdata-sections - -ffunction-sections) + -ffunction-sections + -funroll-loops + -fomit-frame-pointer + -fallow-store-data-races) set(BUILD_FLAGS_DEBUG + -O0 -g3 -gdwarf + -fno-omit-frame-pointer + -fstack-protector-all -fsanitize=undefined - -fsanitize-trap=undefined) + -fsanitize=address) target_compile_options(${PROJECT_NAME} PRIVATE ${WARNING_FLAGS} ${BUILD_FLAGS} diff --git a/src/audio.c b/src/audio.c index 7a28241..e220178 100644 --- a/src/audio.c +++ b/src/audio.c @@ -244,7 +244,7 @@ static void audio_sample_and_mixin_channel(const struct audio_channel *channel, static uint8_t buffer[16384]; const int int16_buffer_frames = sizeof (buffer) / sizeof (int16_t); const int float_buffer_frames = sizeof (buffer) / sizeof (float); - const int stream_frames = len / sizeof (int16_t); + const int stream_frames = len / (int)(sizeof (int16_t)); switch (channel->file_type) { case audio_file_type_ogg: { diff --git a/src/game/scenes/ingame.c b/src/game/scenes/ingame.c index b018f25..1296ab7 100644 --- a/src/game/scenes/ingame.c +++ b/src/game/scenes/ingame.c @@ -65,6 +65,7 @@ static void ingame_end(struct state *state) { struct scene_ingame *scn = (struct scene_ingame *)state->scene; player_destroy(scn->player); world_destroy(scn->world); + free(state->scene); } diff --git a/src/game/scenes/title.c b/src/game/scenes/title.c index ed9b32a..7db88df 100644 --- a/src/game/scenes/title.c +++ b/src/game/scenes/title.c @@ -24,6 +24,7 @@ static void title_end(struct state *state) { struct scene_title *scn = (struct scene_title *)state->scene; player_destroy(scn->player); world_destroy(scn->world); + free(state->scene); } diff --git a/src/private/rendering.h b/src/private/rendering.h index 8257a6e..d4073e0 100644 --- a/src/private/rendering.h +++ b/src/private/rendering.h @@ -95,7 +95,6 @@ union uncolored_space_triangle { /* batch of primitives with overlapping properties */ struct mesh_batch { GLuint buffer; /* server side storage */ - size_t buffer_len; /* element count */ uint8_t *primitives; }; diff --git a/src/system/linux/elf.c b/src/system/linux/elf.c new file mode 100644 index 0000000..7377617 --- /dev/null +++ b/src/system/linux/elf.c @@ -0,0 +1,68 @@ +#include "elf.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +bool infer_elf_section_bounds(const char *const restrict name, + const char **restrict vm_start, + const char **restrict vm_end) +{ + bool result = false; + char buf[PATH_MAX]; + ssize_t l = readlink("/proc/self/exe", buf, PATH_MAX); + if (l == -1) + goto ERR_CANT_READLINK; + buf[l] = 0; /* readlink() doesn't write a terminator */ + + int elf = open(buf, O_RDONLY); + if (elf == -1) + goto ERR_CANT_OPEN_SELF; + + /* elf header */ + Elf64_Ehdr ehdr; + read(elf, &ehdr, sizeof ehdr); + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 || + ehdr.e_ident[EI_MAG1] != ELFMAG1 || + ehdr.e_ident[EI_MAG2] != ELFMAG2 || + ehdr.e_ident[EI_MAG3] != ELFMAG3) + goto ERR_NOT_ELF; + + /* section header string table */ + Elf64_Shdr shstrdr; + lseek(elf, ehdr.e_shoff + ehdr.e_shstrndx * sizeof (Elf64_Shdr), SEEK_SET); + read(elf, &shstrdr, sizeof shstrdr); + char *sh = malloc(shstrdr.sh_size); + lseek(elf, shstrdr.sh_offset, SEEK_SET); + read(elf, sh, shstrdr.sh_size); + + /* walk sections searching for needed name */ + lseek(elf, ehdr.e_shoff, SEEK_SET); + for (size_t s = 0; s < ehdr.e_shnum; ++s) { + Elf64_Shdr shdr; + read(elf, &shdr, sizeof shdr); + + if (strcmp(&sh[shdr.sh_name], name) == 0) { + result = true; + *vm_start = getauxval(AT_ENTRY) - ehdr.e_entry + (char *)shdr.sh_addr; + *vm_end = getauxval(AT_ENTRY) - ehdr.e_entry + (char *)shdr.sh_addr + shdr.sh_size; + break; + } + } + + free(sh); + +ERR_NOT_ELF: + close(elf); + +ERR_CANT_OPEN_SELF: +ERR_CANT_READLINK: + return result; +} diff --git a/src/system/linux/elf.h b/src/system/linux/elf.h new file mode 100644 index 0000000..23790d7 --- /dev/null +++ b/src/system/linux/elf.h @@ -0,0 +1,10 @@ +#ifndef ELF_H +#define ELF_H + +#include + +bool infer_elf_section_bounds(const char *restrict name, + const char **restrict vm_start, + const char **restrict vm_end); + +#endif diff --git a/src/textures.c b/src/textures.c index ca9ea9c..5ba34cf 100644 --- a/src/textures.c +++ b/src/textures.c @@ -295,8 +295,9 @@ void textures_dump_atlases(struct texture_cache *cache) { static t_texture_key textures_load(struct texture_cache *cache, const char *path) { /* no need to do anything if it was loaded already */ - if (shgeti(cache->hash, path) >= 0) - return (t_texture_key){0}; + const ptrdiff_t i = shgeti(cache->hash, path); + if (i >= 0) + return (t_texture_key){ (uint16_t)i }; SDL_Surface *surface = image_to_surface(path); struct texture new_texture = {0}; @@ -308,12 +309,12 @@ static t_texture_key textures_load(struct texture_cache *cache, const char *path upload_texture_from_surface(new_texture.loner_texture, surface); new_texture.srcrect = (t_rect) { .w = surface->w, .h = surface->h }; shput(cache->hash, path, new_texture); - return (t_texture_key){ shgeti(cache->hash, path) }; + return (t_texture_key){ (uint16_t)shgeti(cache->hash, path) }; } else { new_texture.atlas_index = cache->atlas_index; shput(cache->hash, path, new_texture); cache->is_dirty = true; - return (t_texture_key){ shgeti(cache->hash, path) }; + return (t_texture_key){ (uint16_t)shgeti(cache->hash, path) }; } } @@ -363,35 +364,44 @@ void textures_update_atlas(struct texture_cache *cache) { } /* EXPERIMANTAL: LIKELY TO BE REMOVED! */ -/* todo: If it's proven to be useful: add runtime checking for .rodata > .data */ -#ifdef __unix__ /* use rodata elf section for fast lookups of repeating textures */ +#ifdef __linux__ /* use rodata elf section for fast lookups of repeating textures */ -extern const char start_rodata_address[]; -extern const char stop_rodata_heuristic[]; +#include "system/linux/elf.h" -asm(".set start_rodata_address, .rodata"); -asm(".set stop_rodata_heuristic, .data"); /* there's nothing in default linker script to know the size of .rodata */ +static const char *rodata_start; +static const char *rodata_stop; -/* TODO: it might be better to contruct a new table that hashes pointers, not strings, to texture keys */ -/* this way every used texture will benefit, no matter the order of commission */ t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { static const char *last_path = NULL; static t_texture_key last_texture; + static struct ptr_to_texture { + const void *key; + t_texture_key value; + } *ptr_to_texture; - /* fast path */ + if (rodata_stop == NULL) + if (!infer_elf_section_bounds(".rodata", &rodata_start, &rodata_stop)) + CRY("Section inference", ".rodata section lookup failed"); + + /* the fastest path */ if (path == last_path) return last_texture; + else { + /* moderately fast path, by pointer hashing */ + const ptrdiff_t texture = hmgeti(ptr_to_texture, path); + if (texture != -1) { + if (path >= rodata_start && path < rodata_stop) + last_path = path; + last_texture = ptr_to_texture[texture].value; + return last_texture; + } + } - /* hash tables are assumed to be stable, so we just return indices */ - ptrdiff_t texture = shgeti(cache->hash, path); + /* try loading */ + last_texture = textures_load(cache, path); + hmput(ptr_to_texture, path, last_texture); - /* load it if it isn't */ - if (texture == -1) { - last_texture = textures_load(cache, path); - } else - last_texture = (t_texture_key){ (uint16_t)texture }; - - if (path >= start_rodata_address && path < stop_rodata_heuristic) + if (path >= rodata_start && path < rodata_stop) last_path = path; return last_texture; @@ -400,7 +410,7 @@ t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { #else t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { /* hash tables are assumed to be stable, so we just return indices */ - ptrdiff_t texture = shgeti(cache->hash, path); + const ptrdiff_t texture = shgeti(cache->hash, path); /* load it if it isn't */ if (texture == -1) { diff --git a/third-party/stb/stb_ds.h b/third-party/stb/stb_ds.h index e84c82d..5330d21 100644 --- a/third-party/stb/stb_ds.h +++ b/third-party/stb/stb_ds.h @@ -1121,7 +1121,7 @@ size_t stbds_hash_bytes(void *p, size_t len, size_t seed) unsigned char *d = (unsigned char *) p; if (len == 4) { - unsigned int hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); + uint32_t hash = (uint32_t)d[0] | ((uint32_t)d[1] << 8) | ((uint32_t)d[2] << 16) | ((uint32_t)d[3] << 24); #if 0 // HASH32-A Bob Jenkin's hash function w/o large constants hash ^= seed; @@ -1177,8 +1177,8 @@ size_t stbds_hash_bytes(void *p, size_t len, size_t seed) return (((size_t) hash << 16 << 16) | hash) ^ seed; } else if (len == 8 && sizeof(size_t) == 8) { - size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); - hash |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // avoid warning if size_t == 4 + size_t hash = (size_t)d[0] | ((size_t)d[1] << 8) | ((size_t)d[2] << 16) | ((size_t)d[3] << 24); + hash |= ((size_t)d[4] | ((size_t)d[5] << 8) | ((size_t)d[6] << 16) | ((size_t)d[7] << 24)) << 16 << 16; hash ^= seed; hash = (~hash) + (hash << 21); hash ^= STBDS_ROTATE_RIGHT(hash,24);