| | |
| | |
| | |
| |
|
| | #include <cstring> |
| | #include <memory> |
| | #include <string> |
| | #include "common/common_types.h" |
| | #include "common/file_util.h" |
| | #include "common/logging/log.h" |
| | #include "core/core.h" |
| | #include "core/hle/kernel/process.h" |
| | #include "core/hle/kernel/resource_limit.h" |
| | #include "core/loader/elf.h" |
| | #include "core/memory.h" |
| |
|
| | using Kernel::CodeSet; |
| |
|
| | |
| | enum ElfType { |
| | ET_NONE = 0, |
| | ET_REL = 1, |
| | ET_EXEC = 2, |
| | ET_DYN = 3, |
| | ET_CORE = 4, |
| | ET_LOPROC = 0xFF00, |
| | ET_HIPROC = 0xFFFF, |
| | }; |
| |
|
| | |
| | enum ElfMachine { |
| | EM_NONE = 0, |
| | EM_M32 = 1, |
| | EM_SPARC = 2, |
| | EM_386 = 3, |
| | EM_68K = 4, |
| | EM_88K = 5, |
| | EM_860 = 7, |
| | EM_MIPS = 8 |
| | }; |
| |
|
| | |
| | #define EV_NONE 0 |
| | #define EV_CURRENT 1 |
| |
|
| | |
| | #define EI_MAG0 0 |
| | #define EI_MAG1 1 |
| | #define EI_MAG2 2 |
| | #define EI_MAG3 3 |
| | #define EI_CLASS 4 |
| | #define EI_DATA 5 |
| | #define EI_VERSION 6 |
| | #define EI_PAD 7 |
| | #define EI_NIDENT 16 |
| |
|
| | |
| |
|
| | |
| | #define SHT_NULL 0 |
| | #define SHT_PROGBITS 1 |
| | #define SHT_SYMTAB 2 |
| | #define SHT_STRTAB 3 |
| | #define SHT_RELA 4 |
| | #define SHT_HASH 5 |
| | #define SHT_DYNAMIC 6 |
| | #define SHT_NOTE 7 |
| | #define SHT_NOBITS 8 |
| | #define SHT_REL 9 |
| | #define SHT_SHLIB 10 |
| | #define SHT_DYNSYM 11 |
| | #define SHT_LOPROC 0x70000000 |
| | #define SHT_HIPROC 0x7FFFFFFF |
| | #define SHT_LOUSER 0x80000000 |
| | #define SHT_HIUSER 0xFFFFFFFF |
| |
|
| | |
| | enum ElfSectionFlags { |
| | SHF_WRITE = 0x1, |
| | SHF_ALLOC = 0x2, |
| | SHF_EXECINSTR = 0x4, |
| | SHF_MASKPROC = 0xF0000000, |
| | }; |
| |
|
| | |
| | #define PT_NULL 0 |
| | #define PT_LOAD 1 |
| | #define PT_DYNAMIC 2 |
| | #define PT_INTERP 3 |
| | #define PT_NOTE 4 |
| | #define PT_SHLIB 5 |
| | #define PT_PHDR 6 |
| | #define PT_LOPROC 0x70000000 |
| | #define PT_HIPROC 0x7FFFFFFF |
| |
|
| | |
| | #define PF_X 0x1 |
| | #define PF_W 0x2 |
| | #define PF_R 0x4 |
| | #define PF_MASKPROC 0xF0000000 |
| |
|
| | typedef unsigned int Elf32_Addr; |
| | typedef unsigned short Elf32_Half; |
| | typedef unsigned int Elf32_Off; |
| | typedef signed int Elf32_Sword; |
| | typedef unsigned int Elf32_Word; |
| |
|
| | struct Elf32_Ehdr { |
| | unsigned char e_ident[EI_NIDENT]; |
| | Elf32_Half e_type; |
| | Elf32_Half e_machine; |
| | Elf32_Word e_version; |
| | Elf32_Addr e_entry; |
| | Elf32_Off e_phoff; |
| | Elf32_Off e_shoff; |
| | Elf32_Word e_flags; |
| | Elf32_Half e_ehsize; |
| | Elf32_Half e_phentsize; |
| | Elf32_Half e_phnum; |
| | Elf32_Half e_shentsize; |
| | Elf32_Half e_shnum; |
| | Elf32_Half e_shstrndx; |
| | }; |
| |
|
| | |
| | struct Elf32_Shdr { |
| | Elf32_Word sh_name; |
| | Elf32_Word sh_type; |
| | Elf32_Word sh_flags; |
| | Elf32_Addr sh_addr; |
| | Elf32_Off sh_offset; |
| | Elf32_Word sh_size; |
| | Elf32_Word sh_link; |
| | Elf32_Word sh_info; |
| | Elf32_Word sh_addralign; |
| | Elf32_Word sh_entsize; |
| | }; |
| |
|
| | |
| | struct Elf32_Phdr { |
| | Elf32_Word p_type; |
| | Elf32_Off p_offset; |
| | Elf32_Addr p_vaddr; |
| | Elf32_Addr p_paddr; |
| | Elf32_Word p_filesz; |
| | Elf32_Word p_memsz; |
| | Elf32_Word p_flags; |
| | Elf32_Word p_align; |
| | }; |
| |
|
| | |
| | struct Elf32_Sym { |
| | Elf32_Word st_name; |
| | Elf32_Addr st_value; |
| | Elf32_Word st_size; |
| | unsigned char st_info; |
| | unsigned char st_other; |
| | Elf32_Half st_shndx; |
| | }; |
| |
|
| | |
| | struct Elf32_Rel { |
| | Elf32_Addr r_offset; |
| | Elf32_Word r_info; |
| | }; |
| |
|
| | typedef int SectionID; |
| |
|
| | class ElfReader { |
| | private: |
| | char* base; |
| | u32* base32; |
| |
|
| | Elf32_Ehdr* header; |
| | Elf32_Phdr* segments; |
| | Elf32_Shdr* sections; |
| |
|
| | u32* sectionAddrs; |
| | bool relocate; |
| | u32 entryPoint; |
| |
|
| | public: |
| | explicit ElfReader(void* ptr); |
| |
|
| | u32 Read32(int off) const { |
| | return base32[off >> 2]; |
| | } |
| |
|
| | |
| | ElfType GetType() const { |
| | return (ElfType)(header->e_type); |
| | } |
| | ElfMachine GetMachine() const { |
| | return (ElfMachine)(header->e_machine); |
| | } |
| | u32 GetEntryPoint() const { |
| | return entryPoint; |
| | } |
| | u32 GetFlags() const { |
| | return (u32)(header->e_flags); |
| | } |
| | std::shared_ptr<CodeSet> LoadInto(Core::System& system, u32 vaddr); |
| |
|
| | int GetNumSegments() const { |
| | return (int)(header->e_phnum); |
| | } |
| | int GetNumSections() const { |
| | return (int)(header->e_shnum); |
| | } |
| | const u8* GetPtr(int offset) const { |
| | return (u8*)base + offset; |
| | } |
| | const char* GetSectionName(int section) const; |
| | const u8* GetSectionDataPtr(int section) const { |
| | if (section < 0 || section >= header->e_shnum) |
| | return nullptr; |
| | if (sections[section].sh_type != SHT_NOBITS) |
| | return GetPtr(sections[section].sh_offset); |
| | else |
| | return nullptr; |
| | } |
| | bool IsCodeSection(int section) const { |
| | return sections[section].sh_type == SHT_PROGBITS; |
| | } |
| | const u8* GetSegmentPtr(int segment) { |
| | return GetPtr(segments[segment].p_offset); |
| | } |
| | u32 GetSectionAddr(SectionID section) const { |
| | return sectionAddrs[section]; |
| | } |
| | unsigned int GetSectionSize(SectionID section) const { |
| | return sections[section].sh_size; |
| | } |
| | SectionID GetSectionByName(const char* name, int firstSection = 0) const; |
| |
|
| | bool DidRelocate() const { |
| | return relocate; |
| | } |
| | }; |
| |
|
| | ElfReader::ElfReader(void* ptr) { |
| | base = (char*)ptr; |
| | base32 = (u32*)ptr; |
| | header = (Elf32_Ehdr*)ptr; |
| |
|
| | segments = (Elf32_Phdr*)(base + header->e_phoff); |
| | sections = (Elf32_Shdr*)(base + header->e_shoff); |
| |
|
| | entryPoint = header->e_entry; |
| | } |
| |
|
| | const char* ElfReader::GetSectionName(int section) const { |
| | if (sections[section].sh_type == SHT_NULL) |
| | return nullptr; |
| |
|
| | int name_offset = sections[section].sh_name; |
| | const char* ptr = reinterpret_cast<const char*>(GetSectionDataPtr(header->e_shstrndx)); |
| |
|
| | if (ptr) |
| | return ptr + name_offset; |
| |
|
| | return nullptr; |
| | } |
| |
|
| | std::shared_ptr<CodeSet> ElfReader::LoadInto(Core::System& system, u32 vaddr) { |
| | LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); |
| |
|
| | |
| | relocate = (header->e_type != ET_EXEC); |
| |
|
| | if (relocate) { |
| | LOG_DEBUG(Loader, "Relocatable module"); |
| | entryPoint += vaddr; |
| | } else { |
| | LOG_DEBUG(Loader, "Prerelocated executable"); |
| | } |
| | LOG_DEBUG(Loader, "{} segments:", header->e_phnum); |
| |
|
| | |
| | u32 base_addr = relocate ? vaddr : 0; |
| |
|
| | u32 total_image_size = 0; |
| | for (unsigned int i = 0; i < header->e_phnum; ++i) { |
| | Elf32_Phdr* p = &segments[i]; |
| | if (p->p_type == PT_LOAD) { |
| | total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF; |
| | } |
| | } |
| |
|
| | std::vector<u8> program_image(total_image_size); |
| | std::size_t current_image_position = 0; |
| |
|
| | std::shared_ptr<CodeSet> codeset = system.Kernel().CreateCodeSet("", 0); |
| |
|
| | for (unsigned int i = 0; i < header->e_phnum; ++i) { |
| | Elf32_Phdr* p = &segments[i]; |
| | LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type, |
| | p->p_vaddr, p->p_filesz, p->p_memsz); |
| |
|
| | if (p->p_type == PT_LOAD) { |
| | CodeSet::Segment* codeset_segment; |
| | u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X); |
| | if (permission_flags == (PF_R | PF_X)) { |
| | codeset_segment = &codeset->CodeSegment(); |
| | } else if (permission_flags == (PF_R)) { |
| | codeset_segment = &codeset->RODataSegment(); |
| | } else if (permission_flags == (PF_R | PF_W)) { |
| | codeset_segment = &codeset->DataSegment(); |
| | } else { |
| | LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, |
| | p->p_flags); |
| | continue; |
| | } |
| |
|
| | if (codeset_segment->size != 0) { |
| | LOG_ERROR(Loader, |
| | "ELF has more than one segment of the same type. Skipping extra " |
| | "segment (id {})", |
| | i); |
| | continue; |
| | } |
| |
|
| | u32 segment_addr = base_addr + p->p_vaddr; |
| | u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF; |
| |
|
| | codeset_segment->offset = current_image_position; |
| | codeset_segment->addr = segment_addr; |
| | codeset_segment->size = aligned_size; |
| |
|
| | std::memcpy(&program_image[current_image_position], GetSegmentPtr(i), p->p_filesz); |
| | current_image_position += aligned_size; |
| | } |
| | } |
| |
|
| | codeset->entrypoint = base_addr + header->e_entry; |
| | codeset->memory = std::move(program_image); |
| |
|
| | LOG_DEBUG(Loader, "Done loading."); |
| |
|
| | return codeset; |
| | } |
| |
|
| | SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const { |
| | for (int i = firstSection; i < header->e_shnum; i++) { |
| | const char* secname = GetSectionName(i); |
| |
|
| | if (secname != nullptr && strcmp(name, secname) == 0) |
| | return i; |
| | } |
| | return -1; |
| | } |
| |
|
| | namespace Loader { |
| |
|
| | FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file) { |
| | u32 magic; |
| | file.Seek(0, SEEK_SET); |
| | if (1 != file.ReadArray<u32>(&magic, 1)) |
| | return FileType::Error; |
| |
|
| | if (MakeMagic('\x7f', 'E', 'L', 'F') == magic) |
| | return FileType::ELF; |
| |
|
| | return FileType::Error; |
| | } |
| |
|
| | ResultStatus AppLoader_ELF::Load(std::shared_ptr<Kernel::Process>& process) { |
| | if (is_loaded) |
| | return ResultStatus::ErrorAlreadyLoaded; |
| |
|
| | if (!file.IsOpen()) |
| | return ResultStatus::Error; |
| |
|
| | |
| | file.Seek(0, SEEK_SET); |
| |
|
| | std::size_t size = file.GetSize(); |
| | std::unique_ptr<u8[]> buffer(new u8[size]); |
| | if (file.ReadBytes(&buffer[0], size) != size) |
| | return ResultStatus::Error; |
| |
|
| | ElfReader elf_reader(&buffer[0]); |
| | std::shared_ptr<CodeSet> codeset = elf_reader.LoadInto(system, Memory::PROCESS_IMAGE_VADDR); |
| | codeset->name = filename; |
| |
|
| | process = system.Kernel().CreateProcess(std::move(codeset)); |
| | process->Set3dsxKernelCaps(); |
| |
|
| | |
| | process->resource_limit = |
| | system.Kernel().ResourceLimit().GetForCategory(Kernel::ResourceLimitCategory::Application); |
| |
|
| | process->Run(48, Kernel::DEFAULT_STACK_SIZE); |
| |
|
| | is_loaded = true; |
| | return ResultStatus::Success; |
| | } |
| |
|
| | } |
| |
|