1 //===-- SBPlatform.cpp ----------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/API/SBPlatform.h"
10 #include "lldb/API/SBEnvironment.h"
11 #include "lldb/API/SBError.h"
12 #include "lldb/API/SBFileSpec.h"
13 #include "lldb/API/SBLaunchInfo.h"
14 #include "lldb/API/SBModuleSpec.h"
15 #include "lldb/API/SBPlatform.h"
16 #include "lldb/API/SBUnixSignals.h"
17 #include "lldb/Host/File.h"
18 #include "lldb/Target/Platform.h"
19 #include "lldb/Target/Target.h"
20 #include "lldb/Utility/ArchSpec.h"
21 #include "lldb/Utility/Args.h"
22 #include "lldb/Utility/Instrumentation.h"
23 #include "lldb/Utility/Status.h"
24 
25 #include "llvm/Support/FileSystem.h"
26 
27 #include <functional>
28 
29 using namespace lldb;
30 using namespace lldb_private;
31 
32 // PlatformConnectOptions
33 struct PlatformConnectOptions {
34   PlatformConnectOptions(const char *url = nullptr) {
35     if (url && url[0])
36       m_url = url;
37   }
38 
39   ~PlatformConnectOptions() = default;
40 
41   std::string m_url;
42   std::string m_rsync_options;
43   std::string m_rsync_remote_path_prefix;
44   bool m_rsync_enabled = false;
45   bool m_rsync_omit_hostname_from_remote_path = false;
46   ConstString m_local_cache_directory;
47 };
48 
49 // PlatformShellCommand
50 struct PlatformShellCommand {
51   PlatformShellCommand(llvm::StringRef shell_interpreter,
52                        llvm::StringRef shell_command) {
53     if (!shell_interpreter.empty())
54       m_shell = shell_interpreter.str();
55 
56     if (!m_shell.empty() && !shell_command.empty())
57       m_command = shell_command.str();
58   }
59 
60   PlatformShellCommand(llvm::StringRef shell_command = llvm::StringRef()) {
61     if (!shell_command.empty())
62       m_command = shell_command.str();
63   }
64 
65   ~PlatformShellCommand() = default;
66 
67   std::string m_shell;
68   std::string m_command;
69   std::string m_working_dir;
70   std::string m_output;
71   int m_status = 0;
72   int m_signo = 0;
73   Timeout<std::ratio<1>> m_timeout = std::nullopt;
74 };
75 // SBPlatformConnectOptions
76 SBPlatformConnectOptions::SBPlatformConnectOptions(const char *url)
77     : m_opaque_ptr(new PlatformConnectOptions(url)) {
78   LLDB_INSTRUMENT_VA(this, url);
79 }
80 
81 SBPlatformConnectOptions::SBPlatformConnectOptions(
82     const SBPlatformConnectOptions &rhs)
83     : m_opaque_ptr(new PlatformConnectOptions()) {
84   LLDB_INSTRUMENT_VA(this, rhs);
85 
86   *m_opaque_ptr = *rhs.m_opaque_ptr;
87 }
88 
89 SBPlatformConnectOptions::~SBPlatformConnectOptions() { delete m_opaque_ptr; }
90 
91 SBPlatformConnectOptions &
92 SBPlatformConnectOptions::operator=(const SBPlatformConnectOptions &rhs) {
93   LLDB_INSTRUMENT_VA(this, rhs);
94 
95   *m_opaque_ptr = *rhs.m_opaque_ptr;
96   return *this;
97 }
98 
99 const char *SBPlatformConnectOptions::GetURL() {
100   LLDB_INSTRUMENT_VA(this);
101 
102   if (m_opaque_ptr->m_url.empty())
103     return nullptr;
104   return ConstString(m_opaque_ptr->m_url.c_str()).GetCString();
105 }
106 
107 void SBPlatformConnectOptions::SetURL(const char *url) {
108   LLDB_INSTRUMENT_VA(this, url);
109 
110   if (url && url[0])
111     m_opaque_ptr->m_url = url;
112   else
113     m_opaque_ptr->m_url.clear();
114 }
115 
116 bool SBPlatformConnectOptions::GetRsyncEnabled() {
117   LLDB_INSTRUMENT_VA(this);
118 
119   return m_opaque_ptr->m_rsync_enabled;
120 }
121 
122 void SBPlatformConnectOptions::EnableRsync(
123     const char *options, const char *remote_path_prefix,
124     bool omit_hostname_from_remote_path) {
125   LLDB_INSTRUMENT_VA(this, options, remote_path_prefix,
126                      omit_hostname_from_remote_path);
127 
128   m_opaque_ptr->m_rsync_enabled = true;
129   m_opaque_ptr->m_rsync_omit_hostname_from_remote_path =
130       omit_hostname_from_remote_path;
131   if (remote_path_prefix && remote_path_prefix[0])
132     m_opaque_ptr->m_rsync_remote_path_prefix = remote_path_prefix;
133   else
134     m_opaque_ptr->m_rsync_remote_path_prefix.clear();
135 
136   if (options && options[0])
137     m_opaque_ptr->m_rsync_options = options;
138   else
139     m_opaque_ptr->m_rsync_options.clear();
140 }
141 
142 void SBPlatformConnectOptions::DisableRsync() {
143   LLDB_INSTRUMENT_VA(this);
144 
145   m_opaque_ptr->m_rsync_enabled = false;
146 }
147 
148 const char *SBPlatformConnectOptions::GetLocalCacheDirectory() {
149   LLDB_INSTRUMENT_VA(this);
150 
151   return m_opaque_ptr->m_local_cache_directory.GetCString();
152 }
153 
154 void SBPlatformConnectOptions::SetLocalCacheDirectory(const char *path) {
155   LLDB_INSTRUMENT_VA(this, path);
156 
157   if (path && path[0])
158     m_opaque_ptr->m_local_cache_directory.SetCString(path);
159   else
160     m_opaque_ptr->m_local_cache_directory = ConstString();
161 }
162 
163 // SBPlatformShellCommand
164 SBPlatformShellCommand::SBPlatformShellCommand(const char *shell_interpreter,
165                                                const char *shell_command)
166     : m_opaque_ptr(new PlatformShellCommand(shell_interpreter, shell_command)) {
167   LLDB_INSTRUMENT_VA(this, shell_interpreter, shell_command);
168 }
169 
170 SBPlatformShellCommand::SBPlatformShellCommand(const char *shell_command)
171     : m_opaque_ptr(new PlatformShellCommand(shell_command)) {
172   LLDB_INSTRUMENT_VA(this, shell_command);
173 }
174 
175 SBPlatformShellCommand::SBPlatformShellCommand(
176     const SBPlatformShellCommand &rhs)
177     : m_opaque_ptr(new PlatformShellCommand()) {
178   LLDB_INSTRUMENT_VA(this, rhs);
179 
180   *m_opaque_ptr = *rhs.m_opaque_ptr;
181 }
182 
183 SBPlatformShellCommand &
184 SBPlatformShellCommand::operator=(const SBPlatformShellCommand &rhs) {
185 
186   LLDB_INSTRUMENT_VA(this, rhs);
187 
188   *m_opaque_ptr = *rhs.m_opaque_ptr;
189   return *this;
190 }
191 
192 SBPlatformShellCommand::~SBPlatformShellCommand() { delete m_opaque_ptr; }
193 
194 void SBPlatformShellCommand::Clear() {
195   LLDB_INSTRUMENT_VA(this);
196 
197   m_opaque_ptr->m_output = std::string();
198   m_opaque_ptr->m_status = 0;
199   m_opaque_ptr->m_signo = 0;
200 }
201 
202 const char *SBPlatformShellCommand::GetShell() {
203   LLDB_INSTRUMENT_VA(this);
204 
205   if (m_opaque_ptr->m_shell.empty())
206     return nullptr;
207   return ConstString(m_opaque_ptr->m_shell.c_str()).GetCString();
208 }
209 
210 void SBPlatformShellCommand::SetShell(const char *shell_interpreter) {
211   LLDB_INSTRUMENT_VA(this, shell_interpreter);
212 
213   if (shell_interpreter && shell_interpreter[0])
214     m_opaque_ptr->m_shell = shell_interpreter;
215   else
216     m_opaque_ptr->m_shell.clear();
217 }
218 
219 const char *SBPlatformShellCommand::GetCommand() {
220   LLDB_INSTRUMENT_VA(this);
221 
222   if (m_opaque_ptr->m_command.empty())
223     return nullptr;
224   return ConstString(m_opaque_ptr->m_command.c_str()).GetCString();
225 }
226 
227 void SBPlatformShellCommand::SetCommand(const char *shell_command) {
228   LLDB_INSTRUMENT_VA(this, shell_command);
229 
230   if (shell_command && shell_command[0])
231     m_opaque_ptr->m_command = shell_command;
232   else
233     m_opaque_ptr->m_command.clear();
234 }
235 
236 const char *SBPlatformShellCommand::GetWorkingDirectory() {
237   LLDB_INSTRUMENT_VA(this);
238 
239   if (m_opaque_ptr->m_working_dir.empty())
240     return nullptr;
241   return ConstString(m_opaque_ptr->m_working_dir.c_str()).GetCString();
242 }
243 
244 void SBPlatformShellCommand::SetWorkingDirectory(const char *path) {
245   LLDB_INSTRUMENT_VA(this, path);
246 
247   if (path && path[0])
248     m_opaque_ptr->m_working_dir = path;
249   else
250     m_opaque_ptr->m_working_dir.clear();
251 }
252 
253 uint32_t SBPlatformShellCommand::GetTimeoutSeconds() {
254   LLDB_INSTRUMENT_VA(this);
255 
256   if (m_opaque_ptr->m_timeout)
257     return m_opaque_ptr->m_timeout->count();
258   return UINT32_MAX;
259 }
260 
261 void SBPlatformShellCommand::SetTimeoutSeconds(uint32_t sec) {
262   LLDB_INSTRUMENT_VA(this, sec);
263 
264   if (sec == UINT32_MAX)
265     m_opaque_ptr->m_timeout = std::nullopt;
266   else
267     m_opaque_ptr->m_timeout = std::chrono::seconds(sec);
268 }
269 
270 int SBPlatformShellCommand::GetSignal() {
271   LLDB_INSTRUMENT_VA(this);
272 
273   return m_opaque_ptr->m_signo;
274 }
275 
276 int SBPlatformShellCommand::GetStatus() {
277   LLDB_INSTRUMENT_VA(this);
278 
279   return m_opaque_ptr->m_status;
280 }
281 
282 const char *SBPlatformShellCommand::GetOutput() {
283   LLDB_INSTRUMENT_VA(this);
284 
285   if (m_opaque_ptr->m_output.empty())
286     return nullptr;
287   return ConstString(m_opaque_ptr->m_output.c_str()).GetCString();
288 }
289 
290 // SBPlatform
291 SBPlatform::SBPlatform() { LLDB_INSTRUMENT_VA(this); }
292 
293 SBPlatform::SBPlatform(const char *platform_name) {
294   LLDB_INSTRUMENT_VA(this, platform_name);
295 
296   m_opaque_sp = Platform::Create(platform_name);
297 }
298 
299 SBPlatform::SBPlatform(const SBPlatform &rhs) {
300   LLDB_INSTRUMENT_VA(this, rhs);
301 
302   m_opaque_sp = rhs.m_opaque_sp;
303 }
304 
305 SBPlatform &SBPlatform::operator=(const SBPlatform &rhs) {
306   LLDB_INSTRUMENT_VA(this, rhs);
307 
308   m_opaque_sp = rhs.m_opaque_sp;
309   return *this;
310 }
311 
312 SBPlatform::~SBPlatform() = default;
313 
314 SBPlatform SBPlatform::GetHostPlatform() {
315   LLDB_INSTRUMENT();
316 
317   SBPlatform host_platform;
318   host_platform.m_opaque_sp = Platform::GetHostPlatform();
319   return host_platform;
320 }
321 
322 bool SBPlatform::IsValid() const {
323   LLDB_INSTRUMENT_VA(this);
324   return this->operator bool();
325 }
326 SBPlatform::operator bool() const {
327   LLDB_INSTRUMENT_VA(this);
328 
329   return m_opaque_sp.get() != nullptr;
330 }
331 
332 void SBPlatform::Clear() {
333   LLDB_INSTRUMENT_VA(this);
334 
335   m_opaque_sp.reset();
336 }
337 
338 const char *SBPlatform::GetName() {
339   LLDB_INSTRUMENT_VA(this);
340 
341   PlatformSP platform_sp(GetSP());
342   if (platform_sp)
343     return ConstString(platform_sp->GetName()).AsCString();
344   return nullptr;
345 }
346 
347 lldb::PlatformSP SBPlatform::GetSP() const { return m_opaque_sp; }
348 
349 void SBPlatform::SetSP(const lldb::PlatformSP &platform_sp) {
350   m_opaque_sp = platform_sp;
351 }
352 
353 const char *SBPlatform::GetWorkingDirectory() {
354   LLDB_INSTRUMENT_VA(this);
355 
356   PlatformSP platform_sp(GetSP());
357   if (platform_sp)
358     return platform_sp->GetWorkingDirectory().GetPathAsConstString().AsCString();
359   return nullptr;
360 }
361 
362 bool SBPlatform::SetWorkingDirectory(const char *path) {
363   LLDB_INSTRUMENT_VA(this, path);
364 
365   PlatformSP platform_sp(GetSP());
366   if (platform_sp) {
367     if (path)
368       platform_sp->SetWorkingDirectory(FileSpec(path));
369     else
370       platform_sp->SetWorkingDirectory(FileSpec());
371     return true;
372   }
373   return false;
374 }
375 
376 SBError SBPlatform::ConnectRemote(SBPlatformConnectOptions &connect_options) {
377   LLDB_INSTRUMENT_VA(this, connect_options);
378 
379   SBError sb_error;
380   PlatformSP platform_sp(GetSP());
381   if (platform_sp && connect_options.GetURL()) {
382     Args args;
383     args.AppendArgument(connect_options.GetURL());
384     sb_error.ref() = platform_sp->ConnectRemote(args);
385   } else {
386     sb_error.SetErrorString("invalid platform");
387   }
388   return sb_error;
389 }
390 
391 void SBPlatform::DisconnectRemote() {
392   LLDB_INSTRUMENT_VA(this);
393 
394   PlatformSP platform_sp(GetSP());
395   if (platform_sp)
396     platform_sp->DisconnectRemote();
397 }
398 
399 bool SBPlatform::IsConnected() {
400   LLDB_INSTRUMENT_VA(this);
401 
402   PlatformSP platform_sp(GetSP());
403   if (platform_sp)
404     return platform_sp->IsConnected();
405   return false;
406 }
407 
408 const char *SBPlatform::GetTriple() {
409   LLDB_INSTRUMENT_VA(this);
410 
411   PlatformSP platform_sp(GetSP());
412   if (platform_sp) {
413     ArchSpec arch(platform_sp->GetSystemArchitecture());
414     if (arch.IsValid()) {
415       // Const-ify the string so we don't need to worry about the lifetime of
416       // the string
417       return ConstString(arch.GetTriple().getTriple().c_str()).GetCString();
418     }
419   }
420   return nullptr;
421 }
422 
423 const char *SBPlatform::GetOSBuild() {
424   LLDB_INSTRUMENT_VA(this);
425 
426   PlatformSP platform_sp(GetSP());
427   if (platform_sp) {
428     std::string s = platform_sp->GetOSBuildString().value_or("");
429     if (!s.empty()) {
430       // Const-ify the string so we don't need to worry about the lifetime of
431       // the string
432       return ConstString(s).GetCString();
433     }
434   }
435   return nullptr;
436 }
437 
438 const char *SBPlatform::GetOSDescription() {
439   LLDB_INSTRUMENT_VA(this);
440 
441   PlatformSP platform_sp(GetSP());
442   if (platform_sp) {
443     std::string s = platform_sp->GetOSKernelDescription().value_or("");
444     if (!s.empty()) {
445       // Const-ify the string so we don't need to worry about the lifetime of
446       // the string
447       return ConstString(s.c_str()).GetCString();
448     }
449   }
450   return nullptr;
451 }
452 
453 const char *SBPlatform::GetHostname() {
454   LLDB_INSTRUMENT_VA(this);
455 
456   PlatformSP platform_sp(GetSP());
457   if (platform_sp)
458     return ConstString(platform_sp->GetHostname()).GetCString();
459   return nullptr;
460 }
461 
462 uint32_t SBPlatform::GetOSMajorVersion() {
463   LLDB_INSTRUMENT_VA(this);
464 
465   llvm::VersionTuple version;
466   if (PlatformSP platform_sp = GetSP())
467     version = platform_sp->GetOSVersion();
468   return version.empty() ? UINT32_MAX : version.getMajor();
469 }
470 
471 uint32_t SBPlatform::GetOSMinorVersion() {
472   LLDB_INSTRUMENT_VA(this);
473 
474   llvm::VersionTuple version;
475   if (PlatformSP platform_sp = GetSP())
476     version = platform_sp->GetOSVersion();
477   return version.getMinor().value_or(UINT32_MAX);
478 }
479 
480 uint32_t SBPlatform::GetOSUpdateVersion() {
481   LLDB_INSTRUMENT_VA(this);
482 
483   llvm::VersionTuple version;
484   if (PlatformSP platform_sp = GetSP())
485     version = platform_sp->GetOSVersion();
486   return version.getSubminor().value_or(UINT32_MAX);
487 }
488 
489 void SBPlatform::SetSDKRoot(const char *sysroot) {
490   LLDB_INSTRUMENT_VA(this, sysroot);
491   if (PlatformSP platform_sp = GetSP())
492     platform_sp->SetSDKRootDirectory(llvm::StringRef(sysroot).str());
493 }
494 
495 SBError SBPlatform::Get(SBFileSpec &src, SBFileSpec &dst) {
496   LLDB_INSTRUMENT_VA(this, src, dst);
497 
498   SBError sb_error;
499   PlatformSP platform_sp(GetSP());
500   if (platform_sp) {
501     sb_error.ref() = platform_sp->GetFile(src.ref(), dst.ref());
502   } else {
503     sb_error.SetErrorString("invalid platform");
504   }
505   return sb_error;
506 }
507 
508 SBError SBPlatform::Put(SBFileSpec &src, SBFileSpec &dst) {
509   LLDB_INSTRUMENT_VA(this, src, dst);
510   return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
511     if (src.Exists()) {
512       uint32_t permissions = FileSystem::Instance().GetPermissions(src.ref());
513       if (permissions == 0) {
514         if (FileSystem::Instance().IsDirectory(src.ref()))
515           permissions = eFilePermissionsDirectoryDefault;
516         else
517           permissions = eFilePermissionsFileDefault;
518       }
519 
520       return platform_sp->PutFile(src.ref(), dst.ref(), permissions);
521     }
522 
523     Status error;
524     error.SetErrorStringWithFormat("'src' argument doesn't exist: '%s'",
525                                    src.ref().GetPath().c_str());
526     return error;
527   });
528 }
529 
530 SBError SBPlatform::Install(SBFileSpec &src, SBFileSpec &dst) {
531   LLDB_INSTRUMENT_VA(this, src, dst);
532   return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
533     if (src.Exists())
534       return platform_sp->Install(src.ref(), dst.ref());
535 
536     Status error;
537     error.SetErrorStringWithFormat("'src' argument doesn't exist: '%s'",
538                                    src.ref().GetPath().c_str());
539     return error;
540   });
541 }
542 
543 SBError SBPlatform::Run(SBPlatformShellCommand &shell_command) {
544   LLDB_INSTRUMENT_VA(this, shell_command);
545   return ExecuteConnected(
546       [&](const lldb::PlatformSP &platform_sp) {
547         const char *command = shell_command.GetCommand();
548         if (!command)
549           return Status("invalid shell command (empty)");
550 
551         if (shell_command.GetWorkingDirectory() == nullptr) {
552           std::string platform_working_dir =
553               platform_sp->GetWorkingDirectory().GetPath();
554           if (!platform_working_dir.empty())
555             shell_command.SetWorkingDirectory(platform_working_dir.c_str());
556         }
557         return platform_sp->RunShellCommand(
558             shell_command.m_opaque_ptr->m_shell, command,
559             FileSpec(shell_command.GetWorkingDirectory()),
560             &shell_command.m_opaque_ptr->m_status,
561             &shell_command.m_opaque_ptr->m_signo,
562             &shell_command.m_opaque_ptr->m_output,
563             shell_command.m_opaque_ptr->m_timeout);
564       });
565 }
566 
567 SBError SBPlatform::Launch(SBLaunchInfo &launch_info) {
568   LLDB_INSTRUMENT_VA(this, launch_info);
569   return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
570     ProcessLaunchInfo info = launch_info.ref();
571     Status error = platform_sp->LaunchProcess(info);
572     launch_info.set_ref(info);
573     return error;
574   });
575 }
576 
577 SBError SBPlatform::Kill(const lldb::pid_t pid) {
578   LLDB_INSTRUMENT_VA(this, pid);
579   return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
580     return platform_sp->KillProcess(pid);
581   });
582 }
583 
584 SBError SBPlatform::ExecuteConnected(
585     const std::function<Status(const lldb::PlatformSP &)> &func) {
586   SBError sb_error;
587   const auto platform_sp(GetSP());
588   if (platform_sp) {
589     if (platform_sp->IsConnected())
590       sb_error.ref() = func(platform_sp);
591     else
592       sb_error.SetErrorString("not connected");
593   } else
594     sb_error.SetErrorString("invalid platform");
595 
596   return sb_error;
597 }
598 
599 SBError SBPlatform::MakeDirectory(const char *path, uint32_t file_permissions) {
600   LLDB_INSTRUMENT_VA(this, path, file_permissions);
601 
602   SBError sb_error;
603   PlatformSP platform_sp(GetSP());
604   if (platform_sp) {
605     sb_error.ref() =
606         platform_sp->MakeDirectory(FileSpec(path), file_permissions);
607   } else {
608     sb_error.SetErrorString("invalid platform");
609   }
610   return sb_error;
611 }
612 
613 uint32_t SBPlatform::GetFilePermissions(const char *path) {
614   LLDB_INSTRUMENT_VA(this, path);
615 
616   PlatformSP platform_sp(GetSP());
617   if (platform_sp) {
618     uint32_t file_permissions = 0;
619     platform_sp->GetFilePermissions(FileSpec(path), file_permissions);
620     return file_permissions;
621   }
622   return 0;
623 }
624 
625 SBError SBPlatform::SetFilePermissions(const char *path,
626                                        uint32_t file_permissions) {
627   LLDB_INSTRUMENT_VA(this, path, file_permissions);
628 
629   SBError sb_error;
630   PlatformSP platform_sp(GetSP());
631   if (platform_sp) {
632     sb_error.ref() =
633         platform_sp->SetFilePermissions(FileSpec(path), file_permissions);
634   } else {
635     sb_error.SetErrorString("invalid platform");
636   }
637   return sb_error;
638 }
639 
640 SBUnixSignals SBPlatform::GetUnixSignals() const {
641   LLDB_INSTRUMENT_VA(this);
642 
643   if (auto platform_sp = GetSP())
644     return SBUnixSignals{platform_sp};
645 
646   return SBUnixSignals();
647 }
648 
649 SBEnvironment SBPlatform::GetEnvironment() {
650   LLDB_INSTRUMENT_VA(this);
651   PlatformSP platform_sp(GetSP());
652 
653   if (platform_sp) {
654     return SBEnvironment(platform_sp->GetEnvironment());
655   }
656 
657   return SBEnvironment();
658 }
659 
660 SBError SBPlatform::SetLocateModuleCallback(
661     lldb::SBPlatformLocateModuleCallback callback, void *callback_baton) {
662   LLDB_INSTRUMENT_VA(this, callback, callback_baton);
663   PlatformSP platform_sp(GetSP());
664   if (!platform_sp)
665     return SBError("invalid platform");
666 
667   if (!callback) {
668     // Clear the callback.
669     platform_sp->SetLocateModuleCallback(nullptr);
670     return SBError();
671   }
672 
673   // Platform.h does not accept lldb::SBPlatformLocateModuleCallback directly
674   // because of the SBModuleSpec and SBFileSpec dependencies. Use a lambda to
675   // convert ModuleSpec/FileSpec <--> SBModuleSpec/SBFileSpec for the callback
676   // arguments.
677   platform_sp->SetLocateModuleCallback(
678       [callback, callback_baton](const ModuleSpec &module_spec,
679                                  FileSpec &module_file_spec,
680                                  FileSpec &symbol_file_spec) {
681         SBModuleSpec module_spec_sb(module_spec);
682         SBFileSpec module_file_spec_sb;
683         SBFileSpec symbol_file_spec_sb;
684 
685         SBError error = callback(callback_baton, module_spec_sb,
686                                  module_file_spec_sb, symbol_file_spec_sb);
687 
688         if (error.Success()) {
689           module_file_spec = module_file_spec_sb.ref();
690           symbol_file_spec = symbol_file_spec_sb.ref();
691         }
692 
693         return error.ref();
694       });
695   return SBError();
696 }
697