fix missing syms in ginge_dyn
[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/types.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
14 #include <sys/mman.h>
15
16 #include "header.h"
17 #include "realfuncs.h"
18
19 char *bin_path;
20 char **g_argv;
21
22 #define CHECK_(val, fail_operator, expect, err_msg) \
23   if (val fail_operator expect) { \
24     fprintf(stderr, err_msg ", exiting (%d)\n", (int)(long)val); \
25     return 1; \
26   }
27
28 #define CHECK_EQ(val, expect, err_msg) \
29   CHECK_(val, !=, expect, err_msg)
30
31 #define CHECK_NE(val, expect, err_msg) \
32   CHECK_(val, ==, expect, err_msg)
33
34 #define HDR_CHECK_EQ(field, expect, err_msg) \
35   CHECK_(hdr.field, !=, expect, err_msg)
36
37 #define HDR_CHECK_NE(field, expect, err_msg) \
38   CHECK_(hdr.field, ==, expect, err_msg)
39
40 #define FAIL_PERROR(msg) { \
41   perror(msg); \
42   return 1; \
43 }
44
45 typedef struct {
46   unsigned long start;
47   unsigned long end;
48 } maps_range;
49
50 static int is_range_used(maps_range *maps, int map_cnt, unsigned long start, unsigned long end)
51 {
52   int i;
53   for (i = 0; i < map_cnt; i++) {
54     if (maps[i].end <= start)
55       continue;
56     if (end <= maps[i].start)
57       continue;
58
59     return i + 1;
60   }
61
62   return 0;
63 }
64
65 extern char **environ;
66
67 int main(int argc, char *argv[])
68 {
69   void *lowest_segment = (void *)-1;
70   Elf32_Ehdr hdr;
71   Elf32_Phdr *phdr;
72   FILE *fi;
73   maps_range maps[16];
74   int map_cnt;
75   int i, ret, envc, sfp;
76   long *stack_frame;
77   struct stat st;
78   char buf[64];
79
80   if (argc < 2) {
81     fprintf(stderr, "usage: %s <program> [args]\n", argv[0]);
82     return 1;
83   }
84
85   g_argv = argv;
86
87   fi = fopen("/proc/self/maps", "r");
88   CHECK_NE(fi, NULL, "fopen maps");
89
90   for (i = 0; i < ARRAY_SIZE(maps); i++) {
91     ret = fscanf(fi, "%lx-%lx %*s %*s %*s %*s %*s\n", &maps[i].start, &maps[i].end);
92     if (ret <= 0)
93       break;
94     CHECK_EQ(ret, 2, "maps parse error");
95   }
96   fclose(fi);
97   map_cnt = i;
98   CHECK_NE(map_cnt, 0, "no maps");
99   CHECK_NE(map_cnt, ARRAY_SIZE(maps), "too many maps");
100
101   fi = fopen(argv[1], "rb");
102   if (fi == NULL)
103     FAIL_PERROR("fopen");
104
105   if (fread(&hdr, 1, sizeof(hdr), fi) != sizeof(hdr))
106     FAIL_PERROR("too small or");
107
108   if (memcmp(hdr.e_ident, ELFMAG "\x01\x01", SELFMAG + 2) != 0) {
109     fprintf(stderr, "not 32bit LE ELF?\n");
110     return 1;
111   }
112
113   HDR_CHECK_EQ(e_type, ET_EXEC, "not executable");
114   HDR_CHECK_EQ(e_machine, EM_ARM, "not ARM");
115   HDR_CHECK_EQ(e_phentsize, sizeof(Elf32_Phdr), "bad PH entry size");
116   HDR_CHECK_NE(e_phnum, 0, "no PH entries");
117
118   phdr = malloc(hdr.e_phnum * hdr.e_phentsize);
119   CHECK_NE(phdr, NULL, "OOM");
120
121   if (fread(phdr, hdr.e_phentsize, hdr.e_phnum, fi) != hdr.e_phnum)
122     FAIL_PERROR("too small or");
123
124   for (i = 0; i < hdr.e_phnum; i++) {
125     Elf32_Addr end_addr = phdr[i].p_vaddr + phdr[i].p_memsz;
126     Elf32_Addr align;
127     void *ptr, *map_ptr;
128
129     if (phdr[i].p_type == PT_NOTE)
130       continue;
131     if (phdr[i].p_type != PT_LOAD) {
132       fprintf(stderr, "skipping section %d\n", phdr[i].p_type);
133       continue;
134     }
135
136     ret = is_range_used(maps, map_cnt, phdr[i].p_vaddr, end_addr);
137     if (ret) {
138       fprintf(stderr, "segment %d (%08x-%08x) hits %08lx-%08lx in maps\n",
139         i, phdr[i].p_vaddr, end_addr, maps[ret - 1].start, maps[ret - 1].end);
140       return 1;
141     }
142
143     log("load %d %08x-%08x from %08x\n", phdr[i].p_type,
144       phdr[i].p_vaddr, end_addr, phdr[i].p_offset);
145
146     align = phdr[i].p_vaddr & 0xfff;
147     map_ptr = (void *)(phdr[i].p_vaddr - align);
148     ptr = mmap(map_ptr, phdr[i].p_memsz + align, PROT_READ|PROT_WRITE|PROT_EXEC,
149       MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
150     if (ptr == MAP_FAILED || ptr != map_ptr)
151       FAIL_PERROR("mmap");
152
153     if (phdr[i].p_filesz > 0) {
154       if (fseek(fi, phdr[i].p_offset, SEEK_SET) != 0)
155         FAIL_PERROR("fseek");
156       if (fread((char *)ptr + align, 1, phdr[i].p_filesz, fi) != phdr[i].p_filesz)
157         FAIL_PERROR("too small or");
158
159       if (phdr[i].p_flags & PF_X)
160         do_patches((char *)ptr + align, phdr[i].p_filesz);
161     }
162
163     if (map_ptr < lowest_segment)
164       lowest_segment = map_ptr;
165   }
166
167   // build self bin path
168   snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fileno(fi));
169   if (lstat(buf, &st) != 0)
170     FAIL_PERROR("lstat bin_path");
171   bin_path = malloc(st.st_size + 1);
172   CHECK_NE(bin_path, NULL, "bin_path");
173   ret = readlink(buf, bin_path, st.st_size);
174   if (ret < 0)
175     FAIL_PERROR("readlink");
176   bin_path[ret] = 0;
177
178   fclose(fi);
179
180   emu_init(lowest_segment);
181
182   // generate stack frame: argc, argv[], NULL, env[], NULL
183   for (envc = 0; environ[envc] != NULL; envc++)
184     ;
185
186   stack_frame = calloc(argc + envc + 3, sizeof(stack_frame[0]));
187   if (stack_frame == NULL) {
188     fprintf(stderr, "stack_frame OOM\n");
189     return 1;
190   }
191
192   // update the environment
193   setenv("_", bin_path, 1);
194
195   sfp = 0;
196   stack_frame[sfp++] = argc - 1;
197   for (i = 1; i < argc; i++)
198     stack_frame[sfp++] = (long)argv[i];
199   stack_frame[sfp++] = 0;
200   for (i = 0; i < envc; i++)
201     stack_frame[sfp++] = (long)environ[i];
202   stack_frame[sfp++] = 0;
203
204   log("entering %08x, %d stack entries\n", hdr.e_entry, sfp);
205   do_entry(hdr.e_entry, stack_frame, sfp, NULL);
206
207   fprintf(stderr, "do_entry failed!\n");
208   return 1;
209 }
210
211 // vim:shiftwidth=2:expandtab