diff --git a/rpcs3/Emu/Cell/Modules/cellSpurs.cpp b/rpcs3/Emu/Cell/Modules/cellSpurs.cpp index a12e591c7a..8827959f2b 100644 --- a/rpcs3/Emu/Cell/Modules/cellSpurs.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSpurs.cpp @@ -17,7 +17,7 @@ logs::channel cellSpurs("cellSpurs"); -s32 sys_spu_image_close(vm::ptr img); +error_code sys_spu_image_close(vm::ptr img); // TODO struct cell_error_t diff --git a/rpcs3/Emu/Cell/Modules/sys_spu_.cpp b/rpcs3/Emu/Cell/Modules/sys_spu_.cpp index 36dd8ce03e..fa53704c3c 100644 --- a/rpcs3/Emu/Cell/Modules/sys_spu_.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_spu_.cpp @@ -5,6 +5,7 @@ #include "Emu/Cell/RawSPUThread.h" #include "Emu/Cell/lv2/sys_spu.h" #include "Crypto/unself.h" +#include "Loader/ELF.h" #include "sysPrxForUser.h" extern logs::channel sysPrxForUser; @@ -16,6 +17,172 @@ spu_printf_cb_t g_spu_printf_dgcb; spu_printf_cb_t g_spu_printf_atcb; spu_printf_cb_t g_spu_printf_dtcb; +struct spu_elf_ldr +{ + be_t _vtable; + vm::bptr src; + be_t x8; + be_t ehdr_off; + be_t phdr_off; + + s32 get_ehdr(vm::ptr> out) + { + if (!src) + { + return -1; + } + + if (_vtable == vm::cast(u32{1})) + { + vm::ptr> ehdr = vm::cast(src.addr() + ehdr_off); + std::memcpy(out.get_ptr(), ehdr.get_ptr(), 0x10); // Not needed? + out->e_type = ehdr->e_type; + out->e_machine = ehdr->e_machine; + out->e_version = ehdr->e_version; + out->e_entry = ehdr->e_entry; + out->e_phoff = ehdr->e_phoff; + out->e_shoff = ehdr->e_shoff; + out->e_flags = ehdr->e_flags; + out->e_ehsize = ehdr->e_ehsize; + out->e_phentsize = ehdr->e_phentsize; + out->e_phnum = ehdr->e_phnum; + out->e_shentsize = ehdr->e_shentsize; + out->e_shnum = ehdr->e_shnum; + out->e_shstrndx = ehdr->e_shstrndx; + } + else + { + vm::ptr> ehdr = vm::cast(src.addr() + ehdr_off); + *out = *ehdr; + } + + return 0; + } + + s32 get_phdr(vm::ptr> out, u32 count) + { + if (!src) + { + return -1; + } + + if (_vtable == vm::cast(u32{1})) + { + vm::ptr> ehdr = vm::cast(src.addr() + ehdr_off); + vm::ptr> phdr = vm::cast(src.addr() + (phdr_off ? +phdr_off : +ehdr->e_phoff)); + + for (; count; count--, phdr++, out++) + { + out->p_type = phdr->p_type; + out->p_flags = phdr->p_flags; + out->p_offset = phdr->p_offset; + out->p_vaddr = phdr->p_vaddr; + out->p_paddr = phdr->p_paddr; + out->p_filesz = phdr->p_filesz; + out->p_memsz = phdr->p_memsz; + out->p_align = phdr->p_align; + } + } + else + { + vm::ptr> ehdr = vm::cast(src.addr() + ehdr_off); + vm::ptr> phdr = vm::cast(src.addr() + (phdr_off ? +phdr_off : +ehdr->e_phoff)); + + std::memcpy(out.get_ptr(), phdr.get_ptr(), sizeof(*out) * count); + } + + return 0; + } +}; + +struct spu_elf_info +{ + u8 e_class; + vm::bptr ldr; + + struct sce_hdr + { + be_t se_magic; + be_t se_hver; + be_t se_flags; + be_t se_type; + be_t se_meta; + be_t se_hsize; + be_t se_esize; + } sce0; + + struct self_hdr + { + be_t se_htype; + be_t se_appinfooff; + be_t se_elfoff; + be_t se_phdroff; + be_t se_shdroff; + be_t se_secinfoff; + be_t se_sceveroff; + be_t se_controloff; + be_t se_controlsize; + be_t pad; + } self; + + // Doesn't exist there + spu_elf_ldr _overlay; + + error_code init(vm::ptr src, s32 arg2 = 0) + { + if (!src) + { + return CELL_EINVAL; + } + + u32 ehdr_off = 0; + u32 phdr_off = 0; + + // Check SCE header if found + std::memcpy(&sce0, src.get_ptr(), sizeof(sce0)); + + if (sce0.se_magic == 0x53434500 /* SCE\0 */) + { + if (sce0.se_hver != 2 || sce0.se_type != 1 || sce0.se_meta == 0) + { + return CELL_ENOEXEC; + } + + std::memcpy(&self, src.get_ptr(), sizeof(self)); + ehdr_off = static_cast(+self.se_elfoff); + phdr_off = static_cast(+self.se_phdroff); + + if (self.se_htype != 3 || !ehdr_off || !phdr_off) + { + return CELL_ENOEXEC; + } + } + + // Check ELF header + vm::ptr> ehdr = vm::cast(src.addr() + ehdr_off); + + if (ehdr->e_magic != "\177ELF"_u32 || ehdr->e_data != 2 /* BE */) + { + return CELL_ENOEXEC; + } + + if (ehdr->e_class != 1 && ehdr->e_class != 2) + { + return CELL_ENOEXEC; + } + + e_class = ehdr->e_class; + ldr = vm::get_addr(&_overlay); + ldr->_vtable = vm::cast(u32{e_class}); // TODO + ldr->src = vm::static_ptr_cast(src); + ldr->x8 = arg2; + ldr->ehdr_off = ehdr_off; + ldr->phdr_off = phdr_off; + + return CELL_OK; + } +}; + s32 sys_spu_elf_get_information(u32 elf_img, vm::ptr entry, vm::ptr nseg) { sysPrxForUser.todo("sys_spu_elf_get_information(elf_img=0x%x, entry=*0x%x, nseg=*0x%x)", elf_img, entry, nseg); @@ -28,34 +195,116 @@ s32 sys_spu_elf_get_segments(u32 elf_img, vm::ptr segments, s32 return CELL_OK; } -s32 sys_spu_image_import(vm::ptr img, u32 src, u32 type) +error_code sys_spu_image_import(vm::ptr img, u32 src, u32 type) { sysPrxForUser.warning("sys_spu_image_import(img=*0x%x, src=0x%x, type=%d)", img, src, type); - // Load from memory (TODO) - img->load(fs::file{vm::base(src), 0 - src}); - - return CELL_OK; -} - -s32 sys_spu_image_close(vm::ptr img) -{ - sysPrxForUser.warning("sys_spu_image_close(img=*0x%x)", img); - - if (img->type == SYS_SPU_IMAGE_TYPE_USER) + if (type != SYS_SPU_IMAGE_PROTECT && type != SYS_SPU_IMAGE_DIRECT) { - //_sys_free(img->segs.addr()); + return CELL_EINVAL; } - else if (img->type == SYS_SPU_IMAGE_TYPE_KERNEL) + + // Initialize ELF loader + vm::var info(spu_elf_info{}); + + if (auto res = info->init(vm::cast(src))) { - //return syscall_158(img); + return res; + } + + if (info->sce0.se_magic == 0x53434500) + { + return CELL_ENOEXEC; + } + + // Load ELF header + vm::var> ehdr(elf_ehdr{}); + + if (info->ldr->get_ehdr(ehdr) || ehdr->e_machine != elf_machine::spu || !ehdr->e_phnum) + { + return CELL_ENOEXEC; + } + + // Load program headers + vm::var[]> phdr(ehdr->e_phnum); + + if (info->ldr->get_phdr(phdr, ehdr->e_phnum)) + { + return CELL_ENOEXEC; + } + + if (type == SYS_SPU_IMAGE_PROTECT) + { + u32 img_size = 0; + + for (const auto& p : phdr) + { + if (p.p_type != 1 && p.p_type != 4) + { + return CELL_ENOEXEC; + } + + img_size = std::max(img_size, static_cast(p.p_offset + p.p_filesz)); + } + + return _sys_spu_image_import(img, src, img_size, 0); + } + else if (type == SYS_SPU_IMAGE_DIRECT) + { + s32 num_segs = sys_spu_image::get_nsegs(phdr); + + if (num_segs < 0) + { + return CELL_ENOEXEC; + } + + img->nsegs = num_segs; + img->entry_point = static_cast(ehdr->e_entry); + + vm::ptr segs = vm::cast(vm::alloc(num_segs * sizeof(sys_spu_segment), vm::main)); + + if (!segs) + { + return CELL_ENOMEM; + } + + if (sys_spu_image::fill(segs, phdr, src) != num_segs) + { + vm::dealloc(segs.addr()); + return CELL_ENOEXEC; + } + + img->type = SYS_SPU_IMAGE_TYPE_USER; + img->segs = segs; + return CELL_OK; + } + else + { + return CELL_EINVAL; + } + + return CELL_OK; +} + +error_code sys_spu_image_close(vm::ptr img) +{ + sysPrxForUser.warning("sys_spu_image_close(img=*0x%x)", img); + + if (img->type == SYS_SPU_IMAGE_TYPE_USER) + { + //_sys_free(img->segs.addr()); + vm::dealloc_verbose_nothrow(img->segs.addr(), vm::main); + } + else if (img->type == SYS_SPU_IMAGE_TYPE_KERNEL) + { + // Call the syscall + return _sys_spu_image_close(img); } else { return CELL_EINVAL; } - img->free(); return CELL_OK; } @@ -94,7 +343,7 @@ s32 sys_raw_spu_image_load(ppu_thread& ppu, s32 id, vm::ptr img) return CELL_OK; } -s32 _sys_spu_printf_initialize(spu_printf_cb_t agcb, spu_printf_cb_t dgcb, spu_printf_cb_t atcb, spu_printf_cb_t dtcb) +error_code _sys_spu_printf_initialize(spu_printf_cb_t agcb, spu_printf_cb_t dgcb, spu_printf_cb_t atcb, spu_printf_cb_t dtcb) { sysPrxForUser.warning("_sys_spu_printf_initialize(agcb=*0x%x, dgcb=*0x%x, atcb=*0x%x, dtcb=*0x%x)", agcb, dgcb, atcb, dtcb); @@ -107,7 +356,7 @@ s32 _sys_spu_printf_initialize(spu_printf_cb_t agcb, spu_printf_cb_t dgcb, spu_p return CELL_OK; } -s32 _sys_spu_printf_finalize() +error_code _sys_spu_printf_finalize() { sysPrxForUser.warning("_sys_spu_printf_finalize()"); @@ -119,7 +368,7 @@ s32 _sys_spu_printf_finalize() return CELL_OK; } -s32 _sys_spu_printf_attach_group(ppu_thread& ppu, u32 group) +error_code _sys_spu_printf_attach_group(ppu_thread& ppu, u32 group) { sysPrxForUser.warning("_sys_spu_printf_attach_group(group=0x%x)", group); @@ -131,7 +380,7 @@ s32 _sys_spu_printf_attach_group(ppu_thread& ppu, u32 group) return g_spu_printf_agcb(ppu, group); } -s32 _sys_spu_printf_detach_group(ppu_thread& ppu, u32 group) +error_code _sys_spu_printf_detach_group(ppu_thread& ppu, u32 group) { sysPrxForUser.warning("_sys_spu_printf_detach_group(group=0x%x)", group); @@ -143,7 +392,7 @@ s32 _sys_spu_printf_detach_group(ppu_thread& ppu, u32 group) return g_spu_printf_dgcb(ppu, group); } -s32 _sys_spu_printf_attach_thread(ppu_thread& ppu, u32 thread) +error_code _sys_spu_printf_attach_thread(ppu_thread& ppu, u32 thread) { sysPrxForUser.warning("_sys_spu_printf_attach_thread(thread=0x%x)", thread); @@ -155,7 +404,7 @@ s32 _sys_spu_printf_attach_thread(ppu_thread& ppu, u32 thread) return g_spu_printf_atcb(ppu, thread); } -s32 _sys_spu_printf_detach_thread(ppu_thread& ppu, u32 thread) +error_code _sys_spu_printf_detach_thread(ppu_thread& ppu, u32 thread) { sysPrxForUser.warning("_sys_spu_printf_detach_thread(thread=0x%x)", thread); diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index bb04d52613..71868422a4 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -20,20 +20,13 @@ logs::channel sys_spu("sys_spu"); void sys_spu_image::load(const fs::file& stream) { - const spu_exec_object obj{stream}; + const spu_exec_object obj{stream, 0, elf_opt::no_sections + elf_opt::no_data}; if (obj != elf_error::ok) { fmt::throw_exception("Failed to load SPU image: %s" HERE, obj.get_error()); } - this->type = SYS_SPU_IMAGE_TYPE_KERNEL; - this->entry_point = obj.header.e_entry; - this->segs.set(vm::alloc(65 * 4096, vm::main)); - this->nsegs = 0; - - const u32 addr = this->segs.addr() + 4096; - for (const auto& shdr : obj.shdrs) { LOG_NOTICE(SPU, "** Section: sh_type=0x%x, addr=0x%llx, size=0x%llx, flags=0x%x", shdr.sh_type, shdr.sh_addr, shdr.sh_size, shdr.sh_flags); @@ -43,37 +36,26 @@ void sys_spu_image::load(const fs::file& stream) { LOG_NOTICE(SPU, "** Segment: p_type=0x%x, p_vaddr=0x%llx, p_filesz=0x%llx, p_memsz=0x%llx, flags=0x%x", prog.p_type, prog.p_vaddr, prog.p_filesz, prog.p_memsz, prog.p_flags); - if (prog.p_type == SYS_SPU_SEGMENT_TYPE_COPY) - { - auto& seg = segs[nsegs++]; - seg.type = prog.p_type; - seg.ls = prog.p_vaddr; - seg.addr = addr + prog.p_vaddr; - seg.size = std::min(prog.p_filesz, prog.p_memsz); - std::memcpy(vm::base(seg.addr), prog.bin.data(), seg.size); - - if (prog.p_memsz > prog.p_filesz) - { - auto& zero = segs[nsegs++]; - zero.type = SYS_SPU_SEGMENT_TYPE_FILL; - zero.ls = prog.p_vaddr + prog.p_filesz; - zero.addr = 0; - zero.size = prog.p_memsz - seg.size; - } - } - else if (prog.p_type == SYS_SPU_SEGMENT_TYPE_INFO) - { - auto& seg = segs[nsegs++]; - seg.type = SYS_SPU_SEGMENT_TYPE_INFO; - seg.ls = prog.p_vaddr; - seg.addr = 0; - seg.size = prog.p_filesz; - } - else + if (prog.p_type != SYS_SPU_SEGMENT_TYPE_COPY && prog.p_type != SYS_SPU_SEGMENT_TYPE_INFO) { LOG_ERROR(SPU, "Unknown program type (0x%x)", prog.p_type); } } + + type = SYS_SPU_IMAGE_TYPE_KERNEL; + entry_point = obj.header.e_entry; + nsegs = sys_spu_image::get_nsegs(obj.progs); + segs = vm::cast(vm::alloc(nsegs * sizeof(sys_spu_segment) + ::size32(stream), vm::main)); + + const u32 src = segs.addr() + nsegs * sizeof(sys_spu_segment); + + stream.seek(0); + stream.read(vm::base(src), stream.size()); + + if (nsegs < 0 || sys_spu_image::fill(segs, obj.progs, src) != nsegs) + { + fmt::throw_exception("Failed to load SPU segments (%d)" HERE, nsegs); + } } void sys_spu_image::free() @@ -100,13 +82,13 @@ void sys_spu_image::deploy(u32 loc) fmt::append(dump, "\n\t[%d] t=0x%x, ls=0x%x, size=0x%x, addr=0x%x", i, seg.type, seg.ls, seg.size, seg.addr); - // Hash big-endian values sha1_update(&sha, (uchar*)&seg.type, sizeof(seg.type)); - sha1_update(&sha, (uchar*)&seg.size, sizeof(seg.size)); + // Hash big-endian values if (seg.type == SYS_SPU_SEGMENT_TYPE_COPY) { std::memcpy(vm::base(loc + seg.ls), vm::base(seg.addr), seg.size); + sha1_update(&sha, (uchar*)&seg.size, sizeof(seg.size)); sha1_update(&sha, (uchar*)&seg.ls, sizeof(seg.ls)); sha1_update(&sha, vm::g_base_addr + seg.addr, seg.size); } @@ -118,9 +100,15 @@ void sys_spu_image::deploy(u32 loc) } std::fill_n(vm::_ptr(loc + seg.ls), seg.size / 4, seg.addr); + sha1_update(&sha, (uchar*)&seg.size, sizeof(seg.size)); sha1_update(&sha, (uchar*)&seg.ls, sizeof(seg.ls)); sha1_update(&sha, (uchar*)&seg.addr, sizeof(seg.addr)); } + else if (seg.type == SYS_SPU_SEGMENT_TYPE_INFO) + { + const be_t size = seg.size + 0x14; // Workaround + sha1_update(&sha, (uchar*)&size, sizeof(size)); + } } sha1_finish(&sha, sha1_hash); @@ -182,18 +170,20 @@ error_code sys_spu_image_open(vm::ptr img, vm::cptr path) return CELL_OK; } -error_code _sys_spu_image_import(vm::ptr img, u32 src, u32 arg3, u32 arg4) +error_code _sys_spu_image_import(vm::ptr img, u32 src, u32 size, u32 arg4) { - sys_spu.todo("_sys_spu_image_import(img=*0x%x, src=*0x%x, arg3=0x%x, arg4=0x%x)", img, src, arg3, arg4); + sys_spu.warning("_sys_spu_image_import(img=*0x%x, src=*0x%x, size=0x%x, arg4=0x%x)", img, src, size, arg4); - fmt::throw_exception("Unimplemented syscall: _sys_spu_image_import"); + img->load(fs::file{vm::base(src), size}); + return CELL_OK; } error_code _sys_spu_image_close(vm::ptr img) { - sys_spu.todo("_sys_spu_image_close(img=*0x%x)", img); + sys_spu.warning("_sys_spu_image_close(img=*0x%x)", img); - fmt::throw_exception("Unimplemented syscall: _sys_spu_image_close"); + vm::dealloc(img->segs.addr(), vm::main); + return CELL_OK; } error_code _sys_raw_spu_image_load(vm::ptr img, u32 ptr, u32 arg3) diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.h b/rpcs3/Emu/Cell/lv2/sys_spu.h index 8aa71ca2db..46348af11f 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.h +++ b/rpcs3/Emu/Cell/lv2/sys_spu.h @@ -112,6 +112,74 @@ struct sys_spu_image vm::ps3::bptr segs; be_t nsegs; + template + static s32 get_nsegs(const Phdrs& phdrs) + { + s32 num_segs = 0; + + for (const auto& phdr : phdrs) + { + if (phdr.p_type != 1 && phdr.p_type != 4) + { + return -1; + } + + if (phdr.p_type == 1 && phdr.p_filesz != phdr.p_memsz && phdr.p_filesz) + { + num_segs += 2; + } + else + { + num_segs += 1; + } + } + + return num_segs; + } + + template + static s32 fill(vm::ps3::ptr segs, const Phdrs& phdrs, u32 src) + { + s32 num_segs = 0; + + for (const auto& phdr : phdrs) + { + if (phdr.p_type == 1) + { + if (phdr.p_filesz) + { + auto* seg = &segs[num_segs++]; + seg->type = SYS_SPU_SEGMENT_TYPE_COPY; + seg->ls = static_cast(phdr.p_vaddr); + seg->size = static_cast(phdr.p_filesz); + seg->addr = static_cast(phdr.p_offset + src); + } + + if (phdr.p_memsz > phdr.p_filesz) + { + auto* seg = &segs[num_segs++]; + seg->type = SYS_SPU_SEGMENT_TYPE_FILL; + seg->ls = static_cast(phdr.p_vaddr + phdr.p_filesz); + seg->size = static_cast(phdr.p_memsz - phdr.p_filesz); + seg->addr = 0; + } + } + else if (phdr.p_type == 4) + { + auto* seg = &segs[num_segs++]; + seg->type = SYS_SPU_SEGMENT_TYPE_INFO; + seg->size = 0x20; + seg->addr = static_cast(phdr.p_offset + 0x14 + src); + } + else + { + return -1; + } + } + + return num_segs; + } + void load(const fs::file& stream); void free(); void deploy(u32 loc); @@ -205,7 +273,7 @@ class ppu_thread; error_code sys_spu_initialize(u32 max_usable_spu, u32 max_raw_spu); error_code _sys_spu_image_get_information(vm::ps3::ptr img, u32 ptr1, u32 ptr2); error_code sys_spu_image_open(vm::ps3::ptr img, vm::ps3::cptr path); -error_code _sys_spu_image_import(vm::ps3::ptr img, u32 src, u32 arg3, u32 arg4); +error_code _sys_spu_image_import(vm::ps3::ptr img, u32 src, u32 size, u32 arg4); error_code _sys_spu_image_close(vm::ps3::ptr img); error_code _sys_raw_spu_image_load(vm::ps3::ptr img, u32 ptr, u32 arg3); error_code sys_spu_thread_initialize(vm::ps3::ptr thread, u32 group, u32 spu_num, vm::ps3::ptr, vm::ps3::ptr, vm::ps3::ptr); diff --git a/rpcs3/Emu/Memory/vm_var.h b/rpcs3/Emu/Memory/vm_var.h index 41b677cea5..781e386a11 100644 --- a/rpcs3/Emu/Memory/vm_var.h +++ b/rpcs3/Emu/Memory/vm_var.h @@ -96,6 +96,16 @@ namespace vm { return m_size / SIZE_32(T); } + + auto begin() const + { + return *this + 0; + } + + auto end() const + { + return *this + get_count(); + } }; // LE variable diff --git a/rpcs3/Loader/ELF.h b/rpcs3/Loader/ELF.h index dc5c7dc132..de1af42ccd 100644 --- a/rpcs3/Loader/ELF.h +++ b/rpcs3/Loader/ELF.h @@ -2,6 +2,7 @@ #include "../../Utilities/types.h" #include "../../Utilities/File.h" +#include "../../Utilities/bit_set.h" enum class elf_os : u8 { @@ -133,6 +134,16 @@ struct elf_shdr en_t sh_entsize; }; +// ELF loading options +enum class elf_opt : u32 +{ + no_programs, // Don't load phdrs, implies no_data + no_sections, // Don't load shdrs + no_data, // Load phdrs without data + + __bitset_enum_max +}; + // ELF loading error enum class elf_error { @@ -180,12 +191,12 @@ public: public: elf_object() = default; - elf_object(const fs::file& stream, u64 offset = 0) + elf_object(const fs::file& stream, u64 offset = 0, bs_t opts = {}) { - open(stream, offset); + open(stream, offset, opts); } - elf_error open(const fs::file& stream, u64 offset = 0) + elf_error open(const fs::file& stream, u64 offset = 0, bs_t opts = {}) { // Check stream if (!stream) @@ -231,15 +242,23 @@ public: return set_error(elf_error::header_version); // Load program headers - std::vector _phdrs(header.e_phnum); - stream.seek(offset + header.e_phoff); - if (!stream.read(_phdrs)) - return set_error(elf_error::stream_phdrs); + std::vector _phdrs; + + if (!test(opts, elf_opt::no_programs)) + { + _phdrs.resize(header.e_phnum); + stream.seek(offset + header.e_phoff); + if (!stream.read(_phdrs)) + return set_error(elf_error::stream_phdrs); + } - shdrs.resize(header.e_shnum); - stream.seek(offset + header.e_shoff); - if (!stream.read(shdrs)) - return set_error(elf_error::stream_shdrs); + if (!test(opts, elf_opt::no_sections)) + { + shdrs.resize(header.e_shnum); + stream.seek(offset + header.e_shoff); + if (!stream.read(shdrs)) + return set_error(elf_error::stream_shdrs); + } progs.clear(); progs.reserve(_phdrs.size()); @@ -248,10 +267,14 @@ public: progs.emplace_back(); static_cast(progs.back()) = hdr; - progs.back().bin.resize(hdr.p_filesz); - stream.seek(offset + hdr.p_offset); - if (!stream.read(progs.back().bin)) - return set_error(elf_error::stream_data); + + if (!test(opts, elf_opt::no_data)) + { + progs.back().bin.resize(hdr.p_filesz); + stream.seek(offset + hdr.p_offset); + if (!stream.read(progs.back().bin)) + return set_error(elf_error::stream_data); + } } shdrs.shrink_to_fit();