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