_sys_spu_image_import implemented

vm:var<T[]> improved (begin/end)
sys_spu_image_import rewritten
This commit is contained in:
Nekotekina 2017-08-26 22:09:46 +03:00
parent dfc970c926
commit aa5dc5455e
6 changed files with 421 additions and 81 deletions

View File

@ -17,7 +17,7 @@
logs::channel cellSpurs("cellSpurs");
s32 sys_spu_image_close(vm::ptr<sys_spu_image> img);
error_code sys_spu_image_close(vm::ptr<sys_spu_image> img);
// TODO
struct cell_error_t

View File

@ -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<u32> _vtable;
vm::bptr<void> src;
be_t<u32> x8;
be_t<u64> ehdr_off;
be_t<u64> phdr_off;
s32 get_ehdr(vm::ptr<elf_ehdr<elf_be, u64>> out)
{
if (!src)
{
return -1;
}
if (_vtable == vm::cast(u32{1}))
{
vm::ptr<elf_ehdr<elf_be, u32>> 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<elf_ehdr<elf_be, u64>> ehdr = vm::cast(src.addr() + ehdr_off);
*out = *ehdr;
}
return 0;
}
s32 get_phdr(vm::ptr<elf_phdr<elf_be, u64>> out, u32 count)
{
if (!src)
{
return -1;
}
if (_vtable == vm::cast(u32{1}))
{
vm::ptr<elf_ehdr<elf_be, u32>> ehdr = vm::cast(src.addr() + ehdr_off);
vm::ptr<elf_phdr<elf_be, u32>> 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<elf_ehdr<elf_be, u64>> ehdr = vm::cast(src.addr() + ehdr_off);
vm::ptr<elf_phdr<elf_be, u64>> 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<spu_elf_ldr> ldr;
struct sce_hdr
{
be_t<u32> se_magic;
be_t<u32> se_hver;
be_t<u16> se_flags;
be_t<u16> se_type;
be_t<u32> se_meta;
be_t<u64> se_hsize;
be_t<u64> se_esize;
} sce0;
struct self_hdr
{
be_t<u64> se_htype;
be_t<u64> se_appinfooff;
be_t<u64> se_elfoff;
be_t<u64> se_phdroff;
be_t<u64> se_shdroff;
be_t<u64> se_secinfoff;
be_t<u64> se_sceveroff;
be_t<u64> se_controloff;
be_t<u64> se_controlsize;
be_t<u64> pad;
} self;
// Doesn't exist there
spu_elf_ldr _overlay;
error_code init(vm::ptr<void> 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<u32>(+self.se_elfoff);
phdr_off = static_cast<u32>(+self.se_phdroff);
if (self.se_htype != 3 || !ehdr_off || !phdr_off)
{
return CELL_ENOEXEC;
}
}
// Check ELF header
vm::ptr<elf_ehdr<elf_be, u32>> 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<u8>(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<u32> entry, vm::ptr<s32> 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<sys_spu_segment> segments, s32
return CELL_OK;
}
s32 sys_spu_image_import(vm::ptr<sys_spu_image> img, u32 src, u32 type)
error_code sys_spu_image_import(vm::ptr<sys_spu_image> 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<sys_spu_image> 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<spu_elf_info> 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<elf_ehdr<elf_be, u64>> ehdr(elf_ehdr<elf_be, u64>{});
if (info->ldr->get_ehdr(ehdr) || ehdr->e_machine != elf_machine::spu || !ehdr->e_phnum)
{
return CELL_ENOEXEC;
}
// Load program headers
vm::var<elf_phdr<elf_be, u64>[]> 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<u32>(img_size, static_cast<u32>(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<u32>(ehdr->e_entry);
vm::ptr<sys_spu_segment> 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<sys_spu_image> 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<sys_spu_image> 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);

View File

@ -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<u32>(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<u32> 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<sys_spu_image> img, vm::cptr<char> path)
return CELL_OK;
}
error_code _sys_spu_image_import(vm::ptr<sys_spu_image> img, u32 src, u32 arg3, u32 arg4)
error_code _sys_spu_image_import(vm::ptr<sys_spu_image> 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<sys_spu_image> 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<sys_spu_image> img, u32 ptr, u32 arg3)

View File

@ -112,6 +112,74 @@ struct sys_spu_image
vm::ps3::bptr<sys_spu_segment> segs;
be_t<s32> nsegs;
template <typename Phdrs>
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 <typename Phdrs>
static s32 fill(vm::ps3::ptr<sys_spu_segment> 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<u32>(phdr.p_vaddr);
seg->size = static_cast<u32>(phdr.p_filesz);
seg->addr = static_cast<u32>(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<u32>(phdr.p_vaddr + phdr.p_filesz);
seg->size = static_cast<u32>(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<u32>(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<sys_spu_image> img, u32 ptr1, u32 ptr2);
error_code sys_spu_image_open(vm::ps3::ptr<sys_spu_image> img, vm::ps3::cptr<char> path);
error_code _sys_spu_image_import(vm::ps3::ptr<sys_spu_image> img, u32 src, u32 arg3, u32 arg4);
error_code _sys_spu_image_import(vm::ps3::ptr<sys_spu_image> img, u32 src, u32 size, u32 arg4);
error_code _sys_spu_image_close(vm::ps3::ptr<sys_spu_image> img);
error_code _sys_raw_spu_image_load(vm::ps3::ptr<sys_spu_image> img, u32 ptr, u32 arg3);
error_code sys_spu_thread_initialize(vm::ps3::ptr<u32> thread, u32 group, u32 spu_num, vm::ps3::ptr<sys_spu_image>, vm::ps3::ptr<sys_spu_thread_attribute>, vm::ps3::ptr<sys_spu_thread_argument>);

View File

@ -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

View File

@ -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<sz_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<elf_opt> 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<elf_opt> opts = {})
{
// Check stream
if (!stream)
@ -231,15 +242,23 @@ public:
return set_error(elf_error::header_version);
// Load program headers
std::vector<phdr_t> _phdrs(header.e_phnum);
stream.seek(offset + header.e_phoff);
if (!stream.read(_phdrs))
return set_error(elf_error::stream_phdrs);
std::vector<phdr_t> _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<phdr_t&>(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();