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