1 /*
2  * Copyright (C) 2023 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 <malloc.h>
18 #include <sys/auxv.h>
19 #include <unistd.h>
20 
21 #include <cstdio>
22 #include <cstring>
23 #include <string>
24 
25 #include "berberis/base/checks.h"
26 
27 #if defined(__i386__) || defined(__x86_64__)
28 #include "berberis/program_runner/program_runner.h"
29 #elif defined(__aarch64__)
30 #include "berberis/guest_state/guest_state.h"
31 #include "berberis/interpreter/riscv64/interpreter.h"
32 #include "berberis/tiny_loader/loaded_elf_file.h"
33 #include "berberis/tiny_loader/tiny_loader.h"
34 #endif
35 
36 // Program runner meant for testing and manual invocation.
37 
38 namespace berberis {
39 
40 namespace {
41 
Usage(const char * argv_0)42 void Usage(const char* argv_0) {
43   printf(
44       "Usage: %s [-h|?] [-l loader] [-s vdso] guest_executable [arg1 [arg2 ...]]\n"
45       "  -h, -?           - print this message\n"
46       "  -l loader        - path to guest loader\n"
47       "  -s vdso          - path to guest vdso\n"
48       "  guest_executable - path to the guest executable\n",
49       argv_0);
50 }
51 
52 struct Options {
53   const char* guest_executable;
54   const char* loader_path;
55   const char* vdso_path;
56   bool print_help_and_exit;
57 };
58 
ParseArgs(int argc,char * argv[])59 Options ParseArgs(int argc, char* argv[]) {
60   CHECK_GE(argc, 1);
61   static const Options kOptsError{.print_help_and_exit = true};
62   Options opts{
63       .guest_executable = nullptr,
64       .loader_path = nullptr,
65       .vdso_path = nullptr,
66       .print_help_and_exit = false,
67   };
68 
69   int curr_arg = 1;
70   for (int curr_arg = 1; curr_arg < argc; ++curr_arg) {
71     if (argv[curr_arg][0] != '-') {
72       break;
73     }
74     const char option = argv[curr_arg][1];
75     switch (option) {
76       case 's':
77       case 'l':
78         if (++curr_arg == argc) {
79           return kOptsError;
80         }
81         if (option == 's') {
82           opts.vdso_path = argv[curr_arg];
83         } else {
84           opts.loader_path = argv[curr_arg];
85         }
86         break;
87       case 'h':
88       case '?':
89       default:
90         return kOptsError;
91     }
92   }
93 
94   if (curr_arg >= argc) {
95     return kOptsError;
96   }
97 
98   return opts;
99 }
100 
101 }  // namespace
102 
103 }  // namespace berberis
104 
main(int argc,char * argv[],char * envp[])105 int main(int argc, char* argv[], [[maybe_unused]] char* envp[]) {
106 #if defined(__GLIBC__)
107   // Disable brk in glibc-malloc.
108   //
109   // By default GLIBC uses brk in malloc which may lead to conflicts with
110   // executables that use brk for their own needs. See http://b/64720148 for
111   // example.
112   mallopt(M_MMAP_THRESHOLD, 0);
113   mallopt(M_TRIM_THRESHOLD, -1);
114 #endif
115 
116   berberis::Options opts = berberis::ParseArgs(argc, argv);
117 
118   if (opts.print_help_and_exit) {
119     berberis::Usage(argv[0]);
120     return -1;
121   }
122 
123   std::string error_msg;
124 #if defined(__i386__) || defined(__x86_64__) || defined(__riscv)
125   if (!berberis::Run(
126           // TODO(b/276787135): Make vdso and loader configurable via command line arguments.
127           /* vdso_path */ nullptr,
128           /* loader_path */ nullptr,
129           argc - optind,
130           const_cast<const char**>(argv + optind),
131           envp,
132           &error_msg)) {
133     fprintf(stderr, "unable to start executable: %s\n", error_msg.c_str());
134     return -1;
135   }
136 #elif defined(__aarch64__)
137   LoadedElfFile elf_file;
138   if (!TinyLoader::LoadFromFile(argv[optind], &elf_file, &error_msg)) {
139     fprintf(stderr, "unable to start load file: %s\n", error_msg.c_str());
140     return -1;
141   }
142   if (elf_file.e_type() != ET_EXEC) {
143     fprintf(stderr, "this is not a static executable file: %hu\n", elf_file.e_type());
144     return -1;
145   }
146 
147   berberis::ThreadState state{};
148   state.cpu.insn_addr = berberis::ToGuestAddr(elf_file.entry_point());
149   while (true) {
150     InterpretInsn(&state);
151   }
152 #else
153 #error Unsupported platform
154 #endif
155 
156   return 0;
157 }
158