1/*
2 * Copyright 2011, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "ELFObject.h"
18
19#include "utils/serialize.h"
20#include "ELF.h"
21
22#include <llvm/ADT/OwningPtr.h>
23
24#include <fcntl.h>
25#include <stdlib.h>
26#include <sys/mman.h>
27#include <sys/stat.h>
28#include <sys/types.h>
29#include <map>
30#include <stdio.h>
31#include <stdarg.h>
32
33using namespace std;
34
35bool open_mmap_file(char const *filename,
36                    int &fd,
37                    unsigned char const *&image,
38                    size_t &size);
39
40void close_mmap_file(int fd,
41                     unsigned char const *image,
42                     size_t size);
43
44void dump_and_run_file(unsigned char const *image, size_t size,
45                       int argc, char **argv);
46
47int main(int argc, char **argv) {
48  // Check arguments
49  if (argc < 2) {
50    llvm::errs() << "USAGE: " << argv[0] << " [ELFObjectFile] [ARGS]\n";
51    exit(EXIT_FAILURE);
52  }
53
54  // Filename from argument
55  char const *filename = argv[1];
56
57  // Open the file
58  int fd = -1;
59  unsigned char const *image = NULL;
60  size_t image_size = 0;
61
62  if (!open_mmap_file(filename, fd, image, image_size)) {
63    exit(EXIT_FAILURE);
64  }
65
66  // Dump and run the file
67  dump_and_run_file(image, image_size, argc - 1, argv + 1);
68
69  // Close the file
70  close_mmap_file(fd, image, image_size);
71
72  return EXIT_SUCCESS;
73}
74
75// FIXME: I don't like these stub as well.  However, before we implement
76// x86 64bit far jump stub, we have to ensure find_sym only returns
77// near address.
78
79int stub_printf(char const *fmt, ...) {
80  va_list ap;
81  va_start(ap, fmt);
82  int result = vprintf(fmt, ap);
83  va_end(ap);
84  return result;
85}
86
87int stub_scanf(char const *fmt, ...) {
88  va_list ap;
89  va_start(ap, fmt);
90  int result = vscanf(fmt, ap);
91  va_end(ap);
92  return result;
93}
94
95void stub_srand(unsigned int seed) {
96  srand(seed);
97}
98
99int stub_rand() {
100  return rand();
101}
102
103time_t stub_time(time_t *output) {
104  return time(output);
105}
106
107void *find_sym(void *context, char const *name) {
108  struct func_entry_t {
109    char const *name;
110    size_t name_len;
111    void *addr;
112  };
113
114  static func_entry_t const tab[] = {
115#define DEF(NAME, ADDR) \
116    { NAME, sizeof(NAME) - 1, (void *)(ADDR) },
117
118    DEF("printf", stub_printf)
119    DEF("scanf", stub_scanf)
120    DEF("__isoc99_scanf", stub_scanf)
121    DEF("rand", stub_rand)
122    DEF("time", stub_time)
123    DEF("srand", stub_srand)
124#undef DEF
125  };
126
127  static size_t const tab_size = sizeof(tab) / sizeof(func_entry_t);
128
129  // Note: Since our table is small, we are using trivial O(n) searching
130  // function.  For bigger table, it will be better to use binary
131  // search or hash function.
132  size_t name_len = strlen(name);
133  for (size_t i = 0; i < tab_size; ++i) {
134    if (name_len == tab[i].name_len && strcmp(name, tab[i].name) == 0) {
135      return tab[i].addr;
136    }
137  }
138
139  assert(0 && "Can't find symbol.");
140  return 0;
141}
142
143template <unsigned Bitwidth, typename Archiver>
144void dump_and_run_object(Archiver &AR, int argc, char **argv) {
145  llvm::OwningPtr<ELFObject<Bitwidth> > object(ELFObject<Bitwidth>::read(AR));
146
147  if (!object) {
148    llvm::errs() << "ERROR: Unable to load object\n";
149  }
150
151  object->print();
152  out().flush();
153
154  ELFSectionSymTab<Bitwidth> *symtab =
155    static_cast<ELFSectionSymTab<Bitwidth> *>(
156        object->getSectionByName(".symtab"));
157
158  object->relocate(find_sym, 0);
159  out() << "relocate finished!\n";
160  out().flush();
161
162  int machine = object->getHeader()->getMachine();
163
164  void *main_addr = symtab->getByName("main")->getAddress(machine);
165  out() << "main address: " << main_addr << "\n";
166  out().flush();
167
168  ((int (*)(int, char **))main_addr)(argc, argv);
169  fflush(stdout);
170}
171
172template <typename Archiver>
173void dump_and_run_file_from_archive(bool is32bit, Archiver &AR,
174                                    int argc, char **argv) {
175  if (is32bit) {
176    dump_and_run_object<32>(AR, argc, argv);
177  } else {
178    dump_and_run_object<64>(AR, argc, argv);
179  }
180}
181
182void dump_and_run_file(unsigned char const *image, size_t size,
183                       int argc, char **argv) {
184  if (size < EI_NIDENT) {
185    llvm::errs() << "ERROR: ELF identification corrupted.\n";
186    return;
187  }
188
189  if (image[EI_DATA] != ELFDATA2LSB && image[EI_DATA] != ELFDATA2MSB) {
190    llvm::errs() << "ERROR: Unknown endianness.\n";
191    return;
192  }
193
194  if (image[EI_CLASS] != ELFCLASS32 && image[EI_CLASS] != ELFCLASS64) {
195    llvm::errs() << "ERROR: Unknown machine class.\n";
196    return;
197  }
198
199  bool isLittleEndian = (image[EI_DATA] == ELFDATA2LSB);
200  bool is32bit = (image[EI_CLASS] == ELFCLASS32);
201
202  if (isLittleEndian) {
203    ArchiveReaderLE AR(image, size);
204    dump_and_run_file_from_archive(is32bit, AR, argc, argv);
205  } else {
206    ArchiveReaderBE AR(image, size);
207    dump_and_run_file_from_archive(is32bit, AR, argc, argv);
208  }
209}
210
211bool open_mmap_file(char const *filename,
212                    int &fd,
213                    unsigned char const *&image,
214                    size_t &size) {
215  // Query the file status
216  struct stat sb;
217  if (stat(filename, &sb) != 0) {
218    llvm::errs() << "ERROR: " << filename << " not found.\n";
219    return false;
220  }
221
222  if (!S_ISREG(sb.st_mode)) {
223    llvm::errs() << "ERROR: " << filename << " is not a regular file.\n";
224    return false;
225  }
226
227  size = (size_t)sb.st_size;
228
229  // Open the file in readonly mode
230  fd = open(filename, O_RDONLY);
231  if (fd < 0) {
232    llvm::errs() << "ERROR: Unable to open " << filename << "\n";
233    return false;
234  }
235
236  // Map the file image
237  image = static_cast<unsigned char const *>(
238    mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0));
239
240  if (image == MAP_FAILED) {
241    llvm::errs() << "ERROR: Unable to map " << filename << " to memory.\n";
242    close(fd);
243    return false;
244  }
245
246  return true;
247}
248
249void close_mmap_file(int fd,
250                     unsigned char const *image,
251                     size_t size) {
252  if (image) {
253    munmap((void *)image, size);
254  }
255
256  if (fd >= 0) {
257    close(fd);
258  }
259}
260