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