/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "berberis/base/bit_util.h" #include "berberis/base/checks.h" #include "berberis/base/macros.h" #include "berberis/guest_state/guest_addr.h" #include "berberis/guest_state/guest_state.h" #include "berberis/runtime/execute_guest.h" #include "berberis/tiny_loader/loaded_elf_file.h" #include "berberis/tiny_loader/tiny_loader.h" namespace berberis { namespace { void Usage(const char* argv_0) { printf( "Usage: %s [-h] [-a start_addr] guest_executable [arg1 [arg2 ...]]\n" " -h - print this message\n" " -a start_addr - start execution at start_addr\n" " guest_executable - path to the guest executable\n", argv_0); } std::tuple ParseGuestAddr(const char* addr_cstr) { char* end_ptr = nullptr; errno = 0; GuestAddr addr = bit_cast(strtoull(addr_cstr, &end_ptr, 16)); // Warning: setting errno on failure is implementation defined. So we also use extra heuristics. if (errno != 0 || (*end_ptr != '\n' && *end_ptr != '\0')) { printf("Cannot convert \"%s\" to integer: %s\n", addr_cstr, errno != 0 ? strerror(errno) : "unexpected end of string"); return {kNullGuestAddr, false}; } return {addr, true}; } struct Options { const char* guest_executable; GuestAddr start_addr; bool print_help_and_exit; }; Options ParseArgs(int argc, char* argv[]) { CHECK_GE(argc, 1); Options opts{}; while (true) { int c = getopt(argc, argv, "ha:"); if (c < 0) { break; } switch (c) { case 'a': { auto [addr, success] = ParseGuestAddr(optarg); if (!success) { return Options{.print_help_and_exit = true}; } opts.start_addr = addr; break; } case 'h': return Options{.print_help_and_exit = true}; default: UNREACHABLE(); } } if (optind >= argc) { return Options{.print_help_and_exit = true}; } opts.guest_executable = argv[optind]; opts.print_help_and_exit = false; return opts; } } // namespace } // namespace berberis int main(int argc, char* argv[]) { berberis::Options opts = berberis::ParseArgs(argc, argv); if (opts.print_help_and_exit) { berberis::Usage(argv[0]); return -1; } LoadedElfFile elf_file; std::string error_msg; if (!TinyLoader::LoadFromFile(opts.guest_executable, &elf_file, &error_msg)) { printf("%s\n", error_msg.c_str()); return -1; } berberis::ThreadState state{}; state.cpu.insn_addr = opts.start_addr; ExecuteGuest(&state, berberis::kNullGuestAddr); return 0; }