a few more patches and a little rework
[ginge.git] / loader / loader.c
1 /*
2  * GINGE - GINGE Is Not Gp2x Emulator
3  * (C) notaz, 2010-2011
4  *
5  * This work is licensed under the MAME license, see COPYING file for details.
6  */
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <elf.h>
11 #include <sys/mman.h>
12
13 #include "header.h"
14 #include "realfuncs.h"
15
16 #define CHECK_(val, fail_operator, expect, err_msg) \
17   if (val fail_operator expect) { \
18     fprintf(stderr, err_msg ", exiting (%d)\n", (int)(long)val); \
19     return 1; \
20   }
21
22 #define CHECK_EQ(val, expect, err_msg) \
23   CHECK_(val, !=, expect, err_msg)
24
25 #define CHECK_NE(val, expect, err_msg) \
26   CHECK_(val, ==, expect, err_msg)
27
28 #define HDR_CHECK_EQ(field, expect, err_msg) \
29   CHECK_(hdr.field, !=, expect, err_msg)
30
31 #define HDR_CHECK_NE(field, expect, err_msg) \
32   CHECK_(hdr.field, ==, expect, err_msg)
33
34 #define FAIL_PERROR(msg) { \
35   perror(msg); \
36   return 1; \
37 }
38
39 typedef struct {
40   unsigned long start;
41   unsigned long end;
42 } maps_range;
43
44 static int is_range_used(maps_range *maps, int map_cnt, unsigned long start, unsigned long end)
45 {
46   int i;
47   for (i = 0; i < map_cnt; i++) {
48     if (maps[i].end <= start)
49       continue;
50     if (end <= maps[i].start)
51       continue;
52
53     return i + 1;
54   }
55
56   return 0;
57 }
58
59 extern char **environ;
60
61 int main(int argc, char *argv[])
62 {
63   void *lowest_segment = (void *)-1;
64   Elf32_Ehdr hdr;
65   Elf32_Phdr *phdr;
66   FILE *fi;
67   maps_range maps[16];
68   int map_cnt;
69   int i, ret, envc, sfp;
70   long *stack_frame;
71
72   if (argc < 2) {
73     fprintf(stderr, "usage: %s <program> [args]\n", argv[0]);
74     return 1;
75   }
76
77   fi = fopen("/proc/self/maps", "r");
78   CHECK_NE(fi, NULL, "fopen maps");
79
80   for (i = 0; i < ARRAY_SIZE(maps); i++) {
81     ret = fscanf(fi, "%lx-%lx %*s %*s %*s %*s %*s\n", &maps[i].start, &maps[i].end);
82     if (ret <= 0)
83       break;
84     CHECK_EQ(ret, 2, "maps parse error");
85   }
86   fclose(fi);
87   map_cnt = i;
88   CHECK_NE(map_cnt, 0, "no maps");
89   CHECK_NE(map_cnt, ARRAY_SIZE(maps), "too many maps");
90
91   fi = fopen(argv[1], "rb");
92   if (fi == NULL)
93     FAIL_PERROR("fopen");
94
95   if (fread(&hdr, 1, sizeof(hdr), fi) != sizeof(hdr))
96     FAIL_PERROR("too small or");
97
98   if (memcmp(hdr.e_ident, ELFMAG "\x01\x01", SELFMAG + 2) != 0) {
99     fprintf(stderr, "not 32bit LE ELF?\n");
100     return 1;
101   }
102
103   HDR_CHECK_EQ(e_type, ET_EXEC, "not executable");
104   HDR_CHECK_EQ(e_machine, EM_ARM, "not ARM");
105   HDR_CHECK_EQ(e_phentsize, sizeof(Elf32_Phdr), "bad PH entry size");
106   HDR_CHECK_NE(e_phnum, 0, "no PH entries");
107
108   phdr = malloc(hdr.e_phnum * hdr.e_phentsize);
109   CHECK_NE(phdr, NULL, "OOM");
110
111   if (fread(phdr, hdr.e_phentsize, hdr.e_phnum, fi) != hdr.e_phnum)
112     FAIL_PERROR("too small or");
113
114   for (i = 0; i < hdr.e_phnum; i++) {
115     Elf32_Addr end_addr = phdr[i].p_vaddr + phdr[i].p_memsz;
116     Elf32_Addr align;
117     void *ptr, *map_ptr;
118
119     if (phdr[i].p_type == PT_NOTE)
120       continue;
121     if (phdr[i].p_type != PT_LOAD) {
122       fprintf(stderr, "skipping section %d\n", phdr[i].p_type);
123       continue;
124     }
125
126     ret = is_range_used(maps, map_cnt, phdr[i].p_vaddr, end_addr);
127     if (ret) {
128       fprintf(stderr, "segment %d (%08x-%08x) hits %08lx-%08lx in maps\n",
129         i, phdr[i].p_vaddr, end_addr, maps[ret - 1].start, maps[ret - 1].end);
130       return 1;
131     }
132
133     log("load %d %08x-%08x from %08x\n", phdr[i].p_type,
134       phdr[i].p_vaddr, end_addr, phdr[i].p_offset);
135
136     align = phdr[i].p_vaddr & 0xfff;
137     map_ptr = (void *)(phdr[i].p_vaddr - align);
138     ptr = mmap(map_ptr, phdr[i].p_memsz + align, PROT_READ|PROT_WRITE|PROT_EXEC,
139       MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
140     if (ptr == MAP_FAILED || ptr != map_ptr)
141       FAIL_PERROR("mmap");
142
143     if (phdr[i].p_filesz > 0) {
144       if (fseek(fi, phdr[i].p_offset, SEEK_SET) != 0)
145         FAIL_PERROR("fseek");
146       if (fread((char *)ptr + align, 1, phdr[i].p_filesz, fi) != phdr[i].p_filesz)
147         FAIL_PERROR("too small or");
148
149       if (phdr[i].p_flags & PF_X)
150         do_patches((char *)ptr + align, phdr[i].p_filesz);
151     }
152
153     if (map_ptr < lowest_segment)
154       lowest_segment = map_ptr;
155   }
156
157   emu_init(lowest_segment);
158
159   // generate stack frame: argc, argv[], NULL, env[], NULL
160   for (envc = 0; environ[envc] != NULL; envc++)
161     ;
162
163   stack_frame = calloc(argc + envc + 3, sizeof(stack_frame[0]));
164   if (stack_frame == NULL) {
165     fprintf(stderr, "stack_frame OOM\n");
166     return 1;
167   }
168
169   sfp = 0;
170   stack_frame[sfp++] = argc - 1;
171   for (i = 1; i < argc; i++)
172     stack_frame[sfp++] = (long)argv[i];
173   stack_frame[sfp++] = 0;
174   for (i = 0; i < envc; i++)
175     stack_frame[sfp++] = (long)environ[i];
176   stack_frame[sfp++] = 0;
177
178   log("entering %08x, %d stack entries\n", hdr.e_entry, sfp);
179   do_entry(hdr.e_entry, stack_frame, sfp, NULL);
180
181   fprintf(stderr, "do_entry failed!\n");
182   return 1;
183 }
184
185 // vim:shiftwidth=2:expandtab