From 744a1528ccb98896f8e2b46226c770b76b5f9eaa Mon Sep 17 00:00:00 2001 From: Eladash Date: Thu, 3 Aug 2023 11:15:34 +0300 Subject: [PATCH] Optimize memory usage of ELF loader Do not duplicate shdr memory when it is present in phdr. --- rpcs3/Emu/Cell/PPUModule.cpp | 2 +- rpcs3/Emu/Cell/RawSPUThread.cpp | 2 +- rpcs3/Loader/ELF.h | 76 +++++++++++++++++++++++++++++++-- 3 files changed, 74 insertions(+), 6 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index f311763a42..8ac6832c9d 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -2783,7 +2783,7 @@ bool ppu_load_rel_exec(const ppu_rel_object& elf) _sec.addr = addr; relm.secs.emplace_back(_sec); - std::memcpy(vm::base(addr), s.bin.data(), size); + std::memcpy(vm::base(addr), s.get_bin().data(), size); addr = utils::align(addr + size, 128); } } diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index eaf5e2dbc1..f442e73f89 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -410,7 +410,7 @@ void spu_load_rel_exec(const spu_rel_object& elf) { if (shdr.sh_type == sec_type::sht_progbits && shdr.sh_flags().all_of(sh_flag::shf_alloc)) { - std::memcpy(spu->_ptr(offs), shdr.bin.data(), shdr.sh_size); + std::memcpy(spu->_ptr(offs), shdr.get_bin().data(), shdr.sh_size); offs = utils::align(offs + shdr.sh_size, 4); } } diff --git a/rpcs3/Loader/ELF.h b/rpcs3/Loader/ELF.h index c7340b7115..831e58dea1 100644 --- a/rpcs3/Loader/ELF.h +++ b/rpcs3/Loader/ELF.h @@ -4,6 +4,8 @@ #include "../../Utilities/File.h" #include "../../Utilities/bit_set.h" +#include + enum class elf_os : u8 { none = 0, @@ -191,10 +193,21 @@ template class en_t, typename sz_t> struct elf_shdata final : elf_shdr { std::vector bin{}; + std::span bin_view{}; using base = elf_shdr; elf_shdata() = default; + + std::span get_bin() const + { + if (!bin_view.empty()) + { + return bin_view; + } + + return {bin.data(), bin.size()}; + } }; // ELF loading options @@ -340,6 +353,26 @@ public: if (!(opts & elf_opt::no_data) && is_memorizable_section(shdr.sh_type, shdr.sh_flags())) { + usz p_index = umax; + + for (const auto& hdr : _phdrs) + { + // Try to find it in phdr data instead of allocating new section + p_index++; + + if (hdr.p_offset <= shdr.sh_offset && shdr.sh_offset + shdr.sh_size - 1 <= hdr.p_offset + hdr.p_filesz - 1) + { + const auto& prog = ::at32(progs, p_index); + shdrs.back().bin_view = {prog.bin.data() + shdr.sh_offset - hdr.p_offset, shdr.sh_size}; + } + } + + if (!shdrs.back().bin_view.empty()) + { + // Optimized + continue; + } + stream.seek(offset + shdr.sh_offset); if (!stream.read(shdrs.back().bin, shdr.sh_size)) return set_error(elf_error::stream_data); @@ -400,14 +433,49 @@ public: stream.write(shdr_t{}); } - for (shdr_t shdr : shdrs) + for (const auto& shdr : shdrs) { + shdr_t out = static_cast(shdr); + if (is_memorizable_section(shdr.sh_type, shdr.sh_flags())) { - shdr.sh_offset = std::exchange(off, off + shdr.sh_size); + usz p_index = umax; + usz data_base = header.e_shoff + u32{sizeof(shdr_t)} * ::size32(shdrs); + bool result = false; + + for (const auto& hdr : progs) + { + if (shdr.bin_view.empty()) + { + break; + } + + // Try to find it in phdr data instead of writing new section + p_index++; + + // Rely on previous sh_offset value! + if (hdr.p_offset <= shdr.sh_offset && shdr.sh_offset + shdr.sh_size - 1 <= hdr.p_offset + hdr.p_filesz - 1) + { + const auto& prog = ::at32(progs, p_index); + out.sh_offset = data_base + shdr.sh_offset - hdr.p_offset; + result = true; + break; + } + + data_base += ::at32(progs, p_index).p_filesz; + } + + if (result) + { + // Optimized + } + else + { + out.sh_offset = std::exchange(off, off + shdr.sh_size); + } } - stream.write(shdr); + stream.write(static_cast(out)); } // Write data @@ -418,7 +486,7 @@ public: for (const auto& shdr : shdrs) { - if (!is_memorizable_section(shdr.sh_type, shdr.sh_flags())) + if (!is_memorizable_section(shdr.sh_type, shdr.sh_flags()) || !shdr.bin_view.empty()) { continue; }