//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Detach () { LoggingUtils.PrintFunction (); try { string command = "-target-detach"; SendCommand (command, delegate (MiResultRecord resultRecord) { MiResultRecord.RequireOk (resultRecord, command); }); } catch (Exception e) { LoggingUtils.HandleException (e); throw; } finally { m_gdbServer = null; m_gdbSetup.ClearPortForwarding (); IsAttached = false; } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public CLangDebugger (DebugEngine debugEngine, LaunchConfiguration launchConfiguration, DebuggeeProgram debugProgram) { Engine = debugEngine; m_launchConfiguration = launchConfiguration; NativeProgram = new CLangDebuggeeProgram (this, debugProgram); NativeMemoryBytes = new CLangDebuggeeMemoryBytes (this); VariableManager = new CLangDebuggerVariableManager (this); // // Evaluate target device's architecture triple. // string preferedGdbAbiToolPrefix = string.Empty; bool allow64BitAbis = true; switch (debugProgram.DebugProcess.NativeProcess.PrimaryCpuAbi) { case "armeabi": case "armeabi-v7a": { preferedGdbAbiToolPrefix = "arm-linux-androideabi"; break; } case "arm64-v8a": { if (allow64BitAbis) { preferedGdbAbiToolPrefix = "aarch64-linux-android"; } else { preferedGdbAbiToolPrefix = "arm-linux-androideabi"; } break; } case "x86": { preferedGdbAbiToolPrefix = "i686-linux-android"; break; } case "x86_64": { if (allow64BitAbis) { preferedGdbAbiToolPrefix = "x86_64-linux-android"; } else { preferedGdbAbiToolPrefix = "i686-linux-android"; } break; } case "mips": { preferedGdbAbiToolPrefix = "mipsel-linux-android"; break; } case "mips64": { if (allow64BitAbis) { preferedGdbAbiToolPrefix = "mips64el-linux-android"; } else { preferedGdbAbiToolPrefix = "mipsel-linux-android"; } break; } } if (string.IsNullOrEmpty (preferedGdbAbiToolPrefix)) { throw new InvalidOperationException (string.Format ("Unrecognised target primary CPU ABI: {0}", debugProgram.DebugProcess.NativeProcess.PrimaryCpuAbi)); } bool preferedGdbAbiIs64Bit = preferedGdbAbiToolPrefix.Contains ("64"); Engine.Broadcast (new DebugEngineEvent.DebuggerConnectionEvent (DebugEngineEvent.DebuggerConnectionEvent.EventType.LogStatus, string.Format ("Configuring GDB for '{0}' target...", preferedGdbAbiToolPrefix)), null, null); // // Android++ bundles its own copies of GDB to get round various NDK issues. Search for these. // string androidPlusPlusRoot = Environment.GetEnvironmentVariable ("ANDROID_PLUS_PLUS"); // // Build GDB version permutations. // List<string> gdbToolPermutations = new List<string> (); string [] availableHostArchitectures = new string [] { "x86", "x86_64" }; foreach (string arch in availableHostArchitectures) { if (arch.Contains ("64") && !Environment.Is64BitOperatingSystem) { continue; } string gdbToolFilePattern = string.Format ("{0}-gdb.cmd", preferedGdbAbiToolPrefix); string [] availableVersionPaths = Directory.GetDirectories (Path.Combine (androidPlusPlusRoot, "contrib", "gdb", "bin", arch), "*.*.*", SearchOption.TopDirectoryOnly); foreach (string versionPath in availableVersionPaths) { string [] gdbToolMatches = Directory.GetFiles (versionPath, gdbToolFilePattern, SearchOption.TopDirectoryOnly); foreach (string tool in gdbToolMatches) { gdbToolPermutations.Add (tool); } } } if (gdbToolPermutations.Count == 0) { throw new InvalidOperationException ("Could not locate required 32/64-bit GDB deployments."); } else { // // Pick the oldest GDB version available if running 'Jelly Bean' or below. // bool forceNdkR9dClient = (debugProgram.DebugProcess.NativeProcess.HostDevice.SdkVersion <= AndroidSettings.VersionCode.JELLY_BEAN); if (forceNdkR9dClient) { m_gdbSetup = new GdbSetup (debugProgram.DebugProcess.NativeProcess, gdbToolPermutations [0]); } else { m_gdbSetup = new GdbSetup (debugProgram.DebugProcess.NativeProcess, gdbToolPermutations [gdbToolPermutations.Count - 1]); } // // A symbolic link to 'share' is placed in the architecture directory, provide GDB with that location. // string architecturePath = Path.GetDirectoryName (Path.GetDirectoryName (m_gdbSetup.GdbToolPath)); string pythonGdbScriptsPath = Path.Combine (architecturePath, "share", "gdb"); m_gdbSetup.GdbToolArguments += " --data-directory " + PathUtils.SantiseWindowsPath (pythonGdbScriptsPath); } if (m_gdbSetup == null) { throw new InvalidOperationException ("Could not evaluate a suitable GDB instance. Ensure you have the correct NDK deployment for your system's architecture."); } if (m_launchConfiguration != null) { string launchDirectory; if (m_launchConfiguration.TryGetValue ("LaunchSuspendedDir", out launchDirectory)) { m_gdbSetup.SymbolDirectories.Add (launchDirectory); } } GdbServer = new GdbServer (m_gdbSetup); GdbClient = new GdbClient (m_gdbSetup); GdbClient.OnResultRecord = OnClientResultRecord; GdbClient.OnAsyncRecord = OnClientAsyncRecord; GdbClient.OnStreamRecord = OnClientStreamRecord; GdbClient.Start (); }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Attach (GdbServer gdbServer) { LoggingUtils.PrintFunction (); if (gdbServer == null) { throw new ArgumentNullException ("gdbServer"); } m_gdbServer = gdbServer; m_gdbSetup.ClearPortForwarding (); m_gdbSetup.SetupPortForwarding (); SetSetting ("auto-solib-add", "on", false); SetSetting ("stop-on-solib-events", "0", false); SetSetting ("breakpoint pending", "on", false); // an unrecognized breakpoint location should automatically result in a pending breakpoint being created. // // Figure out the target executable and architecture to be debugged. // // AFAIK the most reliable way to evaluate this is to read the symbolic link 'exe' for the target process: // - This should also support 64-bit (app_process64) processes. // // lrwxrwxrwx u0_a91 u0_a91 2015-02-09 14:13 exe -> /system/bin/app_process32 // bool debugging64bitProcess = false; string targetAppProcess = m_gdbSetup.Process.HostDevice.Shell ("run-as", string.Format ("{0} readlink /proc/{1}/exe", m_gdbSetup.Process.Name, m_gdbSetup.Process.Pid)).Replace ("\r", "").Replace ("\n", ""); string cachedAppProcess = Path.Combine (m_gdbSetup.CacheSysRoot, targetAppProcess.Substring (1)); if (targetAppProcess.Contains ("app_process64")) { debugging64bitProcess = true; } // // Pull required system and application binaries from the device. // TODO: Use 'ndk-depends' to figure out which system shared libraries are used and pull these. // HashSet<string> sharedLibrarySearchPaths = new HashSet<string> (); HashSet<string> debugFileDirectoryPaths = new HashSet<string> (); string [] cachedSystemBinaries = m_gdbSetup.CacheSystemBinaries (debugging64bitProcess); string [] cachedSystemLibraries = m_gdbSetup.CacheSystemLibraries (debugging64bitProcess); sharedLibrarySearchPaths.Add (PathUtils.SantiseWindowsPath (m_gdbSetup.CacheSysRoot)); // prioritise sysroot parent. foreach (string systemBinary in cachedSystemLibraries) { string posixSystemBinaryDirectory = PathUtils.SantiseWindowsPath (Path.GetDirectoryName (systemBinary)); if (!sharedLibrarySearchPaths.Contains (posixSystemBinaryDirectory)) { sharedLibrarySearchPaths.Add (posixSystemBinaryDirectory); } } string [] cachedAppLibraries = m_gdbSetup.CacheApplicationLibraries (); foreach (string appBinary in cachedAppLibraries) { string posixAppBinaryDirectory = PathUtils.SantiseWindowsPath (Path.GetDirectoryName (appBinary)); if (!sharedLibrarySearchPaths.Contains (posixAppBinaryDirectory)) { sharedLibrarySearchPaths.Add (posixAppBinaryDirectory); } } foreach (string symbolDir in m_gdbSetup.SymbolDirectories) { string path = PathUtils.SantiseWindowsPath (symbolDir); if (!debugFileDirectoryPaths.Contains (path)) { debugFileDirectoryPaths.Add (path); } } SetSetting ("sysroot", PathUtils.SantiseWindowsPath (m_gdbSetup.CacheSysRoot), false); #if true { string [] array = new string [sharedLibrarySearchPaths.Count]; sharedLibrarySearchPaths.CopyTo (array, 0); SetSetting ("solib-search-path", string.Join (";", array), true); } #endif #if true { string [] array = new string [debugFileDirectoryPaths.Count]; debugFileDirectoryPaths.CopyTo (array, 0); SetSetting ("debug-file-directory", string.Join (";", array), true); } #endif if (!File.Exists (cachedAppProcess)) { string linker = (debugging64bitProcess) ? @"system\bin\linker64" : @"system\bin\linker"; cachedAppProcess = Path.Combine (m_gdbSetup.CacheSysRoot, linker); } if (string.IsNullOrEmpty (cachedAppProcess) || !File.Exists (cachedAppProcess)) { throw new InvalidOperationException (string.Format ("Could not locate target binary: {0}", cachedAppProcess)); } try { string command = "-file-exec-and-symbols " + PathUtils.SantiseWindowsPath (cachedAppProcess); MiResultRecord resultRecord = SendSyncCommand (command); MiResultRecord.RequireOk (resultRecord, command); } catch (Exception e) { LoggingUtils.HandleException (e); throw; } // // Connect to the remote target. // try { int timeout = 60000; // 60 seconds string command = string.Format ("-target-select remote {0}:{1}", m_gdbSetup.Host, m_gdbSetup.Port); MiResultRecord resultRecord = SendSyncCommand (command, timeout); MiResultRecord.RequireOk (resultRecord, command); IsAttached = true; } catch (Exception e) { LoggingUtils.HandleException (e); throw; } // // Evaluate target's GDB/MI support and capabilities. // try { string command = "-list-target-features"; MiResultRecord resultRecord = SendSyncCommand (command); MiResultRecord.RequireOk (resultRecord, command); if (resultRecord.HasField ("features")) { foreach (MiResultValue feature in resultRecord ["features"] [0].Values) { m_gdbSupportedTargetMiFeatures.Add (feature.GetString ()); } } } catch (Exception e) { LoggingUtils.HandleException (e); throw; } // // Evaluate target's registers (and their names). // try { string command = "-data-list-register-names"; MiResultRecord resultRecord = SendSyncCommand (command); MiResultRecord.RequireOk (resultRecord, command); if (resultRecord.HasField ("register-names")) { MiResultValue registerNames = resultRecord ["register-names"] [0]; for (int i = 0; i < registerNames.Values.Count; ++i) { string register = registerNames [i].GetString (); m_registerIdMapping.Add ((uint) i, register); } } } catch (Exception e) { LoggingUtils.HandleException (e); throw; } }