@@ -2866,6 +2866,150 @@ class CommandObjectTargetModulesAdd : public CommandObjectParsed {
28662866 }
28672867};
28682868
2869+ class CommandObjectTargetModulesReplace : public CommandObjectParsed {
2870+ public:
2871+ CommandObjectTargetModulesReplace (CommandInterpreter &interpreter)
2872+ : CommandObjectParsed(
2873+ interpreter, " target modules replace" ,
2874+ " Replace module's existing object file with a new object file." ,
2875+ " target modules replace [<module>]" , eCommandRequiresTarget),
2876+ m_file_to_replace (LLDB_OPT_SET_1, false , " shlib" , ' s' ,
2877+ lldb::eModuleCompletion, eArgTypeShlibName,
2878+ " File name of the shared library to replace." ) {
2879+ m_option_group.Append (&m_uuid_option_group, LLDB_OPT_SET_ALL,
2880+ LLDB_OPT_SET_1);
2881+ m_option_group.Append (&m_file_to_replace, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
2882+ m_option_group.Finalize ();
2883+ CommandArgumentData module_arg{eArgTypePath, eArgRepeatStar};
2884+ m_arguments.push_back ({module_arg});
2885+ }
2886+
2887+ ~CommandObjectTargetModulesReplace () override = default ;
2888+
2889+ Options *GetOptions () override { return &m_option_group; }
2890+
2891+ void
2892+ HandleArgumentCompletion (CompletionRequest &request,
2893+ OptionElementVector &opt_element_vector) override {
2894+ CommandCompletions::InvokeCommonCompletionCallbacks (
2895+ GetCommandInterpreter (), lldb::eDiskFileCompletion, request, nullptr );
2896+ }
2897+
2898+ protected:
2899+ OptionGroupOptions m_option_group;
2900+ OptionGroupUUID m_uuid_option_group;
2901+ OptionGroupFile m_file_to_replace;
2902+
2903+ void DoExecute (Args &args, CommandReturnObject &result) override {
2904+ if (args.GetArgumentCount () == 0 ) {
2905+ result.AppendError (
2906+ " one or more executable image paths must be specified" );
2907+ return ;
2908+ }
2909+
2910+ Target &target = GetTarget ();
2911+ bool flush = false ;
2912+ // TODO: investigate if we should only allow one module. Similar for
2913+ // CommandObjectTargetModulesAdd and CommandObjectTargetSymbolsAdd.
2914+ for (auto &entry : args.entries ()) {
2915+ if (entry.ref ().empty ())
2916+ continue ;
2917+
2918+ FileSpec file_spec (entry.ref ());
2919+ if (FileSystem::Instance ().Exists (file_spec)) {
2920+ ModuleSpec module_spec (file_spec);
2921+ if (m_uuid_option_group.GetOptionValue ().OptionWasSet ())
2922+ module_spec.GetUUID () =
2923+ m_uuid_option_group.GetOptionValue ().GetCurrentValue ();
2924+ if (!module_spec.GetArchitecture ().IsValid ())
2925+ module_spec.GetArchitecture () = target.GetArchitecture ();
2926+ if (m_file_to_replace.GetOptionValue ().OptionWasSet ())
2927+ module_spec.GetFileSpec ().SetFilename (
2928+ m_file_to_replace.GetOptionValue ()
2929+ .GetCurrentValue ()
2930+ .GetFilename ());
2931+
2932+ ModuleList matching_modules = findMatchingModules (module_spec);
2933+ if (matching_modules.IsEmpty ()) {
2934+ result.AppendErrorWithFormat (" can't find matching modules for '%s'" ,
2935+ entry.ref ().str ().c_str ());
2936+ return ;
2937+ }
2938+
2939+ if (matching_modules.GetSize () > 1 ) {
2940+ result.AppendErrorWithFormat (
2941+ " multiple modules match symbol file '%s', "
2942+ " use the --uuid option to resolve the "
2943+ " ambiguity.\n " ,
2944+ entry.ref ().str ().c_str ());
2945+ return ;
2946+ }
2947+
2948+ assert (matching_modules.GetSize () == 1 );
2949+ auto module_sp = matching_modules.GetModuleAtIndex (0 );
2950+ module_sp->ReplaceObjectFile (target, file_spec, /* object_offset=*/ 0 );
2951+
2952+ if (target.GetPreloadSymbols ())
2953+ module_sp->PreloadSymbols ();
2954+
2955+ flush = true ;
2956+ result.SetStatus (eReturnStatusSuccessFinishResult);
2957+ } else {
2958+ std::string resolved_path = file_spec.GetPath ();
2959+ if (resolved_path != entry.ref ()) {
2960+ result.AppendErrorWithFormat (
2961+ " invalid module path '%s' with resolved path '%s'\n " ,
2962+ entry.ref ().str ().c_str (), resolved_path.c_str ());
2963+ break ;
2964+ }
2965+ result.AppendErrorWithFormat (" invalid module path '%s'\n " ,
2966+ entry.c_str ());
2967+ break ;
2968+ }
2969+ }
2970+
2971+ if (flush) {
2972+ ProcessSP process = target.GetProcessSP ();
2973+ if (process)
2974+ process->Flush ();
2975+ }
2976+ return ;
2977+ }
2978+
2979+ ModuleList findMatchingModules (const ModuleSpec &module_spec) {
2980+ Target &target = GetTarget ();
2981+ ModuleList matching_modules;
2982+ lldb_private::ModuleSpecList module_specs;
2983+ if (ObjectFile::GetModuleSpecifications (module_spec.GetFileSpec (), 0 , 0 ,
2984+ module_specs)) {
2985+ // Now extract the module spec that matches the target architecture
2986+ ModuleSpec target_arch_module_spec;
2987+ ModuleSpec arch_matched_module_spec;
2988+ target_arch_module_spec.GetArchitecture () = target.GetArchitecture ();
2989+ if (module_specs.FindMatchingModuleSpec (target_arch_module_spec,
2990+ arch_matched_module_spec)) {
2991+ if (arch_matched_module_spec.GetUUID ().IsValid ()) {
2992+ // It has a UUID, look for this UUID in the target modules
2993+ ModuleSpec uuid_module_spec;
2994+ uuid_module_spec.GetUUID () = arch_matched_module_spec.GetUUID ();
2995+ target.GetImages ().FindModules (uuid_module_spec, matching_modules);
2996+ }
2997+ }
2998+ }
2999+
3000+ // Just try to match up the file by basename if we have no matches at
3001+ // this point.
3002+ if (matching_modules.IsEmpty ()) {
3003+ ModuleSpec filename_only_spec;
3004+ filename_only_spec.GetFileSpec ().SetFilename (
3005+ module_spec.GetFileSpec ().GetFilename ());
3006+ target.GetImages ().FindModules (filename_only_spec, matching_modules);
3007+ }
3008+
3009+ return matching_modules;
3010+ }
3011+ };
3012+
28693013class CommandObjectTargetModulesLoad
28703014 : public CommandObjectTargetModulesModuleAutoComplete {
28713015public:
@@ -3333,10 +3477,14 @@ class CommandObjectTargetModulesList : public CommandObjectParsed {
33333477 DumpModuleArchitecture (strm, module , true , width);
33343478 break ;
33353479
3336- case ' f' :
3480+ case ' f' : {
33373481 DumpFullpath (strm, &module ->GetFileSpec (), width);
33383482 dump_object_name = true ;
3339- break ;
3483+
3484+ ObjectFile *objfile = module ->GetObjectFile ();
3485+ if (objfile && objfile->GetPluginName () == " placeholder" )
3486+ strm.Printf (" (*)" );
3487+ } break ;
33403488
33413489 case ' d' :
33423490 DumpDirectory (strm, &module ->GetFileSpec (), width);
@@ -4205,6 +4353,9 @@ class CommandObjectTargetModules : public CommandObjectMultiword {
42054353 " target modules <sub-command> ..." ) {
42064354 LoadSubCommand (
42074355 " add" , CommandObjectSP (new CommandObjectTargetModulesAdd (interpreter)));
4356+ LoadSubCommand (
4357+ " replace" ,
4358+ CommandObjectSP (new CommandObjectTargetModulesReplace (interpreter)));
42084359 LoadSubCommand (" load" , CommandObjectSP (new CommandObjectTargetModulesLoad (
42094360 interpreter)));
42104361 LoadSubCommand (" dump" , CommandObjectSP (new CommandObjectTargetModulesDump (
0 commit comments