1 //===-- TargetList.cpp ------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "lldb/lldb-python.h"
11 
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 // Project includes
16 #include "lldb/Core/Broadcaster.h"
17 #include "lldb/Core/Debugger.h"
18 #include "lldb/Core/Event.h"
19 #include "lldb/Core/Module.h"
20 #include "lldb/Core/ModuleSpec.h"
21 #include "lldb/Core/State.h"
22 #include "lldb/Core/Timer.h"
23 #include "lldb/Host/Host.h"
24 #include "lldb/Host/HostInfo.h"
25 #include "lldb/Interpreter/CommandInterpreter.h"
26 #include "lldb/Interpreter/OptionGroupPlatform.h"
27 #include "lldb/Symbol/ObjectFile.h"
28 #include "lldb/Target/Platform.h"
29 #include "lldb/Target/Process.h"
30 #include "lldb/Target/TargetList.h"
31 
32 #include "llvm/ADT/SmallString.h"
33 
34 using namespace lldb;
35 using namespace lldb_private;
36 
37 ConstString &
38 TargetList::GetStaticBroadcasterClass ()
39 {
40     static ConstString class_name ("lldb.targetList");
41     return class_name;
42 }
43 
44 //----------------------------------------------------------------------
45 // TargetList constructor
46 //----------------------------------------------------------------------
47 TargetList::TargetList(Debugger &debugger) :
48     Broadcaster(&debugger, TargetList::GetStaticBroadcasterClass().AsCString()),
49     m_target_list(),
50     m_target_list_mutex (Mutex::eMutexTypeRecursive),
51     m_selected_target_idx (0)
52 {
53     CheckInWithManager();
54 }
55 
56 //----------------------------------------------------------------------
57 // Destructor
58 //----------------------------------------------------------------------
59 TargetList::~TargetList()
60 {
61     Mutex::Locker locker(m_target_list_mutex);
62     m_target_list.clear();
63 }
64 
65 Error
66 TargetList::CreateTarget (Debugger &debugger,
67                           const char *user_exe_path,
68                           const char *triple_cstr,
69                           bool get_dependent_files,
70                           const OptionGroupPlatform *platform_options,
71                           TargetSP &target_sp)
72 {
73     return CreateTargetInternal (debugger,
74                                  user_exe_path,
75                                  triple_cstr,
76                                  get_dependent_files,
77                                  platform_options,
78                                  target_sp,
79                                  false);
80 }
81 
82 Error
83 TargetList::CreateTarget (Debugger &debugger,
84                           const char *user_exe_path,
85                           const ArchSpec& specified_arch,
86                           bool get_dependent_files,
87                           PlatformSP &platform_sp,
88                           TargetSP &target_sp)
89 {
90     return CreateTargetInternal (debugger,
91                                  user_exe_path,
92                                  specified_arch,
93                                  get_dependent_files,
94                                  platform_sp,
95                                  target_sp,
96                                  false);
97 }
98 
99 Error
100 TargetList::CreateTargetInternal (Debugger &debugger,
101                           const char *user_exe_path,
102                           const char *triple_cstr,
103                           bool get_dependent_files,
104                           const OptionGroupPlatform *platform_options,
105                           TargetSP &target_sp,
106                           bool is_dummy_target)
107 {
108     Error error;
109     PlatformSP platform_sp;
110 
111     // This is purposely left empty unless it is specified by triple_cstr.
112     // If not initialized via triple_cstr, then the currently selected platform
113     // will set the architecture correctly.
114     const ArchSpec arch(triple_cstr);
115     if (triple_cstr && triple_cstr[0])
116     {
117         if (!arch.IsValid())
118         {
119             error.SetErrorStringWithFormat("invalid triple '%s'", triple_cstr);
120             return error;
121         }
122     }
123 
124     ArchSpec platform_arch(arch);
125 
126     bool prefer_platform_arch = false;
127 
128     CommandInterpreter &interpreter = debugger.GetCommandInterpreter();
129 
130     // let's see if there is already an existing plaform before we go creating another...
131     platform_sp = debugger.GetPlatformList().GetSelectedPlatform();
132 
133     if (!platform_sp && platform_options && platform_options->PlatformWasSpecified ())
134     {
135         const bool select_platform = true;
136         platform_sp = platform_options->CreatePlatformWithOptions (interpreter,
137                                                                    arch,
138                                                                    select_platform,
139                                                                    error,
140                                                                    platform_arch);
141         if (!platform_sp)
142             return error;
143     }
144 
145     if (user_exe_path && user_exe_path[0])
146     {
147         ModuleSpecList module_specs;
148         ModuleSpec module_spec;
149         module_spec.GetFileSpec().SetFile(user_exe_path, true);
150 
151         // Resolve the executable in case we are given a path to a application bundle
152         // like a .app bundle on MacOSX
153         Host::ResolveExecutableInBundle (module_spec.GetFileSpec());
154 
155         lldb::offset_t file_offset = 0;
156         lldb::offset_t file_size = 0;
157         const size_t num_specs = ObjectFile::GetModuleSpecifications (module_spec.GetFileSpec(), file_offset, file_size, module_specs);
158         if (num_specs > 0)
159         {
160             ModuleSpec matching_module_spec;
161 
162             if (num_specs == 1)
163             {
164                 if (module_specs.GetModuleSpecAtIndex(0, matching_module_spec))
165                 {
166                     if (platform_arch.IsValid())
167                     {
168                         if (platform_arch.IsCompatibleMatch(matching_module_spec.GetArchitecture()))
169                         {
170                             // If the OS or vendor weren't specified, then adopt the module's
171                             // architecture so that the platform matching can be more accurate
172                             if (!platform_arch.TripleOSWasSpecified() || !platform_arch.TripleVendorWasSpecified())
173                             {
174                                 prefer_platform_arch = true;
175                                 platform_arch = matching_module_spec.GetArchitecture();
176                             }
177                         }
178                         else
179                         {
180                             error.SetErrorStringWithFormat("the specified architecture '%s' is not compatible with '%s' in '%s'",
181                                                            platform_arch.GetTriple().str().c_str(),
182                                                            matching_module_spec.GetArchitecture().GetTriple().str().c_str(),
183                                                            module_spec.GetFileSpec().GetPath().c_str());
184                             return error;
185                         }
186                     }
187                     else
188                     {
189                         // Only one arch and none was specified
190                         prefer_platform_arch = true;
191                         platform_arch = matching_module_spec.GetArchitecture();
192                     }
193                 }
194             }
195             else
196             {
197                 if (arch.IsValid())
198                 {
199                     module_spec.GetArchitecture() = arch;
200                     if (module_specs.FindMatchingModuleSpec(module_spec, matching_module_spec))
201                     {
202                         prefer_platform_arch = true;
203                         platform_arch = matching_module_spec.GetArchitecture();
204                     }
205                 }
206                 else
207                 {
208                     // No architecture specified, check if there is only one platform for
209                     // all of the architectures.
210 
211                     typedef std::vector<PlatformSP> PlatformList;
212                     PlatformList platforms;
213                     PlatformSP host_platform_sp = Platform::GetHostPlatform();
214                     for (size_t i=0; i<num_specs; ++i)
215                     {
216                         ModuleSpec module_spec;
217                         if (module_specs.GetModuleSpecAtIndex(i, module_spec))
218                         {
219                             // See if there was a selected platform and check that first
220                             // since the user may have specified it.
221                             if (platform_sp)
222                             {
223                                 if (platform_sp->IsCompatibleArchitecture(module_spec.GetArchitecture(), false, NULL))
224                                 {
225                                     platforms.push_back(platform_sp);
226                                     continue;
227                                 }
228                             }
229 
230                             // Next check the host platform it if wasn't already checked above
231                             if (host_platform_sp && (!platform_sp || host_platform_sp->GetName() != platform_sp->GetName()))
232                             {
233                                 if (host_platform_sp->IsCompatibleArchitecture(module_spec.GetArchitecture(), false, NULL))
234                                 {
235                                     platforms.push_back(host_platform_sp);
236                                     continue;
237                                 }
238                             }
239 
240                             // Just find a platform that matches the architecture in the executable file
241                             platforms.push_back(Platform::GetPlatformForArchitecture(module_spec.GetArchitecture(), nullptr));
242                         }
243                     }
244 
245                     Platform *platform_ptr = NULL;
246                     for (const auto &the_platform_sp : platforms)
247                     {
248                         if (platform_ptr)
249                         {
250                             if (platform_ptr->GetName() != the_platform_sp->GetName())
251                             {
252                                 platform_ptr = NULL;
253                                 break;
254                             }
255                         }
256                         else
257                         {
258                             platform_ptr = the_platform_sp.get();
259                         }
260                     }
261 
262                     if (platform_ptr)
263                     {
264                         // All platforms for all modules in the exectuable match, so we can select this platform
265                         platform_sp = platforms.front();
266                     }
267                     else
268                     {
269                         // More than one platform claims to support this file, so the --platform option must be specified
270                         StreamString error_strm;
271                         std::set<Platform *> platform_set;
272                         error_strm.Printf ("more than one platform supports this executable (");
273                         for (const auto &the_platform_sp : platforms)
274                         {
275                             if (platform_set.find(the_platform_sp.get()) == platform_set.end())
276                             {
277                                 if (!platform_set.empty())
278                                     error_strm.PutCString(", ");
279                                 error_strm.PutCString(the_platform_sp->GetName().GetCString());
280                                 platform_set.insert(the_platform_sp.get());
281                             }
282                         }
283                         error_strm.Printf("), use the --platform option to specify a platform");
284                         error.SetErrorString(error_strm.GetString().c_str());
285                         return error;
286                     }
287                 }
288             }
289         }
290     }
291 
292     if (!platform_sp)
293     {
294         // Get the current platform and make sure it is compatible with the
295         // current architecture if we have a valid architecture.
296         platform_sp = debugger.GetPlatformList().GetSelectedPlatform ();
297 
298         if (!prefer_platform_arch && arch.IsValid())
299         {
300             if (!platform_sp->IsCompatibleArchitecture(arch, false, &platform_arch))
301             {
302                 platform_sp = Platform::GetPlatformForArchitecture(arch, &platform_arch);
303                 if (platform_sp)
304                     debugger.GetPlatformList().SetSelectedPlatform(platform_sp);
305             }
306         }
307         else if (platform_arch.IsValid())
308         {
309             // if "arch" isn't valid, yet "platform_arch" is, it means we have an executable file with
310             // a single architecture which should be used
311             ArchSpec fixed_platform_arch;
312             if (!platform_sp->IsCompatibleArchitecture(platform_arch, false, &fixed_platform_arch))
313             {
314                 platform_sp = Platform::GetPlatformForArchitecture(platform_arch, &fixed_platform_arch);
315                 if (platform_sp)
316                     debugger.GetPlatformList().SetSelectedPlatform(platform_sp);
317             }
318         }
319     }
320 
321     if (!platform_arch.IsValid())
322         platform_arch = arch;
323 
324     error = TargetList::CreateTargetInternal (debugger,
325                                               user_exe_path,
326                                               platform_arch,
327                                               get_dependent_files,
328                                               platform_sp,
329                                               target_sp,
330                                               is_dummy_target);
331     return error;
332 }
333 
334 lldb::TargetSP
335 TargetList::GetDummyTarget (lldb_private::Debugger &debugger)
336 {
337     // FIXME: Maybe the dummy target should be per-Debugger
338     if (!m_dummy_target_sp || !m_dummy_target_sp->IsValid())
339     {
340         ArchSpec arch(Target::GetDefaultArchitecture());
341         if (!arch.IsValid())
342             arch = HostInfo::GetArchitecture();
343         Error err = CreateDummyTarget(debugger,
344                                       arch.GetTriple().getTriple().c_str(),
345                                       m_dummy_target_sp);
346     }
347 
348     return m_dummy_target_sp;
349 }
350 
351 Error
352 TargetList::CreateDummyTarget (Debugger &debugger,
353                                const char *specified_arch_name,
354                                lldb::TargetSP &target_sp)
355 {
356     PlatformSP host_platform_sp(Platform::GetHostPlatform());
357     return CreateTargetInternal (debugger,
358                                  (const char *) nullptr,
359                                  specified_arch_name,
360                                  false,
361                                  (const OptionGroupPlatform *) nullptr,
362                                  target_sp,
363                                  true);
364 }
365 
366 Error
367 TargetList::CreateTargetInternal (Debugger &debugger,
368                       const char *user_exe_path,
369                       const ArchSpec& specified_arch,
370                       bool get_dependent_files,
371                       lldb::PlatformSP &platform_sp,
372                       lldb::TargetSP &target_sp,
373                       bool is_dummy_target)
374 {
375 
376     Timer scoped_timer (__PRETTY_FUNCTION__,
377                         "TargetList::CreateTarget (file = '%s', arch = '%s')",
378                         user_exe_path,
379                         specified_arch.GetArchitectureName());
380     Error error;
381 
382     ArchSpec arch(specified_arch);
383 
384     if (arch.IsValid())
385     {
386         if (!platform_sp || !platform_sp->IsCompatibleArchitecture(arch, false, NULL))
387             platform_sp = Platform::GetPlatformForArchitecture(specified_arch, &arch);
388     }
389 
390     if (!platform_sp)
391         platform_sp = debugger.GetPlatformList().GetSelectedPlatform();
392 
393     if (!arch.IsValid())
394         arch = specified_arch;
395 
396     FileSpec file (user_exe_path, false);
397     if (!file.Exists() && user_exe_path && user_exe_path[0] == '~')
398     {
399         // we want to expand the tilde but we don't want to resolve any symbolic links
400         // so we can't use the FileSpec constructor's resolve flag
401         llvm::SmallString<64> unglobbed_path(user_exe_path);
402         FileSpec::ResolveUsername(unglobbed_path);
403 
404         if (unglobbed_path.empty())
405             file = FileSpec(user_exe_path, false);
406         else
407             file = FileSpec(unglobbed_path.c_str(), false);
408     }
409 
410     bool user_exe_path_is_bundle = false;
411     char resolved_bundle_exe_path[PATH_MAX];
412     resolved_bundle_exe_path[0] = '\0';
413     if (file)
414     {
415         if (file.GetFileType() == FileSpec::eFileTypeDirectory)
416             user_exe_path_is_bundle = true;
417 
418         if (file.IsRelativeToCurrentWorkingDirectory() && user_exe_path)
419         {
420             // Ignore paths that start with "./" and "../"
421             if (!((user_exe_path[0] == '.' && user_exe_path[1] == '/') ||
422                   (user_exe_path[0] == '.' && user_exe_path[1] == '.' && user_exe_path[2] == '/')))
423             {
424                 char cwd[PATH_MAX];
425                 if (getcwd (cwd, sizeof(cwd)))
426                 {
427                     std::string cwd_user_exe_path (cwd);
428                     cwd_user_exe_path += '/';
429                     cwd_user_exe_path += user_exe_path;
430                     FileSpec cwd_file (cwd_user_exe_path.c_str(), false);
431                     if (cwd_file.Exists())
432                         file = cwd_file;
433                 }
434             }
435         }
436 
437         ModuleSP exe_module_sp;
438         if (platform_sp)
439         {
440             FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths());
441             ModuleSpec module_spec(file, arch);
442             error = platform_sp->ResolveExecutable (module_spec,
443                                                     exe_module_sp,
444                                                     executable_search_paths.GetSize() ? &executable_search_paths : NULL);
445         }
446 
447         if (error.Success() && exe_module_sp)
448         {
449             if (exe_module_sp->GetObjectFile() == NULL)
450             {
451                 if (arch.IsValid())
452                 {
453                     error.SetErrorStringWithFormat("\"%s\" doesn't contain architecture %s",
454                                                    file.GetPath().c_str(),
455                                                    arch.GetArchitectureName());
456                 }
457                 else
458                 {
459                     error.SetErrorStringWithFormat("unsupported file type \"%s\"",
460                                                    file.GetPath().c_str());
461                 }
462                 return error;
463             }
464             target_sp.reset(new Target(debugger, arch, platform_sp, is_dummy_target));
465             target_sp->SetExecutableModule (exe_module_sp, get_dependent_files);
466             if (user_exe_path_is_bundle)
467                 exe_module_sp->GetFileSpec().GetPath(resolved_bundle_exe_path, sizeof(resolved_bundle_exe_path));
468         }
469     }
470     else
471     {
472         // No file was specified, just create an empty target with any arch
473         // if a valid arch was specified
474         target_sp.reset(new Target(debugger, arch, platform_sp, is_dummy_target));
475     }
476 
477     if (target_sp)
478     {
479         // Set argv0 with what the user typed, unless the user specified a
480         // directory. If the user specified a directory, then it is probably a
481         // bundle that was resolved and we need to use the resolved bundle path
482         if (user_exe_path)
483         {
484             // Use exactly what the user typed as the first argument when we exec or posix_spawn
485             if (user_exe_path_is_bundle && resolved_bundle_exe_path[0])
486             {
487                 target_sp->SetArg0 (resolved_bundle_exe_path);
488             }
489             else
490             {
491                 // Use resolved path
492                 target_sp->SetArg0 (file.GetPath().c_str());
493             }
494         }
495         if (file.GetDirectory())
496         {
497             FileSpec file_dir;
498             file_dir.GetDirectory() = file.GetDirectory();
499             target_sp->GetExecutableSearchPaths ().Append (file_dir);
500         }
501 
502         // Don't put the dummy target in the target list, it's held separately.
503         if (!is_dummy_target)
504         {
505             Mutex::Locker locker(m_target_list_mutex);
506             m_selected_target_idx = m_target_list.size();
507             m_target_list.push_back(target_sp);
508             // Now prime this from the dummy target:
509             target_sp->PrimeFromDummyTarget(debugger.GetDummyTarget());
510         }
511         else
512         {
513             m_dummy_target_sp = target_sp;
514         }
515     }
516 
517     return error;
518 }
519 
520 bool
521 TargetList::DeleteTarget (TargetSP &target_sp)
522 {
523     Mutex::Locker locker(m_target_list_mutex);
524     collection::iterator pos, end = m_target_list.end();
525 
526     for (pos = m_target_list.begin(); pos != end; ++pos)
527     {
528         if (pos->get() == target_sp.get())
529         {
530             m_target_list.erase(pos);
531             return true;
532         }
533     }
534     return false;
535 }
536 
537 
538 TargetSP
539 TargetList::FindTargetWithExecutableAndArchitecture
540 (
541     const FileSpec &exe_file_spec,
542     const ArchSpec *exe_arch_ptr
543 ) const
544 {
545     Mutex::Locker locker (m_target_list_mutex);
546     TargetSP target_sp;
547     bool full_match = (bool)exe_file_spec.GetDirectory();
548 
549     collection::const_iterator pos, end = m_target_list.end();
550     for (pos = m_target_list.begin(); pos != end; ++pos)
551     {
552         Module *exe_module = (*pos)->GetExecutableModulePointer();
553 
554         if (exe_module)
555         {
556             if (FileSpec::Equal (exe_file_spec, exe_module->GetFileSpec(), full_match))
557             {
558                 if (exe_arch_ptr)
559                 {
560                     if (!exe_arch_ptr->IsCompatibleMatch(exe_module->GetArchitecture()))
561                         continue;
562                 }
563                 target_sp = *pos;
564                 break;
565             }
566         }
567     }
568     return target_sp;
569 }
570 
571 TargetSP
572 TargetList::FindTargetWithProcessID (lldb::pid_t pid) const
573 {
574     Mutex::Locker locker(m_target_list_mutex);
575     TargetSP target_sp;
576     collection::const_iterator pos, end = m_target_list.end();
577     for (pos = m_target_list.begin(); pos != end; ++pos)
578     {
579         Process* process = (*pos)->GetProcessSP().get();
580         if (process && process->GetID() == pid)
581         {
582             target_sp = *pos;
583             break;
584         }
585     }
586     return target_sp;
587 }
588 
589 
590 TargetSP
591 TargetList::FindTargetWithProcess (Process *process) const
592 {
593     TargetSP target_sp;
594     if (process)
595     {
596         Mutex::Locker locker(m_target_list_mutex);
597         collection::const_iterator pos, end = m_target_list.end();
598         for (pos = m_target_list.begin(); pos != end; ++pos)
599         {
600             if (process == (*pos)->GetProcessSP().get())
601             {
602                 target_sp = *pos;
603                 break;
604             }
605         }
606     }
607     return target_sp;
608 }
609 
610 TargetSP
611 TargetList::GetTargetSP (Target *target) const
612 {
613     TargetSP target_sp;
614     if (target)
615     {
616         Mutex::Locker locker(m_target_list_mutex);
617         collection::const_iterator pos, end = m_target_list.end();
618         for (pos = m_target_list.begin(); pos != end; ++pos)
619         {
620             if (target == (*pos).get())
621             {
622                 target_sp = *pos;
623                 break;
624             }
625         }
626     }
627     return target_sp;
628 }
629 
630 uint32_t
631 TargetList::SendAsyncInterrupt (lldb::pid_t pid)
632 {
633     uint32_t num_async_interrupts_sent = 0;
634 
635     if (pid != LLDB_INVALID_PROCESS_ID)
636     {
637         TargetSP target_sp(FindTargetWithProcessID (pid));
638         if (target_sp.get())
639         {
640             Process* process = target_sp->GetProcessSP().get();
641             if (process)
642             {
643                 process->SendAsyncInterrupt();
644                 ++num_async_interrupts_sent;
645             }
646         }
647     }
648     else
649     {
650         // We don't have a valid pid to broadcast to, so broadcast to the target
651         // list's async broadcaster...
652         BroadcastEvent (Process::eBroadcastBitInterrupt, NULL);
653     }
654 
655     return num_async_interrupts_sent;
656 }
657 
658 uint32_t
659 TargetList::SignalIfRunning (lldb::pid_t pid, int signo)
660 {
661     uint32_t num_signals_sent = 0;
662     Process *process = NULL;
663     if (pid == LLDB_INVALID_PROCESS_ID)
664     {
665         // Signal all processes with signal
666         Mutex::Locker locker(m_target_list_mutex);
667         collection::iterator pos, end = m_target_list.end();
668         for (pos = m_target_list.begin(); pos != end; ++pos)
669         {
670             process = (*pos)->GetProcessSP().get();
671             if (process)
672             {
673                 if (process->IsAlive())
674                 {
675                     ++num_signals_sent;
676                     process->Signal (signo);
677                 }
678             }
679         }
680     }
681     else
682     {
683         // Signal a specific process with signal
684         TargetSP target_sp(FindTargetWithProcessID (pid));
685         if (target_sp.get())
686         {
687             process = target_sp->GetProcessSP().get();
688             if (process)
689             {
690                 if (process->IsAlive())
691                 {
692                     ++num_signals_sent;
693                     process->Signal (signo);
694                 }
695             }
696         }
697     }
698     return num_signals_sent;
699 }
700 
701 int
702 TargetList::GetNumTargets () const
703 {
704     Mutex::Locker locker (m_target_list_mutex);
705     return m_target_list.size();
706 }
707 
708 lldb::TargetSP
709 TargetList::GetTargetAtIndex (uint32_t idx) const
710 {
711     TargetSP target_sp;
712     Mutex::Locker locker (m_target_list_mutex);
713     if (idx < m_target_list.size())
714         target_sp = m_target_list[idx];
715     return target_sp;
716 }
717 
718 uint32_t
719 TargetList::GetIndexOfTarget (lldb::TargetSP target_sp) const
720 {
721     Mutex::Locker locker (m_target_list_mutex);
722     size_t num_targets = m_target_list.size();
723     for (size_t idx = 0; idx < num_targets; idx++)
724     {
725         if (target_sp == m_target_list[idx])
726             return idx;
727     }
728     return UINT32_MAX;
729 }
730 
731 uint32_t
732 TargetList::SetSelectedTarget (Target* target)
733 {
734     Mutex::Locker locker (m_target_list_mutex);
735     collection::const_iterator pos,
736         begin = m_target_list.begin(),
737         end = m_target_list.end();
738     for (pos = begin; pos != end; ++pos)
739     {
740         if (pos->get() == target)
741         {
742             m_selected_target_idx = std::distance (begin, pos);
743             return m_selected_target_idx;
744         }
745     }
746     m_selected_target_idx = 0;
747     return m_selected_target_idx;
748 }
749 
750 lldb::TargetSP
751 TargetList::GetSelectedTarget ()
752 {
753     Mutex::Locker locker (m_target_list_mutex);
754     if (m_selected_target_idx >= m_target_list.size())
755         m_selected_target_idx = 0;
756     return GetTargetAtIndex (m_selected_target_idx);
757 }
758