Merge pull request #1419 from DarkLordZach/homebrew-args
nso/nro: Add support for passing command-line arguments to executable
This commit is contained in:
		| @@ -3,6 +3,7 @@ | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <cinttypes> | ||||
| #include <cstring> | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/file_util.h" | ||||
| #include "common/logging/log.h" | ||||
| @@ -140,7 +141,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) | ||||
|         const FileSys::VirtualFile module_file = dir->GetFile(module); | ||||
|         if (module_file != nullptr) { | ||||
|             const VAddr load_addr = next_load_addr; | ||||
|             next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr, pm); | ||||
|             next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr, | ||||
|                                                        std::strcmp(module, "rtld") == 0, pm); | ||||
|             LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); | ||||
|             // Register module with GDBStub | ||||
|             GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false); | ||||
|   | ||||
| @@ -18,7 +18,9 @@ | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| #include "core/loader/nro.h" | ||||
| #include "core/loader/nso.h" | ||||
| #include "core/memory.h" | ||||
| #include "core/settings.h" | ||||
|  | ||||
| namespace Loader { | ||||
|  | ||||
| @@ -150,6 +152,19 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { | ||||
|         codeset->segments[i].size = PageAlignSize(nro_header.segments[i].size); | ||||
|     } | ||||
|  | ||||
|     if (!Settings::values.program_args.empty()) { | ||||
|         const auto arg_data = Settings::values.program_args; | ||||
|         codeset->DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; | ||||
|         NSOArgumentHeader args_header{ | ||||
|             NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}}; | ||||
|         const auto end_offset = program_image.size(); | ||||
|         program_image.resize(static_cast<u32>(program_image.size()) + | ||||
|                              NSO_ARGUMENT_DATA_ALLOCATION_SIZE); | ||||
|         std::memcpy(program_image.data() + end_offset, &args_header, sizeof(NSOArgumentHeader)); | ||||
|         std::memcpy(program_image.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(), | ||||
|                     arg_data.size()); | ||||
|     } | ||||
|  | ||||
|     // Read MOD header | ||||
|     ModHeader mod_header{}; | ||||
|     // Default .bss to NRO header bss size if MOD0 section doesn't exist | ||||
|   | ||||
| @@ -17,6 +17,7 @@ | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| #include "core/loader/nso.h" | ||||
| #include "core/memory.h" | ||||
| #include "core/settings.h" | ||||
|  | ||||
| namespace Loader { | ||||
|  | ||||
| @@ -94,6 +95,7 @@ static constexpr u32 PageAlignSize(u32 size) { | ||||
| } | ||||
|  | ||||
| VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, | ||||
|                                 bool should_pass_arguments, | ||||
|                                 boost::optional<FileSys::PatchManager> pm) { | ||||
|     if (file == nullptr) | ||||
|         return {}; | ||||
| @@ -125,6 +127,19 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, | ||||
|         codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size())); | ||||
|     } | ||||
|  | ||||
|     if (should_pass_arguments && !Settings::values.program_args.empty()) { | ||||
|         const auto arg_data = Settings::values.program_args; | ||||
|         codeset->DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; | ||||
|         NSOArgumentHeader args_header{ | ||||
|             NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}}; | ||||
|         const auto end_offset = program_image.size(); | ||||
|         program_image.resize(static_cast<u32>(program_image.size()) + | ||||
|                              NSO_ARGUMENT_DATA_ALLOCATION_SIZE); | ||||
|         std::memcpy(program_image.data() + end_offset, &args_header, sizeof(NSOArgumentHeader)); | ||||
|         std::memcpy(program_image.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(), | ||||
|                     arg_data.size()); | ||||
|     } | ||||
|  | ||||
|     // MOD header pointer is at .text offset + 4 | ||||
|     u32 module_offset; | ||||
|     std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32)); | ||||
| @@ -172,7 +187,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::Process& process) { | ||||
|  | ||||
|     // Load module | ||||
|     const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); | ||||
|     LoadModule(file, base_address); | ||||
|     LoadModule(file, base_address, true); | ||||
|     LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); | ||||
|  | ||||
|     process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); | ||||
|   | ||||
| @@ -11,6 +11,15 @@ | ||||
|  | ||||
| namespace Loader { | ||||
|  | ||||
| constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000; | ||||
|  | ||||
| struct NSOArgumentHeader { | ||||
|     u32_le allocated_size; | ||||
|     u32_le actual_size; | ||||
|     INSERT_PADDING_BYTES(0x18); | ||||
| }; | ||||
| static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size."); | ||||
|  | ||||
| /// Loads an NSO file | ||||
| class AppLoader_NSO final : public AppLoader, Linker { | ||||
| public: | ||||
| @@ -27,7 +36,7 @@ public: | ||||
|         return IdentifyType(file); | ||||
|     } | ||||
|  | ||||
|     static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, | ||||
|     static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, bool should_pass_arguments, | ||||
|                             boost::optional<FileSys::PatchManager> pm = boost::none); | ||||
|  | ||||
|     ResultStatus Load(Kernel::Process& process) override; | ||||
|   | ||||
| @@ -155,6 +155,7 @@ struct Values { | ||||
|     // Debugging | ||||
|     bool use_gdbstub; | ||||
|     u16 gdbstub_port; | ||||
|     std::string program_args; | ||||
|  | ||||
|     // WebService | ||||
|     bool enable_telemetry; | ||||
|   | ||||
| @@ -134,6 +134,7 @@ void Config::ReadValues() { | ||||
|     qt_config->beginGroup("Debugging"); | ||||
|     Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); | ||||
|     Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); | ||||
|     Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString(); | ||||
|     qt_config->endGroup(); | ||||
|  | ||||
|     qt_config->beginGroup("WebService"); | ||||
| @@ -269,6 +270,7 @@ void Config::SaveValues() { | ||||
|     qt_config->beginGroup("Debugging"); | ||||
|     qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); | ||||
|     qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); | ||||
|     qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args)); | ||||
|     qt_config->endGroup(); | ||||
|  | ||||
|     qt_config->beginGroup("WebService"); | ||||
|   | ||||
| @@ -33,6 +33,7 @@ void ConfigureDebug::setConfiguration() { | ||||
|     ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | ||||
|     ui->toggle_console->setChecked(UISettings::values.show_console); | ||||
|     ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); | ||||
|     ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); | ||||
| } | ||||
|  | ||||
| void ConfigureDebug::applyConfiguration() { | ||||
| @@ -40,6 +41,7 @@ void ConfigureDebug::applyConfiguration() { | ||||
|     Settings::values.gdbstub_port = ui->gdbport_spinbox->value(); | ||||
|     UISettings::values.show_console = ui->toggle_console->isChecked(); | ||||
|     Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); | ||||
|     Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); | ||||
|     Debugger::ToggleConsole(); | ||||
|     Log::Filter filter; | ||||
|     filter.ParseFilterString(Settings::values.log_filter); | ||||
|   | ||||
| @@ -106,6 +106,29 @@ | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QGroupBox" name="groupBox_3"> | ||||
|      <property name="title"> | ||||
|       <string>Homebrew</string> | ||||
|      </property> | ||||
|      <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|       <item> | ||||
|        <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|         <item> | ||||
|          <widget class="QLabel" name="label"> | ||||
|           <property name="text"> | ||||
|            <string>Arguments String</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item> | ||||
|          <widget class="QLineEdit" name="homebrew_args_edit"/> | ||||
|         </item> | ||||
|        </layout> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <spacer name="verticalSpacer"> | ||||
|      <property name="orientation"> | ||||
|   | ||||
| @@ -138,6 +138,7 @@ void Config::ReadValues() { | ||||
|     Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); | ||||
|     Settings::values.gdbstub_port = | ||||
|         static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); | ||||
|     Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", ""); | ||||
|  | ||||
|     // Web Service | ||||
|     Settings::values.enable_telemetry = | ||||
|   | ||||
| @@ -56,9 +56,10 @@ static void PrintHelp(const char* argv0) { | ||||
|     std::cout << "Usage: " << argv0 | ||||
|               << " [options] <filename>\n" | ||||
|                  "-g, --gdbport=NUMBER  Enable gdb stub on port NUMBER\n" | ||||
|                  "-f, --fullscreen     Start in fullscreen mode\n" | ||||
|                  "-f, --fullscreen      Start in fullscreen mode\n" | ||||
|                  "-h, --help            Display this help and exit\n" | ||||
|                  "-v, --version         Output version information and exit\n"; | ||||
|                  "-v, --version         Output version information and exit\n" | ||||
|                  "-p, --program         Pass following string as arguments to executable\n"; | ||||
| } | ||||
|  | ||||
| static void PrintVersion() { | ||||
| @@ -103,15 +104,13 @@ int main(int argc, char** argv) { | ||||
|     bool fullscreen = false; | ||||
|  | ||||
|     static struct option long_options[] = { | ||||
|         {"gdbport", required_argument, 0, 'g'}, | ||||
|         {"fullscreen", no_argument, 0, 'f'}, | ||||
|         {"help", no_argument, 0, 'h'}, | ||||
|         {"version", no_argument, 0, 'v'}, | ||||
|         {0, 0, 0, 0}, | ||||
|         {"gdbport", required_argument, 0, 'g'}, {"fullscreen", no_argument, 0, 'f'}, | ||||
|         {"help", no_argument, 0, 'h'},          {"version", no_argument, 0, 'v'}, | ||||
|         {"program", optional_argument, 0, 'p'}, {0, 0, 0, 0}, | ||||
|     }; | ||||
|  | ||||
|     while (optind < argc) { | ||||
|         char arg = getopt_long(argc, argv, "g:fhv", long_options, &option_index); | ||||
|         char arg = getopt_long(argc, argv, "g:fhvp::", long_options, &option_index); | ||||
|         if (arg != -1) { | ||||
|             switch (arg) { | ||||
|             case 'g': | ||||
| @@ -135,6 +134,10 @@ int main(int argc, char** argv) { | ||||
|             case 'v': | ||||
|                 PrintVersion(); | ||||
|                 return 0; | ||||
|             case 'p': | ||||
|                 Settings::values.program_args = argv[optind]; | ||||
|                 ++optind; | ||||
|                 break; | ||||
|             } | ||||
|         } else { | ||||
| #ifdef _WIN32 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user