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