499bf01c |
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 | */ |
55fe8e70 |
7 | #include <stdio.h> |
8 | #include <stdlib.h> |
9 | #include <string.h> |
10 | #include <elf.h> |
c3831532 |
11 | #include <sys/types.h> |
12 | #include <sys/stat.h> |
13 | #include <unistd.h> |
55fe8e70 |
14 | #include <sys/mman.h> |
15 | |
11913091 |
16 | #include "header.h" |
7fd42181 |
17 | #include "realfuncs.h" |
df608af1 |
18 | #include "syscalls.h" |
11913091 |
19 | |
c3831532 |
20 | char *bin_path; |
78684356 |
21 | char **g_argv; |
c3831532 |
22 | |
55fe8e70 |
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 | |
55fe8e70 |
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; |
55fe8e70 |
67 | |
68 | int main(int argc, char *argv[]) |
69 | { |
890c1dc5 |
70 | void *lowest_segments[2] = { NULL, NULL }; |
55fe8e70 |
71 | Elf32_Ehdr hdr; |
72 | Elf32_Phdr *phdr; |
73 | FILE *fi; |
74 | maps_range maps[16]; |
75 | int map_cnt; |
306e06f7 |
76 | int i, ret, envc, sfp; |
77 | long *stack_frame; |
c3831532 |
78 | struct stat st; |
79 | char buf[64]; |
df608af1 |
80 | long lret; |
55fe8e70 |
81 | |
306e06f7 |
82 | if (argc < 2) { |
83 | fprintf(stderr, "usage: %s <program> [args]\n", argv[0]); |
55fe8e70 |
84 | return 1; |
85 | } |
86 | |
78684356 |
87 | g_argv = argv; |
88 | |
df608af1 |
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 | |
55fe8e70 |
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); |
7fd42181 |
100 | if (ret <= 0) |
55fe8e70 |
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"); |
306e06f7 |
122 | HDR_CHECK_EQ(e_machine, EM_ARM, "not ARM"); |
55fe8e70 |
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 | |
4d045184 |
151 | log("load %d %08x-%08x from %08x\n", phdr[i].p_type, |
55fe8e70 |
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"); |
11913091 |
166 | |
167 | if (phdr[i].p_flags & PF_X) |
168 | do_patches((char *)ptr + align, phdr[i].p_filesz); |
55fe8e70 |
169 | } |
11913091 |
170 | |
890c1dc5 |
171 | if (lowest_segments[0] == NULL || map_ptr < lowest_segments[0]) |
172 | lowest_segments[0] = map_ptr; |
55fe8e70 |
173 | } |
174 | |
c3831532 |
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 | |
890c1dc5 |
188 | emu_init(lowest_segments, 0); |
11913091 |
189 | |
306e06f7 |
190 | // generate stack frame: argc, argv[], NULL, env[], NULL |
191 | for (envc = 0; environ[envc] != NULL; envc++) |
192 | ; |
55fe8e70 |
193 | |
306e06f7 |
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 | } |
55fe8e70 |
199 | |
c3831532 |
200 | // update the environment |
201 | setenv("_", bin_path, 1); |
202 | |
306e06f7 |
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 | |
4d045184 |
212 | log("entering %08x, %d stack entries\n", hdr.e_entry, sfp); |
306e06f7 |
213 | do_entry(hdr.e_entry, stack_frame, sfp, NULL); |
214 | |
215 | fprintf(stderr, "do_entry failed!\n"); |
216 | return 1; |
55fe8e70 |
217 | } |
218 | |
499bf01c |
219 | // vim:shiftwidth=2:expandtab |