| 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 | |
| 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 | |
| 44 | typedef struct { |
| 45 | unsigned long start; |
| 46 | unsigned long end; |
| 47 | } maps_range; |
| 48 | |
| 49 | static 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 | |
| 64 | extern char **environ; |
| 65 | |
| 66 | int 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 |