Platform.cpp revision 280031
1//===-- Platform.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/Target/Platform.h" 11 12// C Includes 13// C++ Includes 14// Other libraries and framework includes 15// Project includes 16#include "lldb/Breakpoint/BreakpointIDList.h" 17#include "lldb/Core/Error.h" 18#include "lldb/Core/Log.h" 19#include "lldb/Core/ModuleSpec.h" 20#include "lldb/Core/PluginManager.h" 21#include "lldb/Host/FileSpec.h" 22#include "lldb/Host/FileSystem.h" 23#include "lldb/Host/Host.h" 24#include "lldb/Host/HostInfo.h" 25#include "lldb/Target/Process.h" 26#include "lldb/Target/Target.h" 27#include "lldb/Utility/Utils.h" 28 29using namespace lldb; 30using namespace lldb_private; 31 32// Use a singleton function for g_local_platform_sp to avoid init 33// constructors since LLDB is often part of a shared library 34static PlatformSP& 35GetHostPlatformSP () 36{ 37 static PlatformSP g_platform_sp; 38 return g_platform_sp; 39} 40 41const char * 42Platform::GetHostPlatformName () 43{ 44 return "host"; 45} 46 47//------------------------------------------------------------------ 48/// Get the native host platform plug-in. 49/// 50/// There should only be one of these for each host that LLDB runs 51/// upon that should be statically compiled in and registered using 52/// preprocessor macros or other similar build mechanisms. 53/// 54/// This platform will be used as the default platform when launching 55/// or attaching to processes unless another platform is specified. 56//------------------------------------------------------------------ 57PlatformSP 58Platform::GetHostPlatform () 59{ 60 return GetHostPlatformSP (); 61} 62 63static std::vector<PlatformSP> & 64GetPlatformList() 65{ 66 static std::vector<PlatformSP> g_platform_list; 67 return g_platform_list; 68} 69 70static Mutex & 71GetPlatformListMutex () 72{ 73 static Mutex g_mutex(Mutex::eMutexTypeRecursive); 74 return g_mutex; 75} 76 77void 78Platform::SetHostPlatform (const lldb::PlatformSP &platform_sp) 79{ 80 // The native platform should use its static void Platform::Initialize() 81 // function to register itself as the native platform. 82 GetHostPlatformSP () = platform_sp; 83 84 if (platform_sp) 85 { 86 Mutex::Locker locker(GetPlatformListMutex ()); 87 GetPlatformList().push_back(platform_sp); 88 } 89} 90 91Error 92Platform::GetFileWithUUID (const FileSpec &platform_file, 93 const UUID *uuid_ptr, 94 FileSpec &local_file) 95{ 96 // Default to the local case 97 local_file = platform_file; 98 return Error(); 99} 100 101FileSpecList 102Platform::LocateExecutableScriptingResources (Target *target, Module &module, Stream* feedback_stream) 103{ 104 return FileSpecList(); 105} 106 107//PlatformSP 108//Platform::FindPlugin (Process *process, const ConstString &plugin_name) 109//{ 110// PlatformCreateInstance create_callback = NULL; 111// if (plugin_name) 112// { 113// create_callback = PluginManager::GetPlatformCreateCallbackForPluginName (plugin_name); 114// if (create_callback) 115// { 116// ArchSpec arch; 117// if (process) 118// { 119// arch = process->GetTarget().GetArchitecture(); 120// } 121// PlatformSP platform_sp(create_callback(process, &arch)); 122// if (platform_sp) 123// return platform_sp; 124// } 125// } 126// else 127// { 128// for (uint32_t idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex(idx)) != NULL; ++idx) 129// { 130// PlatformSP platform_sp(create_callback(process, nullptr)); 131// if (platform_sp) 132// return platform_sp; 133// } 134// } 135// return PlatformSP(); 136//} 137 138Error 139Platform::GetSharedModule (const ModuleSpec &module_spec, 140 ModuleSP &module_sp, 141 const FileSpecList *module_search_paths_ptr, 142 ModuleSP *old_module_sp_ptr, 143 bool *did_create_ptr) 144{ 145 // Don't do any path remapping for the default implementation 146 // of the platform GetSharedModule function, just call through 147 // to our static ModuleList function. Platform subclasses that 148 // implement remote debugging, might have a developer kits 149 // installed that have cached versions of the files for the 150 // remote target, or might implement a download and cache 151 // locally implementation. 152 const bool always_create = false; 153 return ModuleList::GetSharedModule (module_spec, 154 module_sp, 155 module_search_paths_ptr, 156 old_module_sp_ptr, 157 did_create_ptr, 158 always_create); 159} 160 161PlatformSP 162Platform::Find (const ConstString &name) 163{ 164 if (name) 165 { 166 static ConstString g_host_platform_name ("host"); 167 if (name == g_host_platform_name) 168 return GetHostPlatform(); 169 170 Mutex::Locker locker(GetPlatformListMutex ()); 171 for (const auto &platform_sp : GetPlatformList()) 172 { 173 if (platform_sp->GetName() == name) 174 return platform_sp; 175 } 176 } 177 return PlatformSP(); 178} 179 180PlatformSP 181Platform::Create (const ConstString &name, Error &error) 182{ 183 PlatformCreateInstance create_callback = NULL; 184 lldb::PlatformSP platform_sp; 185 if (name) 186 { 187 static ConstString g_host_platform_name ("host"); 188 if (name == g_host_platform_name) 189 return GetHostPlatform(); 190 191 create_callback = PluginManager::GetPlatformCreateCallbackForPluginName (name); 192 if (create_callback) 193 platform_sp = create_callback(true, NULL); 194 else 195 error.SetErrorStringWithFormat ("unable to find a plug-in for the platform named \"%s\"", name.GetCString()); 196 } 197 else 198 error.SetErrorString ("invalid platform name"); 199 200 if (platform_sp) 201 { 202 Mutex::Locker locker(GetPlatformListMutex ()); 203 GetPlatformList().push_back(platform_sp); 204 } 205 206 return platform_sp; 207} 208 209 210PlatformSP 211Platform::Create (const ArchSpec &arch, ArchSpec *platform_arch_ptr, Error &error) 212{ 213 lldb::PlatformSP platform_sp; 214 if (arch.IsValid()) 215 { 216 // Scope for locker 217 { 218 // First try exact arch matches across all platforms already created 219 Mutex::Locker locker(GetPlatformListMutex ()); 220 for (const auto &platform_sp : GetPlatformList()) 221 { 222 if (platform_sp->IsCompatibleArchitecture(arch, true, platform_arch_ptr)) 223 return platform_sp; 224 } 225 226 // Next try compatible arch matches across all platforms already created 227 for (const auto &platform_sp : GetPlatformList()) 228 { 229 if (platform_sp->IsCompatibleArchitecture(arch, false, platform_arch_ptr)) 230 return platform_sp; 231 } 232 } 233 234 PlatformCreateInstance create_callback; 235 // First try exact arch matches across all platform plug-ins 236 uint32_t idx; 237 for (idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex (idx)); ++idx) 238 { 239 if (create_callback) 240 { 241 platform_sp = create_callback(false, &arch); 242 if (platform_sp && platform_sp->IsCompatibleArchitecture(arch, true, platform_arch_ptr)) 243 { 244 Mutex::Locker locker(GetPlatformListMutex ()); 245 GetPlatformList().push_back(platform_sp); 246 return platform_sp; 247 } 248 } 249 } 250 // Next try compatible arch matches across all platform plug-ins 251 for (idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex (idx)); ++idx) 252 { 253 if (create_callback) 254 { 255 platform_sp = create_callback(false, &arch); 256 if (platform_sp && platform_sp->IsCompatibleArchitecture(arch, false, platform_arch_ptr)) 257 { 258 Mutex::Locker locker(GetPlatformListMutex ()); 259 GetPlatformList().push_back(platform_sp); 260 return platform_sp; 261 } 262 } 263 } 264 } 265 else 266 error.SetErrorString ("invalid platform name"); 267 if (platform_arch_ptr) 268 platform_arch_ptr->Clear(); 269 platform_sp.reset(); 270 return platform_sp; 271} 272 273//------------------------------------------------------------------ 274/// Default Constructor 275//------------------------------------------------------------------ 276Platform::Platform (bool is_host) : 277 m_is_host (is_host), 278 m_os_version_set_while_connected (false), 279 m_system_arch_set_while_connected (false), 280 m_sdk_sysroot (), 281 m_sdk_build (), 282 m_working_dir (), 283 m_remote_url (), 284 m_name (), 285 m_major_os_version (UINT32_MAX), 286 m_minor_os_version (UINT32_MAX), 287 m_update_os_version (UINT32_MAX), 288 m_system_arch(), 289 m_uid_map_mutex (Mutex::eMutexTypeNormal), 290 m_gid_map_mutex (Mutex::eMutexTypeNormal), 291 m_uid_map(), 292 m_gid_map(), 293 m_max_uid_name_len (0), 294 m_max_gid_name_len (0), 295 m_supports_rsync (false), 296 m_rsync_opts (), 297 m_rsync_prefix (), 298 m_supports_ssh (false), 299 m_ssh_opts (), 300 m_ignores_remote_hostname (false), 301 m_trap_handlers(), 302 m_calculated_trap_handlers (false), 303 m_trap_handler_mutex() 304{ 305 Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); 306 if (log) 307 log->Printf ("%p Platform::Platform()", static_cast<void*>(this)); 308} 309 310//------------------------------------------------------------------ 311/// Destructor. 312/// 313/// The destructor is virtual since this class is designed to be 314/// inherited from by the plug-in instance. 315//------------------------------------------------------------------ 316Platform::~Platform() 317{ 318 Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); 319 if (log) 320 log->Printf ("%p Platform::~Platform()", static_cast<void*>(this)); 321} 322 323void 324Platform::GetStatus (Stream &strm) 325{ 326 uint32_t major = UINT32_MAX; 327 uint32_t minor = UINT32_MAX; 328 uint32_t update = UINT32_MAX; 329 std::string s; 330 strm.Printf (" Platform: %s\n", GetPluginName().GetCString()); 331 332 ArchSpec arch (GetSystemArchitecture()); 333 if (arch.IsValid()) 334 { 335 if (!arch.GetTriple().str().empty()) 336 strm.Printf(" Triple: %s\n", arch.GetTriple().str().c_str()); 337 } 338 339 if (GetOSVersion(major, minor, update)) 340 { 341 strm.Printf("OS Version: %u", major); 342 if (minor != UINT32_MAX) 343 strm.Printf(".%u", minor); 344 if (update != UINT32_MAX) 345 strm.Printf(".%u", update); 346 347 if (GetOSBuildString (s)) 348 strm.Printf(" (%s)", s.c_str()); 349 350 strm.EOL(); 351 } 352 353 if (GetOSKernelDescription (s)) 354 strm.Printf(" Kernel: %s\n", s.c_str()); 355 356 if (IsHost()) 357 { 358 strm.Printf(" Hostname: %s\n", GetHostname()); 359 } 360 else 361 { 362 const bool is_connected = IsConnected(); 363 if (is_connected) 364 strm.Printf(" Hostname: %s\n", GetHostname()); 365 strm.Printf(" Connected: %s\n", is_connected ? "yes" : "no"); 366 } 367 368 if (GetWorkingDirectory()) 369 { 370 strm.Printf("WorkingDir: %s\n", GetWorkingDirectory().GetCString()); 371 } 372 if (!IsConnected()) 373 return; 374 375 std::string specific_info(GetPlatformSpecificConnectionInformation()); 376 377 if (specific_info.empty() == false) 378 strm.Printf("Platform-specific connection: %s\n", specific_info.c_str()); 379} 380 381 382bool 383Platform::GetOSVersion (uint32_t &major, 384 uint32_t &minor, 385 uint32_t &update) 386{ 387 bool success = m_major_os_version != UINT32_MAX; 388 if (IsHost()) 389 { 390 if (!success) 391 { 392 // We have a local host platform 393 success = HostInfo::GetOSVersion(m_major_os_version, m_minor_os_version, m_update_os_version); 394 m_os_version_set_while_connected = success; 395 } 396 } 397 else 398 { 399 // We have a remote platform. We can only fetch the remote 400 // OS version if we are connected, and we don't want to do it 401 // more than once. 402 403 const bool is_connected = IsConnected(); 404 405 bool fetch = false; 406 if (success) 407 { 408 // We have valid OS version info, check to make sure it wasn't 409 // manually set prior to connecting. If it was manually set prior 410 // to connecting, then lets fetch the actual OS version info 411 // if we are now connected. 412 if (is_connected && !m_os_version_set_while_connected) 413 fetch = true; 414 } 415 else 416 { 417 // We don't have valid OS version info, fetch it if we are connected 418 fetch = is_connected; 419 } 420 421 if (fetch) 422 { 423 success = GetRemoteOSVersion (); 424 m_os_version_set_while_connected = success; 425 } 426 } 427 428 if (success) 429 { 430 major = m_major_os_version; 431 minor = m_minor_os_version; 432 update = m_update_os_version; 433 } 434 return success; 435} 436 437bool 438Platform::GetOSBuildString (std::string &s) 439{ 440 s.clear(); 441 442 if (IsHost()) 443#if !defined(__linux__) 444 return HostInfo::GetOSBuildString(s); 445#else 446 return false; 447#endif 448 else 449 return GetRemoteOSBuildString (s); 450} 451 452bool 453Platform::GetOSKernelDescription (std::string &s) 454{ 455 if (IsHost()) 456#if !defined(__linux__) 457 return HostInfo::GetOSKernelDescription(s); 458#else 459 return false; 460#endif 461 else 462 return GetRemoteOSKernelDescription (s); 463} 464 465void 466Platform::AddClangModuleCompilationOptions (std::vector<std::string> &options) 467{ 468 std::vector<std::string> default_compilation_options = 469 { 470 "-x", "c++", "-Xclang", "-nostdsysteminc", "-Xclang", "-nostdsysteminc" 471 }; 472 473 options.insert(options.end(), 474 default_compilation_options.begin(), 475 default_compilation_options.end()); 476} 477 478 479ConstString 480Platform::GetWorkingDirectory () 481{ 482 if (IsHost()) 483 { 484 char cwd[PATH_MAX]; 485 if (getcwd(cwd, sizeof(cwd))) 486 return ConstString(cwd); 487 else 488 return ConstString(); 489 } 490 else 491 { 492 if (!m_working_dir) 493 m_working_dir = GetRemoteWorkingDirectory(); 494 return m_working_dir; 495 } 496} 497 498 499struct RecurseCopyBaton 500{ 501 const FileSpec& dst; 502 Platform *platform_ptr; 503 Error error; 504}; 505 506 507static FileSpec::EnumerateDirectoryResult 508RecurseCopy_Callback (void *baton, 509 FileSpec::FileType file_type, 510 const FileSpec &src) 511{ 512 RecurseCopyBaton* rc_baton = (RecurseCopyBaton*)baton; 513 switch (file_type) 514 { 515 case FileSpec::eFileTypePipe: 516 case FileSpec::eFileTypeSocket: 517 // we have no way to copy pipes and sockets - ignore them and continue 518 return FileSpec::eEnumerateDirectoryResultNext; 519 break; 520 521 case FileSpec::eFileTypeDirectory: 522 { 523 // make the new directory and get in there 524 FileSpec dst_dir = rc_baton->dst; 525 if (!dst_dir.GetFilename()) 526 dst_dir.GetFilename() = src.GetLastPathComponent(); 527 std::string dst_dir_path (dst_dir.GetPath()); 528 Error error = rc_baton->platform_ptr->MakeDirectory(dst_dir_path.c_str(), lldb::eFilePermissionsDirectoryDefault); 529 if (error.Fail()) 530 { 531 rc_baton->error.SetErrorStringWithFormat("unable to setup directory %s on remote end", dst_dir_path.c_str()); 532 return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out 533 } 534 535 // now recurse 536 std::string src_dir_path (src.GetPath()); 537 538 // Make a filespec that only fills in the directory of a FileSpec so 539 // when we enumerate we can quickly fill in the filename for dst copies 540 FileSpec recurse_dst; 541 recurse_dst.GetDirectory().SetCString(dst_dir.GetPath().c_str()); 542 RecurseCopyBaton rc_baton2 = { recurse_dst, rc_baton->platform_ptr, Error() }; 543 FileSpec::EnumerateDirectory(src_dir_path.c_str(), true, true, true, RecurseCopy_Callback, &rc_baton2); 544 if (rc_baton2.error.Fail()) 545 { 546 rc_baton->error.SetErrorString(rc_baton2.error.AsCString()); 547 return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out 548 } 549 return FileSpec::eEnumerateDirectoryResultNext; 550 } 551 break; 552 553 case FileSpec::eFileTypeSymbolicLink: 554 { 555 // copy the file and keep going 556 FileSpec dst_file = rc_baton->dst; 557 if (!dst_file.GetFilename()) 558 dst_file.GetFilename() = src.GetFilename(); 559 560 char buf[PATH_MAX]; 561 562 rc_baton->error = FileSystem::Readlink(src.GetPath().c_str(), buf, sizeof(buf)); 563 564 if (rc_baton->error.Fail()) 565 return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out 566 567 rc_baton->error = rc_baton->platform_ptr->CreateSymlink(dst_file.GetPath().c_str(), buf); 568 569 if (rc_baton->error.Fail()) 570 return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out 571 572 return FileSpec::eEnumerateDirectoryResultNext; 573 } 574 break; 575 case FileSpec::eFileTypeRegular: 576 { 577 // copy the file and keep going 578 FileSpec dst_file = rc_baton->dst; 579 if (!dst_file.GetFilename()) 580 dst_file.GetFilename() = src.GetFilename(); 581 Error err = rc_baton->platform_ptr->PutFile(src, dst_file); 582 if (err.Fail()) 583 { 584 rc_baton->error.SetErrorString(err.AsCString()); 585 return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out 586 } 587 return FileSpec::eEnumerateDirectoryResultNext; 588 } 589 break; 590 591 case FileSpec::eFileTypeInvalid: 592 case FileSpec::eFileTypeOther: 593 case FileSpec::eFileTypeUnknown: 594 rc_baton->error.SetErrorStringWithFormat("invalid file detected during copy: %s", src.GetPath().c_str()); 595 return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out 596 break; 597 } 598 llvm_unreachable("Unhandled FileSpec::FileType!"); 599} 600 601Error 602Platform::Install (const FileSpec& src, const FileSpec& dst) 603{ 604 Error error; 605 606 Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); 607 if (log) 608 log->Printf ("Platform::Install (src='%s', dst='%s')", src.GetPath().c_str(), dst.GetPath().c_str()); 609 FileSpec fixed_dst(dst); 610 611 if (!fixed_dst.GetFilename()) 612 fixed_dst.GetFilename() = src.GetFilename(); 613 614 ConstString working_dir = GetWorkingDirectory(); 615 616 if (dst) 617 { 618 if (dst.GetDirectory()) 619 { 620 const char first_dst_dir_char = dst.GetDirectory().GetCString()[0]; 621 if (first_dst_dir_char == '/' || first_dst_dir_char == '\\') 622 { 623 fixed_dst.GetDirectory() = dst.GetDirectory(); 624 } 625 // If the fixed destination file doesn't have a directory yet, 626 // then we must have a relative path. We will resolve this relative 627 // path against the platform's working directory 628 if (!fixed_dst.GetDirectory()) 629 { 630 FileSpec relative_spec; 631 std::string path; 632 if (working_dir) 633 { 634 relative_spec.SetFile(working_dir.GetCString(), false); 635 relative_spec.AppendPathComponent(dst.GetPath().c_str()); 636 fixed_dst.GetDirectory() = relative_spec.GetDirectory(); 637 } 638 else 639 { 640 error.SetErrorStringWithFormat("platform working directory must be valid for relative path '%s'", dst.GetPath().c_str()); 641 return error; 642 } 643 } 644 } 645 else 646 { 647 if (working_dir) 648 { 649 fixed_dst.GetDirectory() = working_dir; 650 } 651 else 652 { 653 error.SetErrorStringWithFormat("platform working directory must be valid for relative path '%s'", dst.GetPath().c_str()); 654 return error; 655 } 656 } 657 } 658 else 659 { 660 if (working_dir) 661 { 662 fixed_dst.GetDirectory() = working_dir; 663 } 664 else 665 { 666 error.SetErrorStringWithFormat("platform working directory must be valid when destination directory is empty"); 667 return error; 668 } 669 } 670 671 if (log) 672 log->Printf ("Platform::Install (src='%s', dst='%s') fixed_dst='%s'", src.GetPath().c_str(), dst.GetPath().c_str(), fixed_dst.GetPath().c_str()); 673 674 if (GetSupportsRSync()) 675 { 676 error = PutFile(src, dst); 677 } 678 else 679 { 680 switch (src.GetFileType()) 681 { 682 case FileSpec::eFileTypeDirectory: 683 { 684 if (GetFileExists (fixed_dst)) 685 Unlink (fixed_dst.GetPath().c_str()); 686 uint32_t permissions = src.GetPermissions(); 687 if (permissions == 0) 688 permissions = eFilePermissionsDirectoryDefault; 689 std::string dst_dir_path(fixed_dst.GetPath()); 690 error = MakeDirectory(dst_dir_path.c_str(), permissions); 691 if (error.Success()) 692 { 693 // Make a filespec that only fills in the directory of a FileSpec so 694 // when we enumerate we can quickly fill in the filename for dst copies 695 FileSpec recurse_dst; 696 recurse_dst.GetDirectory().SetCString(dst_dir_path.c_str()); 697 std::string src_dir_path (src.GetPath()); 698 RecurseCopyBaton baton = { recurse_dst, this, Error() }; 699 FileSpec::EnumerateDirectory(src_dir_path.c_str(), true, true, true, RecurseCopy_Callback, &baton); 700 return baton.error; 701 } 702 } 703 break; 704 705 case FileSpec::eFileTypeRegular: 706 if (GetFileExists (fixed_dst)) 707 Unlink (fixed_dst.GetPath().c_str()); 708 error = PutFile(src, fixed_dst); 709 break; 710 711 case FileSpec::eFileTypeSymbolicLink: 712 { 713 if (GetFileExists (fixed_dst)) 714 Unlink (fixed_dst.GetPath().c_str()); 715 char buf[PATH_MAX]; 716 error = FileSystem::Readlink(src.GetPath().c_str(), buf, sizeof(buf)); 717 if (error.Success()) 718 error = CreateSymlink(dst.GetPath().c_str(), buf); 719 } 720 break; 721 case FileSpec::eFileTypePipe: 722 error.SetErrorString("platform install doesn't handle pipes"); 723 break; 724 case FileSpec::eFileTypeSocket: 725 error.SetErrorString("platform install doesn't handle sockets"); 726 break; 727 case FileSpec::eFileTypeInvalid: 728 case FileSpec::eFileTypeUnknown: 729 case FileSpec::eFileTypeOther: 730 error.SetErrorString("platform install doesn't handle non file or directory items"); 731 break; 732 } 733 } 734 return error; 735} 736 737bool 738Platform::SetWorkingDirectory (const ConstString &path) 739{ 740 if (IsHost()) 741 { 742 Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); 743 if (log) 744 log->Printf("Platform::SetWorkingDirectory('%s')", path.GetCString()); 745#ifdef _WIN32 746 // Not implemented on Windows 747 return false; 748#else 749 if (path) 750 { 751 if (chdir(path.GetCString()) == 0) 752 return true; 753 } 754 return false; 755#endif 756 } 757 else 758 { 759 m_working_dir.Clear(); 760 return SetRemoteWorkingDirectory(path); 761 } 762} 763 764Error 765Platform::MakeDirectory (const char *path, uint32_t permissions) 766{ 767 if (IsHost()) 768 return FileSystem::MakeDirectory(path, permissions); 769 else 770 { 771 Error error; 772 error.SetErrorStringWithFormat("remote platform %s doesn't support %s", GetPluginName().GetCString(), __PRETTY_FUNCTION__); 773 return error; 774 } 775} 776 777Error 778Platform::GetFilePermissions (const char *path, uint32_t &file_permissions) 779{ 780 if (IsHost()) 781 return FileSystem::GetFilePermissions(path, file_permissions); 782 else 783 { 784 Error error; 785 error.SetErrorStringWithFormat("remote platform %s doesn't support %s", GetPluginName().GetCString(), __PRETTY_FUNCTION__); 786 return error; 787 } 788} 789 790Error 791Platform::SetFilePermissions (const char *path, uint32_t file_permissions) 792{ 793 if (IsHost()) 794 return FileSystem::SetFilePermissions(path, file_permissions); 795 else 796 { 797 Error error; 798 error.SetErrorStringWithFormat("remote platform %s doesn't support %s", GetPluginName().GetCString(), __PRETTY_FUNCTION__); 799 return error; 800 } 801} 802 803ConstString 804Platform::GetName () 805{ 806 return GetPluginName(); 807} 808 809const char * 810Platform::GetHostname () 811{ 812 if (IsHost()) 813 return "127.0.0.1"; 814 815 if (m_name.empty()) 816 return NULL; 817 return m_name.c_str(); 818} 819 820bool 821Platform::SetRemoteWorkingDirectory(const ConstString &path) 822{ 823 Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); 824 if (log) 825 log->Printf("Platform::SetRemoteWorkingDirectory('%s')", path.GetCString()); 826 m_working_dir = path; 827 return true; 828} 829 830const char * 831Platform::GetUserName (uint32_t uid) 832{ 833#if !defined(LLDB_DISABLE_POSIX) 834 const char *user_name = GetCachedUserName(uid); 835 if (user_name) 836 return user_name; 837 if (IsHost()) 838 { 839 std::string name; 840 if (HostInfo::LookupUserName(uid, name)) 841 return SetCachedUserName (uid, name.c_str(), name.size()); 842 } 843#endif 844 return NULL; 845} 846 847const char * 848Platform::GetGroupName (uint32_t gid) 849{ 850#if !defined(LLDB_DISABLE_POSIX) 851 const char *group_name = GetCachedGroupName(gid); 852 if (group_name) 853 return group_name; 854 if (IsHost()) 855 { 856 std::string name; 857 if (HostInfo::LookupGroupName(gid, name)) 858 return SetCachedGroupName (gid, name.c_str(), name.size()); 859 } 860#endif 861 return NULL; 862} 863 864bool 865Platform::SetOSVersion (uint32_t major, 866 uint32_t minor, 867 uint32_t update) 868{ 869 if (IsHost()) 870 { 871 // We don't need anyone setting the OS version for the host platform, 872 // we should be able to figure it out by calling HostInfo::GetOSVersion(...). 873 return false; 874 } 875 else 876 { 877 // We have a remote platform, allow setting the target OS version if 878 // we aren't connected, since if we are connected, we should be able to 879 // request the remote OS version from the connected platform. 880 if (IsConnected()) 881 return false; 882 else 883 { 884 // We aren't connected and we might want to set the OS version 885 // ahead of time before we connect so we can peruse files and 886 // use a local SDK or PDK cache of support files to disassemble 887 // or do other things. 888 m_major_os_version = major; 889 m_minor_os_version = minor; 890 m_update_os_version = update; 891 return true; 892 } 893 } 894 return false; 895} 896 897 898Error 899Platform::ResolveExecutable (const ModuleSpec &module_spec, 900 lldb::ModuleSP &exe_module_sp, 901 const FileSpecList *module_search_paths_ptr) 902{ 903 Error error; 904 if (module_spec.GetFileSpec().Exists()) 905 { 906 if (module_spec.GetArchitecture().IsValid()) 907 { 908 error = ModuleList::GetSharedModule (module_spec, 909 exe_module_sp, 910 module_search_paths_ptr, 911 NULL, 912 NULL); 913 } 914 else 915 { 916 // No valid architecture was specified, ask the platform for 917 // the architectures that we should be using (in the correct order) 918 // and see if we can find a match that way 919 ModuleSpec arch_module_spec(module_spec); 920 for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, arch_module_spec.GetArchitecture()); ++idx) 921 { 922 error = ModuleList::GetSharedModule (arch_module_spec, 923 exe_module_sp, 924 module_search_paths_ptr, 925 NULL, 926 NULL); 927 // Did we find an executable using one of the 928 if (error.Success() && exe_module_sp) 929 break; 930 } 931 } 932 } 933 else 934 { 935 error.SetErrorStringWithFormat ("'%s' does not exist", 936 module_spec.GetFileSpec().GetPath().c_str()); 937 } 938 return error; 939} 940 941Error 942Platform::ResolveSymbolFile (Target &target, 943 const ModuleSpec &sym_spec, 944 FileSpec &sym_file) 945{ 946 Error error; 947 if (sym_spec.GetSymbolFileSpec().Exists()) 948 sym_file = sym_spec.GetSymbolFileSpec(); 949 else 950 error.SetErrorString("unable to resolve symbol file"); 951 return error; 952 953} 954 955 956 957bool 958Platform::ResolveRemotePath (const FileSpec &platform_path, 959 FileSpec &resolved_platform_path) 960{ 961 resolved_platform_path = platform_path; 962 return resolved_platform_path.ResolvePath(); 963} 964 965 966const ArchSpec & 967Platform::GetSystemArchitecture() 968{ 969 if (IsHost()) 970 { 971 if (!m_system_arch.IsValid()) 972 { 973 // We have a local host platform 974 m_system_arch = HostInfo::GetArchitecture(); 975 m_system_arch_set_while_connected = m_system_arch.IsValid(); 976 } 977 } 978 else 979 { 980 // We have a remote platform. We can only fetch the remote 981 // system architecture if we are connected, and we don't want to do it 982 // more than once. 983 984 const bool is_connected = IsConnected(); 985 986 bool fetch = false; 987 if (m_system_arch.IsValid()) 988 { 989 // We have valid OS version info, check to make sure it wasn't 990 // manually set prior to connecting. If it was manually set prior 991 // to connecting, then lets fetch the actual OS version info 992 // if we are now connected. 993 if (is_connected && !m_system_arch_set_while_connected) 994 fetch = true; 995 } 996 else 997 { 998 // We don't have valid OS version info, fetch it if we are connected 999 fetch = is_connected; 1000 } 1001 1002 if (fetch) 1003 { 1004 m_system_arch = GetRemoteSystemArchitecture (); 1005 m_system_arch_set_while_connected = m_system_arch.IsValid(); 1006 } 1007 } 1008 return m_system_arch; 1009} 1010 1011 1012Error 1013Platform::ConnectRemote (Args& args) 1014{ 1015 Error error; 1016 if (IsHost()) 1017 error.SetErrorStringWithFormat ("The currently selected platform (%s) is the host platform and is always connected.", GetPluginName().GetCString()); 1018 else 1019 error.SetErrorStringWithFormat ("Platform::ConnectRemote() is not supported by %s", GetPluginName().GetCString()); 1020 return error; 1021} 1022 1023Error 1024Platform::DisconnectRemote () 1025{ 1026 Error error; 1027 if (IsHost()) 1028 error.SetErrorStringWithFormat ("The currently selected platform (%s) is the host platform and is always connected.", GetPluginName().GetCString()); 1029 else 1030 error.SetErrorStringWithFormat ("Platform::DisconnectRemote() is not supported by %s", GetPluginName().GetCString()); 1031 return error; 1032} 1033 1034bool 1035Platform::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) 1036{ 1037 // Take care of the host case so that each subclass can just 1038 // call this function to get the host functionality. 1039 if (IsHost()) 1040 return Host::GetProcessInfo (pid, process_info); 1041 return false; 1042} 1043 1044uint32_t 1045Platform::FindProcesses (const ProcessInstanceInfoMatch &match_info, 1046 ProcessInstanceInfoList &process_infos) 1047{ 1048 // Take care of the host case so that each subclass can just 1049 // call this function to get the host functionality. 1050 uint32_t match_count = 0; 1051 if (IsHost()) 1052 match_count = Host::FindProcesses (match_info, process_infos); 1053 return match_count; 1054} 1055 1056 1057Error 1058Platform::LaunchProcess (ProcessLaunchInfo &launch_info) 1059{ 1060 Error error; 1061 Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); 1062 if (log) 1063 log->Printf ("Platform::%s()", __FUNCTION__); 1064 1065 // Take care of the host case so that each subclass can just 1066 // call this function to get the host functionality. 1067 if (IsHost()) 1068 { 1069 if (::getenv ("LLDB_LAUNCH_FLAG_LAUNCH_IN_TTY")) 1070 launch_info.GetFlags().Set (eLaunchFlagLaunchInTTY); 1071 1072 if (launch_info.GetFlags().Test (eLaunchFlagLaunchInShell)) 1073 { 1074 const bool is_localhost = true; 1075 const bool will_debug = launch_info.GetFlags().Test(eLaunchFlagDebug); 1076 const bool first_arg_is_full_shell_command = false; 1077 uint32_t num_resumes = GetResumeCountForLaunchInfo (launch_info); 1078 if (log) 1079 { 1080 const FileSpec &shell = launch_info.GetShell(); 1081 const char *shell_str = (shell) ? shell.GetPath().c_str() : "<null>"; 1082 log->Printf ("Platform::%s GetResumeCountForLaunchInfo() returned %" PRIu32 ", shell is '%s'", 1083 __FUNCTION__, 1084 num_resumes, 1085 shell_str); 1086 } 1087 1088 if (!launch_info.ConvertArgumentsForLaunchingInShell (error, 1089 is_localhost, 1090 will_debug, 1091 first_arg_is_full_shell_command, 1092 num_resumes)) 1093 return error; 1094 } 1095 1096 if (log) 1097 log->Printf ("Platform::%s final launch_info resume count: %" PRIu32, __FUNCTION__, launch_info.GetResumeCount ()); 1098 1099 error = Host::LaunchProcess (launch_info); 1100 } 1101 else 1102 error.SetErrorString ("base lldb_private::Platform class can't launch remote processes"); 1103 return error; 1104} 1105 1106lldb::ProcessSP 1107Platform::DebugProcess (ProcessLaunchInfo &launch_info, 1108 Debugger &debugger, 1109 Target *target, // Can be NULL, if NULL create a new target, else use existing one 1110 Error &error) 1111{ 1112 Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); 1113 if (log) 1114 log->Printf ("Platform::%s entered (target %p)", __FUNCTION__, static_cast<void*>(target)); 1115 1116 ProcessSP process_sp; 1117 // Make sure we stop at the entry point 1118 launch_info.GetFlags ().Set (eLaunchFlagDebug); 1119 // We always launch the process we are going to debug in a separate process 1120 // group, since then we can handle ^C interrupts ourselves w/o having to worry 1121 // about the target getting them as well. 1122 launch_info.SetLaunchInSeparateProcessGroup(true); 1123 1124 error = LaunchProcess (launch_info); 1125 if (error.Success()) 1126 { 1127 if (log) 1128 log->Printf ("Platform::%s LaunchProcess() call succeeded (pid=%" PRIu64 ")", __FUNCTION__, launch_info.GetProcessID ()); 1129 if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) 1130 { 1131 ProcessAttachInfo attach_info (launch_info); 1132 process_sp = Attach (attach_info, debugger, target, error); 1133 if (process_sp) 1134 { 1135 if (log) 1136 log->Printf ("Platform::%s Attach() succeeded, Process plugin: %s", __FUNCTION__, process_sp->GetPluginName ().AsCString ()); 1137 launch_info.SetHijackListener(attach_info.GetHijackListener()); 1138 1139 // Since we attached to the process, it will think it needs to detach 1140 // if the process object just goes away without an explicit call to 1141 // Process::Kill() or Process::Detach(), so let it know to kill the 1142 // process if this happens. 1143 process_sp->SetShouldDetach (false); 1144 1145 // If we didn't have any file actions, the pseudo terminal might 1146 // have been used where the slave side was given as the file to 1147 // open for stdin/out/err after we have already opened the master 1148 // so we can read/write stdin/out/err. 1149 int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor(); 1150 if (pty_fd != lldb_utility::PseudoTerminal::invalid_fd) 1151 { 1152 process_sp->SetSTDIOFileDescriptor(pty_fd); 1153 } 1154 } 1155 else 1156 { 1157 if (log) 1158 log->Printf ("Platform::%s Attach() failed: %s", __FUNCTION__, error.AsCString ()); 1159 } 1160 } 1161 else 1162 { 1163 if (log) 1164 log->Printf ("Platform::%s LaunchProcess() returned launch_info with invalid process id", __FUNCTION__); 1165 } 1166 } 1167 else 1168 { 1169 if (log) 1170 log->Printf ("Platform::%s LaunchProcess() failed: %s", __FUNCTION__, error.AsCString ()); 1171 } 1172 1173 return process_sp; 1174} 1175 1176 1177lldb::PlatformSP 1178Platform::GetPlatformForArchitecture (const ArchSpec &arch, ArchSpec *platform_arch_ptr) 1179{ 1180 lldb::PlatformSP platform_sp; 1181 Error error; 1182 if (arch.IsValid()) 1183 platform_sp = Platform::Create (arch, platform_arch_ptr, error); 1184 return platform_sp; 1185} 1186 1187 1188//------------------------------------------------------------------ 1189/// Lets a platform answer if it is compatible with a given 1190/// architecture and the target triple contained within. 1191//------------------------------------------------------------------ 1192bool 1193Platform::IsCompatibleArchitecture (const ArchSpec &arch, bool exact_arch_match, ArchSpec *compatible_arch_ptr) 1194{ 1195 // If the architecture is invalid, we must answer true... 1196 if (arch.IsValid()) 1197 { 1198 ArchSpec platform_arch; 1199 // Try for an exact architecture match first. 1200 if (exact_arch_match) 1201 { 1202 for (uint32_t arch_idx=0; GetSupportedArchitectureAtIndex (arch_idx, platform_arch); ++arch_idx) 1203 { 1204 if (arch.IsExactMatch(platform_arch)) 1205 { 1206 if (compatible_arch_ptr) 1207 *compatible_arch_ptr = platform_arch; 1208 return true; 1209 } 1210 } 1211 } 1212 else 1213 { 1214 for (uint32_t arch_idx=0; GetSupportedArchitectureAtIndex (arch_idx, platform_arch); ++arch_idx) 1215 { 1216 if (arch.IsCompatibleMatch(platform_arch)) 1217 { 1218 if (compatible_arch_ptr) 1219 *compatible_arch_ptr = platform_arch; 1220 return true; 1221 } 1222 } 1223 } 1224 } 1225 if (compatible_arch_ptr) 1226 compatible_arch_ptr->Clear(); 1227 return false; 1228} 1229 1230Error 1231Platform::PutFile (const FileSpec& source, 1232 const FileSpec& destination, 1233 uint32_t uid, 1234 uint32_t gid) 1235{ 1236 Error error("unimplemented"); 1237 return error; 1238} 1239 1240Error 1241Platform::GetFile (const FileSpec& source, 1242 const FileSpec& destination) 1243{ 1244 Error error("unimplemented"); 1245 return error; 1246} 1247 1248Error 1249Platform::CreateSymlink (const char *src, // The name of the link is in src 1250 const char *dst)// The symlink points to dst 1251{ 1252 Error error("unimplemented"); 1253 return error; 1254} 1255 1256bool 1257Platform::GetFileExists (const lldb_private::FileSpec& file_spec) 1258{ 1259 return false; 1260} 1261 1262Error 1263Platform::Unlink (const char *path) 1264{ 1265 Error error("unimplemented"); 1266 return error; 1267} 1268 1269 1270 1271lldb_private::Error 1272Platform::RunShellCommand (const char *command, // Shouldn't be NULL 1273 const char *working_dir, // Pass NULL to use the current working directory 1274 int *status_ptr, // Pass NULL if you don't want the process exit status 1275 int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit 1276 std::string *command_output, // Pass NULL if you don't want the command output 1277 uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish 1278{ 1279 if (IsHost()) 1280 return Host::RunShellCommand (command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec); 1281 else 1282 return Error("unimplemented"); 1283} 1284 1285 1286bool 1287Platform::CalculateMD5 (const FileSpec& file_spec, 1288 uint64_t &low, 1289 uint64_t &high) 1290{ 1291 if (IsHost()) 1292 return FileSystem::CalculateMD5(file_spec, low, high); 1293 else 1294 return false; 1295} 1296 1297Error 1298Platform::LaunchNativeProcess ( 1299 ProcessLaunchInfo &launch_info, 1300 lldb_private::NativeProcessProtocol::NativeDelegate &native_delegate, 1301 NativeProcessProtocolSP &process_sp) 1302{ 1303 // Platforms should override this implementation if they want to 1304 // support lldb-gdbserver. 1305 return Error("unimplemented"); 1306} 1307 1308Error 1309Platform::AttachNativeProcess (lldb::pid_t pid, 1310 lldb_private::NativeProcessProtocol::NativeDelegate &native_delegate, 1311 NativeProcessProtocolSP &process_sp) 1312{ 1313 // Platforms should override this implementation if they want to 1314 // support lldb-gdbserver. 1315 return Error("unimplemented"); 1316} 1317 1318void 1319Platform::SetLocalCacheDirectory (const char* local) 1320{ 1321 m_local_cache_directory.assign(local); 1322} 1323 1324const char* 1325Platform::GetLocalCacheDirectory () 1326{ 1327 return m_local_cache_directory.c_str(); 1328} 1329 1330static OptionDefinition 1331g_rsync_option_table[] = 1332{ 1333 { LLDB_OPT_SET_ALL, false, "rsync" , 'r', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone , "Enable rsync." }, 1334 { LLDB_OPT_SET_ALL, false, "rsync-opts" , 'R', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeCommandName , "Platform-specific options required for rsync to work." }, 1335 { LLDB_OPT_SET_ALL, false, "rsync-prefix" , 'P', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeCommandName , "Platform-specific rsync prefix put before the remote path." }, 1336 { LLDB_OPT_SET_ALL, false, "ignore-remote-hostname" , 'i', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone , "Do not automatically fill in the remote hostname when composing the rsync command." }, 1337}; 1338 1339static OptionDefinition 1340g_ssh_option_table[] = 1341{ 1342 { LLDB_OPT_SET_ALL, false, "ssh" , 's', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone , "Enable SSH." }, 1343 { LLDB_OPT_SET_ALL, false, "ssh-opts" , 'S', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeCommandName , "Platform-specific options required for SSH to work." }, 1344}; 1345 1346static OptionDefinition 1347g_caching_option_table[] = 1348{ 1349 { LLDB_OPT_SET_ALL, false, "local-cache-dir" , 'c', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypePath , "Path in which to store local copies of files." }, 1350}; 1351 1352OptionGroupPlatformRSync::OptionGroupPlatformRSync () 1353{ 1354} 1355 1356OptionGroupPlatformRSync::~OptionGroupPlatformRSync () 1357{ 1358} 1359 1360const lldb_private::OptionDefinition* 1361OptionGroupPlatformRSync::GetDefinitions () 1362{ 1363 return g_rsync_option_table; 1364} 1365 1366void 1367OptionGroupPlatformRSync::OptionParsingStarting (CommandInterpreter &interpreter) 1368{ 1369 m_rsync = false; 1370 m_rsync_opts.clear(); 1371 m_rsync_prefix.clear(); 1372 m_ignores_remote_hostname = false; 1373} 1374 1375lldb_private::Error 1376OptionGroupPlatformRSync::SetOptionValue (CommandInterpreter &interpreter, 1377 uint32_t option_idx, 1378 const char *option_arg) 1379{ 1380 Error error; 1381 char short_option = (char) GetDefinitions()[option_idx].short_option; 1382 switch (short_option) 1383 { 1384 case 'r': 1385 m_rsync = true; 1386 break; 1387 1388 case 'R': 1389 m_rsync_opts.assign(option_arg); 1390 break; 1391 1392 case 'P': 1393 m_rsync_prefix.assign(option_arg); 1394 break; 1395 1396 case 'i': 1397 m_ignores_remote_hostname = true; 1398 break; 1399 1400 default: 1401 error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); 1402 break; 1403 } 1404 1405 return error; 1406} 1407 1408uint32_t 1409OptionGroupPlatformRSync::GetNumDefinitions () 1410{ 1411 return llvm::array_lengthof(g_rsync_option_table); 1412} 1413 1414lldb::BreakpointSP 1415Platform::SetThreadCreationBreakpoint (lldb_private::Target &target) 1416{ 1417 return lldb::BreakpointSP(); 1418} 1419 1420OptionGroupPlatformSSH::OptionGroupPlatformSSH () 1421{ 1422} 1423 1424OptionGroupPlatformSSH::~OptionGroupPlatformSSH () 1425{ 1426} 1427 1428const lldb_private::OptionDefinition* 1429OptionGroupPlatformSSH::GetDefinitions () 1430{ 1431 return g_ssh_option_table; 1432} 1433 1434void 1435OptionGroupPlatformSSH::OptionParsingStarting (CommandInterpreter &interpreter) 1436{ 1437 m_ssh = false; 1438 m_ssh_opts.clear(); 1439} 1440 1441lldb_private::Error 1442OptionGroupPlatformSSH::SetOptionValue (CommandInterpreter &interpreter, 1443 uint32_t option_idx, 1444 const char *option_arg) 1445{ 1446 Error error; 1447 char short_option = (char) GetDefinitions()[option_idx].short_option; 1448 switch (short_option) 1449 { 1450 case 's': 1451 m_ssh = true; 1452 break; 1453 1454 case 'S': 1455 m_ssh_opts.assign(option_arg); 1456 break; 1457 1458 default: 1459 error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); 1460 break; 1461 } 1462 1463 return error; 1464} 1465 1466uint32_t 1467OptionGroupPlatformSSH::GetNumDefinitions () 1468{ 1469 return llvm::array_lengthof(g_ssh_option_table); 1470} 1471 1472OptionGroupPlatformCaching::OptionGroupPlatformCaching () 1473{ 1474} 1475 1476OptionGroupPlatformCaching::~OptionGroupPlatformCaching () 1477{ 1478} 1479 1480const lldb_private::OptionDefinition* 1481OptionGroupPlatformCaching::GetDefinitions () 1482{ 1483 return g_caching_option_table; 1484} 1485 1486void 1487OptionGroupPlatformCaching::OptionParsingStarting (CommandInterpreter &interpreter) 1488{ 1489 m_cache_dir.clear(); 1490} 1491 1492lldb_private::Error 1493OptionGroupPlatformCaching::SetOptionValue (CommandInterpreter &interpreter, 1494 uint32_t option_idx, 1495 const char *option_arg) 1496{ 1497 Error error; 1498 char short_option = (char) GetDefinitions()[option_idx].short_option; 1499 switch (short_option) 1500 { 1501 case 'c': 1502 m_cache_dir.assign(option_arg); 1503 break; 1504 1505 default: 1506 error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); 1507 break; 1508 } 1509 1510 return error; 1511} 1512 1513uint32_t 1514OptionGroupPlatformCaching::GetNumDefinitions () 1515{ 1516 return llvm::array_lengthof(g_caching_option_table); 1517} 1518 1519size_t 1520Platform::GetEnvironment (StringList &environment) 1521{ 1522 environment.Clear(); 1523 return false; 1524} 1525 1526const std::vector<ConstString> & 1527Platform::GetTrapHandlerSymbolNames () 1528{ 1529 if (!m_calculated_trap_handlers) 1530 { 1531 Mutex::Locker locker (m_trap_handler_mutex); 1532 if (!m_calculated_trap_handlers) 1533 { 1534 CalculateTrapHandlerSymbolNames(); 1535 m_calculated_trap_handlers = true; 1536 } 1537 } 1538 return m_trap_handlers; 1539} 1540 1541