summaryrefslogtreecommitdiff
path: root/src/libexec/pef.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libexec/pef.c')
-rw-r--r--src/libexec/pef.c307
1 files changed, 307 insertions, 0 deletions
diff --git a/src/libexec/pef.c b/src/libexec/pef.c
new file mode 100644
index 0000000..2c58014
--- /dev/null
+++ b/src/libexec/pef.c
@@ -0,0 +1,307 @@
+/*
+ * <pef.c>
+ *
+ * Open Hack'Ware BIOS Classic MacOS executable file loader
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License V2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "bios.h"
+#include "exec.h"
+
+/* PEF (old MacOS executable format) */
+typedef struct PEF_container_t PEF_container_t;
+struct PEF_container_t {
+ uint32_t tag1;
+ uint32_t tag2;
+ uint32_t arch;
+ uint32_t version;
+ uint32_t timestamp;
+ uint32_t oldDefVersion;
+ uint32_t oldImpVersion;
+ uint32_t currentVersion;
+ uint16_t nb_sections;
+ uint16_t nb_inst_sections;
+ uint32_t pad;
+} __attribute__ (( packed ));
+
+typedef struct PEF_section_t PEF_section_t;
+struct PEF_section_t {
+ int32_t name_offset;
+ uint32_t address;
+ uint32_t total_size;
+ uint32_t unpacked_size;
+ uint32_t packed_size;
+ uint32_t container_offset;
+ uint8_t section_kind;
+ uint8_t share_kind;
+ uint8_t align;
+ uint8_t pad;
+} __attribute__ (( packed ));
+
+typedef struct PEF_loader_t PEF_loader_t;
+struct PEF_loader_t {
+ int32_t main_section;
+ uint32_t main_offset;
+ int32_t init_section;
+ uint32_t init_offset;
+ int32_t term_section;
+ uint32_t term_offset;
+ uint32_t nb_import_libs;
+ uint32_t nb_import_symbols;
+ uint32_t nb_reloc_sections;
+ uint32_t reloc_instr_offset;
+ uint32_t loader_strings_offset;
+ uint32_t export_hash_offset;
+ uint32_t export_hashtable_power;
+ uint32_t nb_export_symbols;
+} __attribute__ (( packed ));
+
+enum {
+ PEF_SECTION_CODE = 0,
+ PEF_SECTION_UNPDATA = 1,
+ PEF_SECTION_INIDATA = 2,
+ PEF_SECTION_CONSTANT = 3,
+ PEF_SECTION_LOADER = 4,
+ PEF_SECTION_DEBUG = 5,
+ PEF_SECTION_EXEC = 6,
+ PEF_SECTION_EXCP = 7,
+ PEF_SECTION_TRACE = 8,
+};
+
+enum {
+ PEF_SHARE_PROCESS = 1,
+ PEF_SHARE_GLOBAL = 4,
+ PEF_SHARE_PROTECTED = 5,
+};
+
+int exec_load_pef (inode_t *file, void **dest, void **entry, void **end,
+ uint32_t loffset)
+{
+ PEF_container_t container;
+ PEF_section_t section;
+ PEF_loader_t loader;
+ void *first, *last, *addr, **sections;
+ uint32_t pos, padsize, size, lpos, main_offset;
+ uint8_t opcode;
+ int nb_sections, nb_inst_sections, main_section, i, n;
+
+ file_seek(file, loffset);
+ if (fs_read(file, &container, sizeof(PEF_container_t)) < 0) {
+ ERROR("Cannot load container header\n");
+ return -1;
+ }
+ pos = sizeof(PEF_container_t);
+ /* Check tags and architecture */
+ if (memcmp(&container.tag1, "Joy!", 4) != 0) {
+ DPRINTF("No joy, no PEF\n");
+ return -2;
+ }
+ if (memcmp(&container.tag2, "peff", 4) != 0) {
+ DPRINTF("No PEFF file\n");
+ return -2;
+ }
+ if (memcmp(&container.arch, "pwpc", 4) != 0) {
+ DPRINTF("PEFF file not for PPC\n");
+ return -2;
+ }
+ if (get_be32(&container.version) != 1) {
+ DPRINTF("Unknown PEFF container version\n");
+ return -2;
+ }
+ nb_sections = get_be32(&container.nb_sections);
+ sections = malloc(nb_sections * sizeof(void *));
+ if (sections == NULL) {
+ ERROR("Cannot allocate sections\n");
+ return -1;
+ }
+ nb_inst_sections = get_be32(&container.nb_inst_sections);
+ first = (void *)0xFFFFFFFF;
+ last = NULL;
+ main_section = -1;
+ main_offset = 0;
+ for (i = 0, n = 0; i < nb_sections; i++) {
+ file_seek(file, loffset + pos);
+ if (fs_read(file, &section, sizeof(PEF_section_t)) < 0) {
+ ERROR("Cannot read section %d\n", i);
+ return -1;
+ }
+ pos += sizeof(PEF_section_t);
+ addr = (void *)get_be32(&section.address);
+ sections[i] = addr;
+ if (addr < first)
+ first = addr;
+ size = get_be32(&section.total_size);
+ lpos = get_be32(&section.container_offset);
+ file_seek(file, loffset + lpos);
+ switch (section.section_kind) {
+ case PEF_SECTION_CODE:
+ case PEF_SECTION_UNPDATA:
+ /* Load as raw data */
+ padsize = get_be32(&section.unpacked_size) - size;
+ file_seek(file, loffset + lpos);
+ if (fs_read(file, addr, size) < 0) {
+ ERROR("Cannot load section %d\n", i);
+ return -1;
+ }
+ addr = (char *)addr + size;
+ memset(addr, 0, padsize);
+ addr = (char *)addr + padsize;
+ break;
+ case PEF_SECTION_INIDATA:
+ case PEF_SECTION_CONSTANT:
+ case PEF_SECTION_EXEC:
+ /* Load as compressed data */
+ for (;;) {
+ void *ref;
+ uint32_t total;
+ uint8_t bsize, csize, count, j;
+
+ if (fs_read(file, &opcode, 1) < 0) {
+ ERROR("Cannot get opcode\n");
+ return -1;
+ }
+ bsize = opcode & 0x1F;
+ switch (opcode >> 5) {
+ case 0x0:
+ /* Initialize size bytes to zero */
+ memset(addr, 0, bsize);
+ addr = (char *)addr + bsize;
+ total = bsize;
+ break;
+ case 0x1:
+ /* Copy bloc */
+ if (fs_read(file, addr, bsize) < 0) {
+ ERROR("Cannot copy bloc\n");
+ return -1;
+ }
+ addr = (char *)addr + bsize;
+ total = bsize;
+ break;
+ case 0x2:
+ /* Repeat bloc */
+ if (fs_read(file, &count, 1) < 0) {
+ ERROR("Cannot read bloc size\n");
+ return -1;
+ }
+ total = 0;
+ if (count == 0) {
+ break;
+ }
+ if (fs_read(file, addr, bsize) < 0) {
+ ERROR("Cannot read repeat bloc\n");
+ return -1;
+ }
+ ref = addr;
+ addr = (char *)addr + bsize;
+ for (j = 1; j < count; j++) {
+ memcpy(addr, ref, bsize);
+ total += bsize;
+ addr = (char *)addr + bsize;
+ }
+ break;
+ case 0x3:
+ /* Interleave repeat bloc with bloc copy */
+ if (fs_read(file, &csize, 1) < 0 ||
+ fs_read(file, &count, 1) < 0) {
+ ERROR("Cannot read repeat params\n");
+ return -1;
+ }
+ ref = addr;
+ if (fs_read(file, addr, bsize) < 0) {
+ ERROR("Cannot read common data\n");
+ return -1;
+ }
+ addr = (char *)addr + bsize;
+ total = bsize;
+ for (j = 0; j < count; j++) {
+ if (fs_read(file, addr, csize) < 0) {
+ ERROR("Cannot read custom data\n");
+ return -1;
+ }
+ addr = (char *)addr + csize;
+ memcpy(addr, ref, bsize);
+ addr = (char *)addr + bsize;
+ total += csize + bsize;
+ }
+ break;
+ case 0x4:
+ /* Interleave repeat bloc with zero */
+ if (fs_read(file, &csize, 1) < 0 ||
+ fs_read(file, &count, 1) < 0) {
+ ERROR("Cannot read repeat params\n");
+ return -1;
+ }
+ total = 0;
+ for (j = 0; j < count; j++) {
+ memset(addr, 0, bsize);
+ addr = (char *)addr + bsize;
+ if (fs_read(file, addr, csize) < 0) {
+ ERROR("Cannot read repeat data\n");
+ return -1;
+ }
+ addr = (char *)addr + csize;
+ total += csize + bsize;
+ }
+ memset(addr, 0, bsize);
+ addr = (char *)addr + bsize;
+ total += bsize;
+ break;
+ default:
+ ERROR("Unknown opcode\n");
+ return -1;
+ }
+ if (addr > last)
+ last = addr;
+ if (total >= size)
+ break;
+ size -= total;
+ }
+ break;
+ case PEF_SECTION_LOADER:
+ /* find entry point */
+ if (fs_read(file, &loader, sizeof(PEF_loader_t)) < 0) {
+ ERROR("Cannot read loader header\n");
+ return -1;
+ }
+ main_section = get_be32(&loader.main_section);
+ main_offset = get_be32(&loader.main_offset);
+ if (main_section >= nb_sections) {
+ ERROR("Invalid main section\n");
+ return -1;
+ }
+ break;
+ case PEF_SECTION_DEBUG:
+ case PEF_SECTION_EXCP:
+ case PEF_SECTION_TRACE:
+ break;
+ default:
+ return -2;
+ }
+ }
+ *dest = first;
+ *end = last;
+ if (main_section == -1) {
+ *entry = first;
+ } else {
+ *entry = (char *)sections[main_section] + main_offset;
+ }
+ free(sections);
+
+ return 0;
+}