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