override self/exe
[ginge.git] / loader / loader.c
index 6a829ff..2d7d3d2 100644 (file)
@@ -1,10 +1,23 @@
-// vim:shiftwidth=2:expandtab
+/*
+ * GINGE - GINGE Is Not Gp2x Emulator
+ * (C) notaz, 2010-2011
+ *
+ * This work is licensed under the MAME license, see COPYING file for details.
+ */
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <elf.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <elf.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
 #include <sys/mman.h>
 
 #include <sys/mman.h>
 
+#include "header.h"
+#include "realfuncs.h"
+
+char *bin_path;
+
 #define CHECK_(val, fail_operator, expect, err_msg) \
   if (val fail_operator expect) { \
     fprintf(stderr, err_msg ", exiting (%d)\n", (int)(long)val); \
 #define CHECK_(val, fail_operator, expect, err_msg) \
   if (val fail_operator expect) { \
     fprintf(stderr, err_msg ", exiting (%d)\n", (int)(long)val); \
@@ -28,8 +41,6 @@
   return 1; \
 }
 
   return 1; \
 }
 
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
-
 typedef struct {
   unsigned long start;
   unsigned long end;
 typedef struct {
   unsigned long start;
   unsigned long end;
@@ -51,20 +62,22 @@ static int is_range_used(maps_range *maps, int map_cnt, unsigned long start, uns
 }
 
 extern char **environ;
 }
 
 extern char **environ;
-extern void do_entry(Elf32_Addr entry, void *stack_frame, int stack_frame_size, void *exitf);
 
 int main(int argc, char *argv[])
 {
 
 int main(int argc, char *argv[])
 {
+  void *lowest_segment = (void *)-1;
   Elf32_Ehdr hdr;
   Elf32_Phdr *phdr;
   FILE *fi;
   maps_range maps[16];
   int map_cnt;
   Elf32_Ehdr hdr;
   Elf32_Phdr *phdr;
   FILE *fi;
   maps_range maps[16];
   int map_cnt;
-  int i, ret;
-  long stack_frame[5];
+  int i, ret, envc, sfp;
+  long *stack_frame;
+  struct stat st;
+  char buf[64];
 
 
-  if (argc != 2) {
-    fprintf(stderr, "usage: %s <program>\n", argv[0]);
+  if (argc < 2) {
+    fprintf(stderr, "usage: %s <program> [args]\n", argv[0]);
     return 1;
   }
 
     return 1;
   }
 
@@ -73,7 +86,7 @@ int main(int argc, char *argv[])
 
   for (i = 0; i < ARRAY_SIZE(maps); i++) {
     ret = fscanf(fi, "%lx-%lx %*s %*s %*s %*s %*s\n", &maps[i].start, &maps[i].end);
 
   for (i = 0; i < ARRAY_SIZE(maps); i++) {
     ret = fscanf(fi, "%lx-%lx %*s %*s %*s %*s %*s\n", &maps[i].start, &maps[i].end);
-    if (ret == 0)
+    if (ret <= 0)
       break;
     CHECK_EQ(ret, 2, "maps parse error");
   }
       break;
     CHECK_EQ(ret, 2, "maps parse error");
   }
@@ -95,7 +108,7 @@ int main(int argc, char *argv[])
   }
 
   HDR_CHECK_EQ(e_type, ET_EXEC, "not executable");
   }
 
   HDR_CHECK_EQ(e_type, ET_EXEC, "not executable");
-//  HDR_CHECK_EQ(e_machine, EM_ARM, "not ARM");
+  HDR_CHECK_EQ(e_machine, EM_ARM, "not ARM");
   HDR_CHECK_EQ(e_phentsize, sizeof(Elf32_Phdr), "bad PH entry size");
   HDR_CHECK_NE(e_phnum, 0, "no PH entries");
 
   HDR_CHECK_EQ(e_phentsize, sizeof(Elf32_Phdr), "bad PH entry size");
   HDR_CHECK_NE(e_phnum, 0, "no PH entries");
 
@@ -124,7 +137,7 @@ int main(int argc, char *argv[])
       return 1;
     }
 
       return 1;
     }
 
-    printf("load %d %08x-%08x from %08x\n", phdr[i].p_type,
+    log("load %d %08x-%08x from %08x\n", phdr[i].p_type,
       phdr[i].p_vaddr, end_addr, phdr[i].p_offset);
 
     align = phdr[i].p_vaddr & 0xfff;
       phdr[i].p_vaddr, end_addr, phdr[i].p_offset);
 
     align = phdr[i].p_vaddr & 0xfff;
@@ -139,18 +152,57 @@ int main(int argc, char *argv[])
         FAIL_PERROR("fseek");
       if (fread((char *)ptr + align, 1, phdr[i].p_filesz, fi) != phdr[i].p_filesz)
         FAIL_PERROR("too small or");
         FAIL_PERROR("fseek");
       if (fread((char *)ptr + align, 1, phdr[i].p_filesz, fi) != phdr[i].p_filesz)
         FAIL_PERROR("too small or");
+
+      if (phdr[i].p_flags & PF_X)
+        do_patches((char *)ptr + align, phdr[i].p_filesz);
     }
     }
+
+    if (map_ptr < lowest_segment)
+      lowest_segment = map_ptr;
   }
 
   }
 
-  stack_frame[0] = 1; // argc
-  stack_frame[1] = (long)argv[1];
-  stack_frame[2] = 0;
-  stack_frame[3] = (long)environ;
-  stack_frame[4] = 0;
+  // build self bin path
+  snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fileno(fi));
+  if (lstat(buf, &st) != 0)
+    FAIL_PERROR("lstat bin_path");
+  bin_path = malloc(st.st_size + 1);
+  CHECK_NE(bin_path, NULL, "bin_path");
+  ret = readlink(buf, bin_path, st.st_size);
+  if (ret < 0)
+    FAIL_PERROR("readlink");
+  bin_path[ret] = 0;
+
+  fclose(fi);
 
 
-  printf("entering %08x\n", hdr.e_entry);
-  do_entry(hdr.e_entry, stack_frame, 5, NULL);
+  emu_init(lowest_segment);
 
 
-  return 0;
+  // generate stack frame: argc, argv[], NULL, env[], NULL
+  for (envc = 0; environ[envc] != NULL; envc++)
+    ;
+
+  stack_frame = calloc(argc + envc + 3, sizeof(stack_frame[0]));
+  if (stack_frame == NULL) {
+    fprintf(stderr, "stack_frame OOM\n");
+    return 1;
+  }
+
+  // update the environment
+  setenv("_", bin_path, 1);
+
+  sfp = 0;
+  stack_frame[sfp++] = argc - 1;
+  for (i = 1; i < argc; i++)
+    stack_frame[sfp++] = (long)argv[i];
+  stack_frame[sfp++] = 0;
+  for (i = 0; i < envc; i++)
+    stack_frame[sfp++] = (long)environ[i];
+  stack_frame[sfp++] = 0;
+
+  log("entering %08x, %d stack entries\n", hdr.e_entry, sfp);
+  do_entry(hdr.e_entry, stack_frame, sfp, NULL);
+
+  fprintf(stderr, "do_entry failed!\n");
+  return 1;
 }
 
 }
 
+// vim:shiftwidth=2:expandtab