diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 1278a44dc78ca081e52d726dfa488a1e44fe04aa..1aee9b9b5e130bd3651f045ffff6c5448288bca4 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -242,15 +242,28 @@ public: return m_arguments; } + const char * + GetArg0 () const + { + if (m_arg0.empty()) + return NULL; + return m_arg0.c_str(); + } + + void + SetArg0 (const char *arg) + { + if (arg && arg[0]) + m_arg0.clear(); + else + m_arg0 = arg; + } + void - SetArguments (const Args& args, - bool first_arg_is_executable, - bool first_arg_is_executable_and_argument); + SetArguments (const Args& args, bool first_arg_is_executable); void - SetArguments (char const **argv, - bool first_arg_is_executable, - bool first_arg_is_executable_and_argument); + SetArguments (char const **argv, bool first_arg_is_executable); Args & GetEnvironmentEntries () @@ -266,7 +279,11 @@ public: protected: FileSpec m_executable; - Args m_arguments; + std::string m_arg0; // argv[0] if supported. If empty, then use m_executable. + // Not all process plug-ins support specifying an argv[0] + // that differs from the resolved platform executable + // (which is in m_executable) + Args m_arguments; // All program arguments except argv[0] Args m_environment; uint32_t m_uid; uint32_t m_gid; diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index 8d68a490a2bc26a3130b21f8573f40836992b24f..fe784b2c6cca2e5fc914a7fdea223e08dd5f706e 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -83,6 +83,12 @@ public: InlineStrategy GetInlineStrategy () const; + const char * + GetArg0 () const; + + void + SetArg0 (const char *arg); + bool GetRunArguments (Args &args) const; diff --git a/lldb/source/Commands/CommandObjectPlatform.cpp b/lldb/source/Commands/CommandObjectPlatform.cpp index 64c05afc062bc576e8b71354660fa34d9fd85145..c97abf01d8959c42549a3d3b59d0fb8a57024fe4 100644 --- a/lldb/source/Commands/CommandObjectPlatform.cpp +++ b/lldb/source/Commands/CommandObjectPlatform.cpp @@ -416,9 +416,7 @@ protected: // We don't have any file yet, so the first argument is our // executable, and the rest are program arguments const bool first_arg_is_executable = true; - m_options.launch_info.SetArguments (args, - first_arg_is_executable, - first_arg_is_executable); + m_options.launch_info.SetArguments (args, first_arg_is_executable); } } diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp index 41e216ec6cba8a8b2ca1763363d000dc8b185790..441aee9758f758f6f1e7bc57623d5bfbdb6b1336 100644 --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -127,11 +127,6 @@ protected: return false; } - exe_module->GetFileSpec().GetPath (filename, sizeof(filename)); - - const bool add_exe_file_as_first_arg = true; - m_options.launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), add_exe_file_as_first_arg); - StateType state = eStateInvalid; Process *process = m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) @@ -167,18 +162,32 @@ protected: } } + const char *target_settings_argv0 = target->GetArg0(); + + exe_module->GetFileSpec().GetPath (filename, sizeof(filename)); + + if (target_settings_argv0) + { + m_options.launch_info.GetArguments().AppendArgument (target_settings_argv0); + m_options.launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), false); + } + else + { + m_options.launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); + } + if (launch_args.GetArgumentCount() == 0) { Args target_setting_args; - if (target->GetRunArguments(target_setting_args) > 0) + if (target->GetRunArguments(target_setting_args)) m_options.launch_info.GetArguments().AppendArguments (target_setting_args); } else { + m_options.launch_info.GetArguments().AppendArguments (launch_args); + // Save the arguments for subsequent runs in the current target. target->SetRunArguments (launch_args); - - m_options.launch_info.GetArguments().AppendArguments (launch_args); } if (target->GetDisableASLR()) diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp index 6f77106a6f786a73dcd08bfc69a256bfabfd29bf..8c46487bb20a2a5441723ff66ee6711f388dd2db 100644 --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -239,6 +239,13 @@ protected: if (target_sp) { + if (file_path) + { + // Use exactly what the user typed as the first argument + // when we exec or posix_spawn + target_sp->SetArg0 (file_path); + } + debugger.GetTargetList().SetSelectedTarget(target_sp.get()); if (core_file) { diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index 2b9ae599d8cd3d5e96cc227e29d7760c5845c95c..b25f8bcf1b104e217bd4bbf52473dc2899b22383 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -1328,8 +1328,7 @@ Host::RunShellCommand (const char *command, // No shell, just run it Args args (command); const bool first_arg_is_executable = true; - const bool first_arg_is_executable_and_argument = true; - launch_info.SetArguments(args, first_arg_is_executable, first_arg_is_executable_and_argument); + launch_info.SetArguments(args, first_arg_is_executable); } if (working_dir) diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index e77a898e6144f6ea23f3f9ea06b33d2b042ce0e1..dd28cc1edebd6d149609f364174c41ea6ffae4ef 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -302,9 +302,7 @@ ProcessInstanceInfo::DumpAsTableRow (Stream &s, Platform *platform, bool show_ar void -ProcessInfo::SetArguments (char const **argv, - bool first_arg_is_executable, - bool first_arg_is_executable_and_argument) +ProcessInfo::SetArguments (char const **argv, bool first_arg_is_executable) { m_arguments.SetArguments (argv); @@ -319,18 +317,11 @@ ProcessInfo::SetArguments (char const **argv, // could be a remote platform path const bool resolve = false; m_executable.SetFile(first_arg, resolve); - - // If argument zero is an executable and shouldn't be included - // in the arguments, remove it from the front of the arguments - if (first_arg_is_executable_and_argument == false) - m_arguments.DeleteArgumentAtIndex (0); } } } void -ProcessInfo::SetArguments (const Args& args, - bool first_arg_is_executable, - bool first_arg_is_executable_and_argument) +ProcessInfo::SetArguments (const Args& args, bool first_arg_is_executable) { // Copy all arguments m_arguments = args; @@ -346,11 +337,6 @@ ProcessInfo::SetArguments (const Args& args, // could be a remote platform path const bool resolve = false; m_executable.SetFile(first_arg, resolve); - - // If argument zero is an executable and shouldn't be included - // in the arguments, remove it from the front of the arguments - if (first_arg_is_executable_and_argument == false) - m_arguments.DeleteArgumentAtIndex (0); } } } @@ -443,19 +429,57 @@ ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error, shell_executable = shell_resolved_path; } + const char **argv = GetArguments().GetConstArgumentVector (); + if (argv == NULL || argv[0] == NULL) + return false; Args shell_arguments; std::string safe_arg; shell_arguments.AppendArgument (shell_executable); shell_arguments.AppendArgument ("-c"); - StreamString shell_command; if (will_debug) { + // Add a modified PATH environment variable in case argv[0] + // is a relative path + const char *argv0 = argv[0]; + if (argv0 && (argv0[0] != '/' && argv0[0] != '~')) + { + // We have a relative path to our executable which may not work if + // we just try to run "a.out" (without it being converted to "./a.out") + const char *working_dir = GetWorkingDirectory(); + std::string new_path("PATH="); + const size_t empty_path_len = new_path.size(); + + if (working_dir && working_dir[0]) + { + new_path += working_dir; + } + else + { + char current_working_dir[PATH_MAX]; + const char *cwd = getcwd(current_working_dir, sizeof(current_working_dir)); + if (cwd && cwd[0]) + new_path += cwd; + } + const char *curr_path = getenv("PATH"); + if (curr_path) + { + if (new_path.size() > empty_path_len) + new_path += ':'; + new_path += curr_path; + } + new_path += ' '; + shell_command.PutCString(new_path.c_str()); + } + shell_command.PutCString ("exec"); + +#if defined(__APPLE__) + // Only Apple supports /usr/bin/arch being able to specify the architecture if (GetArchitecture().IsValid()) { shell_command.Printf(" /usr/bin/arch -arch %s", GetArchitecture().GetArchitectureName()); - // Set the resume count to 2: + // Set the resume count to 2: // 1 - stop in shell // 2 - stop in /usr/bin/arch // 3 - then we will stop in our program @@ -463,39 +487,36 @@ ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error, } else { - // Set the resume count to 1: + // Set the resume count to 1: // 1 - stop in shell // 2 - then we will stop in our program SetResumeCount(1); } +#else + // Set the resume count to 1: + // 1 - stop in shell + // 2 - then we will stop in our program + SetResumeCount(1); +#endif } - - const char **argv = GetArguments().GetConstArgumentVector (); - if (argv) + + if (first_arg_is_full_shell_command) { - if (first_arg_is_full_shell_command) - { - // There should only be one argument that is the shell command itself to be used as is - if (argv[0] && !argv[1]) - shell_command.Printf("%s", argv[0]); - else - return false; - } + // There should only be one argument that is the shell command itself to be used as is + if (argv[0] && !argv[1]) + shell_command.Printf("%s", argv[0]); else - { - for (size_t i=0; argv[i] != NULL; ++i) - { - const char *arg = Args::GetShellSafeArgument (argv[i], safe_arg); - shell_command.Printf(" %s", arg); - } - } - shell_arguments.AppendArgument (shell_command.GetString().c_str()); + return false; } else { - return false; + for (size_t i=0; argv[i] != NULL; ++i) + { + const char *arg = Args::GetShellSafeArgument (argv[i], safe_arg); + shell_command.Printf(" %s", arg); + } } - + shell_arguments.AppendArgument (shell_command.GetString().c_str()); m_executable.SetFile(shell_executable, false); m_arguments = shell_arguments; return true; diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index 0dd34631da70f5c38b437b282b01b69305dd3bef..330537301afb20c2e742df6c3f071d7ba776d709 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -2151,7 +2151,8 @@ g_properties[] = { "max-children-count" , OptionValue::eTypeSInt64 , false, 256 , NULL, NULL, "Maximum number of children to expand in any level of depth." }, { "max-string-summary-length" , OptionValue::eTypeSInt64 , false, 1024 , NULL, NULL, "Maximum number of characters to show when using %s in summary strings." }, { "breakpoints-use-platform-avoid-list", OptionValue::eTypeBoolean , false, true , NULL, NULL, "Consult the platform module avoid list when setting non-module specific breakpoints." }, - { "run-args" , OptionValue::eTypeArgs , false, 0 , NULL, NULL, "A list containing all the arguments to be passed to the executable when it is run." }, + { "arg0" , OptionValue::eTypeString , false, 0 , NULL, NULL, "The first argument passed to the program in the argument array which can be different from the executable itself." }, + { "run-args" , OptionValue::eTypeArgs , false, 0 , NULL, NULL, "A list containing all the arguments to be passed to the executable when it is run. Note that this does NOT include the argv[0] which is in target.arg0." }, { "env-vars" , OptionValue::eTypeDictionary, false, OptionValue::eTypeString , NULL, NULL, "A list of all the environment variables to be passed to the executable's environment, and their values." }, { "inherit-env" , OptionValue::eTypeBoolean , false, true , NULL, NULL, "Inherit the environment from the process that is running LLDB." }, { "input-path" , OptionValue::eTypeFileSpec , false, 0 , NULL, NULL, "The file/path to be used by the executable program for reading its standard input." }, @@ -2180,6 +2181,7 @@ enum ePropertyMaxChildrenCount, ePropertyMaxSummaryLength, ePropertyBreakpointUseAvoidList, + ePropertyArg0, ePropertyRunArgs, ePropertyEnvVars, ePropertyInheritEnv, @@ -2371,6 +2373,20 @@ TargetProperties::GetInlineStrategy () const return (InlineStrategy)m_collection_sp->GetPropertyAtIndexAsEnumeration (NULL, idx, g_properties[idx].default_uint_value); } +const char * +TargetProperties::GetArg0 () const +{ + const uint32_t idx = ePropertyArg0; + return m_collection_sp->GetPropertyAtIndexAsString (NULL, idx, NULL); +} + +void +TargetProperties::SetArg0 (const char *arg) +{ + const uint32_t idx = ePropertyArg0; + m_collection_sp->SetPropertyAtIndexAsString (NULL, idx, arg); +} + bool TargetProperties::GetRunArguments (Args &args) const { diff --git a/lldb/test/expression_command/test/TestExprs.py b/lldb/test/expression_command/test/TestExprs.py index f63694e59cbbcaa62fa60093e911304911252d6a..b086c2a36aa7496eeea699247bb09482e3d2b373 100644 --- a/lldb/test/expression_command/test/TestExprs.py +++ b/lldb/test/expression_command/test/TestExprs.py @@ -71,8 +71,8 @@ class BasicExprCommandsTestCase(TestBase): # (const char *) $7 = ... self.expect("expression argv[0]", - substrs = ["(const char *)", - os.path.join(self.mydir, "a.out")]) + substrs = ["(const char *)", + "a.out"]) # (const char *) $8 = 0x... "/Volumes/data/lldb/svn/trunk/test/expression_command/test/a.out" @python_api_test