jasonmolenda created this revision.
jasonmolenda added a project: LLDB.
Herald added a subscriber: JDevlieghere.
jasonmolenda requested review of this revision.

ProcessMachCore::DoLoadCore was implemented to handle the two LC_NOTEs "kern 
ver str" and "main bin spec" with only certain combinations of UUID and address 
and type present.  Other groups are starting to adopt these corefile hints and 
there are combinations that lldb wouldn't handle correctly.

This patch cleans up the handling of these hints in DoLoadCore a lot -- I've 
been wanting to clean this up for over a year now -- and adds tests for all of 
the combinations.

I did make one change to DynamicLoaderStatic to only set load addresses of 
Sections to their file addresses (slide==0) if an address hasn't already been 
set.  This gives me the ability to set the load address in ProcessMachCore and 
have it not get re-set back to the original file address later by the dynamic 
loader plugin.

The rest of this patch is is code that's my little playground of 
ProcessMachCore, so I don't know if others will have any comments, but I wanted 
to open it to the floor if anyone has suggestions or questions.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D99571

Files:
  lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp
  lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
  lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py
  lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp

Index: lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp
===================================================================
--- lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp
+++ lldb/test/API/macosx/lc-note/firmware-corefile/create-empty-corefile.cpp
@@ -4,6 +4,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <string>
+#include <sys/errno.h>
 #include <uuid/uuid.h>
 #include <vector>
 
@@ -79,9 +80,17 @@
 
 void add_lc_note_kern_ver_str_load_command(
     std::vector<std::vector<uint8_t>> &loadcmds, std::vector<uint8_t> &payload,
-    int payload_file_offset, std::string uuid) {
+    int payload_file_offset, std::string uuid, uint64_t address) {
   std::string ident = "EFI UUID=";
   ident += uuid;
+
+  if (address != 0xffffffffffffffff) {
+    ident += "; stext=";
+    char buf[24];
+    sprintf(buf, "0x%llx", address);
+    ident += buf;
+  }
+
   std::vector<uint8_t> loadcmd_data;
 
   add_uint32(loadcmd_data, LC_NOTE); // note_command.cmd
@@ -111,7 +120,7 @@
 
 void add_lc_note_main_bin_spec_load_command(
     std::vector<std::vector<uint8_t>> &loadcmds, std::vector<uint8_t> &payload,
-    int payload_file_offset, std::string uuidstr) {
+    int payload_file_offset, std::string uuidstr, uint64_t address) {
   std::vector<uint8_t> loadcmd_data;
 
   add_uint32(loadcmd_data, LC_NOTE); // note_command.cmd
@@ -136,8 +145,8 @@
 
   // Now write the "main bin spec" payload.
   add_uint32(payload, 1);          // version
-  add_uint32(payload, 3);          // type == 3 [ firmware, standalone,e tc ]
-  add_uint64(payload, UINT64_MAX); // load address unknown/unspecified
+  add_uint32(payload, 3);          // type == 3 [ firmware, standalone, etc ]
+  add_uint64(payload, address);    // load address
   uuid_t uuid;
   uuid_parse(uuidstr.c_str(), uuid);
   for (int i = 0; i < sizeof(uuid_t); i++)
@@ -259,9 +268,12 @@
 }
 
 int main(int argc, char **argv) {
-  if (argc != 4) {
-    fprintf(stderr, "usage: create-empty-corefile version-string|main-bin-spec "
-                    "<output-core-name> <binary-to-copy-uuid-from>\n");
+  if (argc != 5) {
+    fprintf(stderr,
+            "usage: create-empty-corefile version-string|main-bin-spec "
+            "<output-core-name> <binary-to-copy-uuid-from> <address>\n");
+    fprintf(stderr,
+            "     <address> is base 16, 0xffffffffffffffff means unknown\n");
     fprintf(
         stderr,
         "Create a Mach-O corefile with an either LC_NOTE 'kern ver str' or \n");
@@ -284,13 +296,22 @@
   // An array of corefile contents (page data, lc_note data, etc)
   std::vector<uint8_t> payload;
 
+  errno = 0;
+  uint64_t address = strtoull(argv[4], NULL, 16);
+  if (errno != 0) {
+    fprintf(stderr, "Unable to parse address %s as base 16", argv[4]);
+    exit(1);
+  }
+
   // First add all the load commands / payload so we can figure out how large
   // the load commands will actually be.
   load_commands.push_back(x86_lc_thread_load_command());
   if (strcmp(argv[1], "version-string") == 0)
-    add_lc_note_kern_ver_str_load_command(load_commands, payload, 0, uuid);
+    add_lc_note_kern_ver_str_load_command(load_commands, payload, 0, uuid,
+                                          address);
   else
-    add_lc_note_main_bin_spec_load_command(load_commands, payload, 0, uuid);
+    add_lc_note_main_bin_spec_load_command(load_commands, payload, 0, uuid,
+                                           address);
   add_lc_segment(load_commands, payload, 0);
 
   int size_of_load_commands = 0;
@@ -308,11 +329,11 @@
   load_commands.push_back(x86_lc_thread_load_command());
 
   if (strcmp(argv[1], "version-string") == 0)
-    add_lc_note_kern_ver_str_load_command(load_commands, payload,
-                                          header_and_load_cmd_room, uuid);
+    add_lc_note_kern_ver_str_load_command(
+        load_commands, payload, header_and_load_cmd_room, uuid, address);
   else
-    add_lc_note_main_bin_spec_load_command(load_commands, payload,
-                                           header_and_load_cmd_room, uuid);
+    add_lc_note_main_bin_spec_load_command(
+        load_commands, payload, header_and_load_cmd_room, uuid, address);
 
   add_lc_segment(load_commands, payload, header_and_load_cmd_room);
 
Index: lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py
===================================================================
--- lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py
+++ lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py
@@ -26,7 +26,9 @@
         self.create_corefile = self.getBuildArtifact("create-empty-corefile")
         self.dsym_for_uuid = self.getBuildArtifact("dsym-for-uuid.sh")
         self.aout_corefile = self.getBuildArtifact("aout.core")
+        self.aout_corefile_addr = self.getBuildArtifact("aout.core_addr")
         self.bout_corefile = self.getBuildArtifact("bout.core")
+        self.bout_corefile_addr = self.getBuildArtifact("bout.core_addr")
 
         ## We can hook in our dsym-for-uuid shell script to lldb with this env
         ## var instead of requiring a defaults write.
@@ -99,8 +101,10 @@
         os.chmod(self.dsym_for_uuid, 0o755)
 
         ### Create our corefile
-        retcode = call(self.create_corefile + " version-string " + self.aout_corefile + " " + self.aout_exe, shell=True)
-        retcode = call(self.create_corefile + " main-bin-spec " + self.bout_corefile + " " + self.bout_exe, shell=True)
+        retcode = call(self.create_corefile + " version-string " + self.aout_corefile + " " + self.aout_exe + " 0xffffffffffffffff", shell=True)
+        retcode = call(self.create_corefile + " main-bin-spec " + self.bout_corefile + " " + self.bout_exe + " 0xffffffffffffffff", shell=True)
+        retcode = call(self.create_corefile + " version-string " + self.aout_corefile_addr + " " + self.aout_exe + " 0x70000000000", shell=True)
+        retcode = call(self.create_corefile + " main-bin-spec " + self.bout_corefile_addr + " " + self.bout_exe + " 0x70000000000", shell=True)
 
         ### Now run lldb on the corefile
         ### which will give us a UUID
@@ -115,19 +119,53 @@
         self.assertEqual(self.process.IsValid(), True)
         if self.TraceOn():
             self.runCmd("image list")
+            self.runCmd("target mod dump sections")
         self.assertEqual(self.target.GetNumModules(), 1)
         fspec = self.target.GetModuleAtIndex(0).GetFileSpec()
         filepath = fspec.GetDirectory() + "/" + fspec.GetFilename()
         self.assertEqual(filepath, self.aout_exe)
 
+        # Second, try the "kern ver str" corefile where it loads at an address
+        self.target = self.dbg.CreateTarget('')
+        err = lldb.SBError()
+        self.process = self.target.LoadCore(self.aout_corefile_addr)
+        self.assertEqual(self.process.IsValid(), True)
+        if self.TraceOn():
+            self.runCmd("image list")
+            self.runCmd("target mod dump sections")
+        self.assertEqual(self.target.GetNumModules(), 1)
+        fspec = self.target.GetModuleAtIndex(0).GetFileSpec()
+        filepath = fspec.GetDirectory() + "/" + fspec.GetFilename()
+        self.assertEqual(filepath, self.aout_exe)
+        main_sym = self.target.GetModuleAtIndex(0).FindSymbol("main", lldb.eSymbolTypeAny)
+        main_addr = main_sym.GetStartAddress()
+        self.assertGreater(main_addr.GetLoadAddress(self.target), 0x70000000000)
+        self.assertNotEqual(main_addr.GetLoadAddress(self.target), lldb.LLDB_INVALID_ADDRESS)
 
-        # Second, try the "main bin spec" corefile
+        # Third, try the "main bin spec" corefile
         self.target = self.dbg.CreateTarget('')
         self.process = self.target.LoadCore(self.bout_corefile)
         self.assertEqual(self.process.IsValid(), True)
         if self.TraceOn():
             self.runCmd("image list")
+            self.runCmd("target mod dump sections")
+        self.assertEqual(self.target.GetNumModules(), 1)
+        fspec = self.target.GetModuleAtIndex(0).GetFileSpec()
+        filepath = fspec.GetDirectory() + "/" + fspec.GetFilename()
+        self.assertEqual(filepath, self.bout_exe)
+
+        # Fourth, try the "main bin spec" corefile where it loads at an address
+        self.target = self.dbg.CreateTarget('')
+        self.process = self.target.LoadCore(self.bout_corefile_addr)
+        self.assertEqual(self.process.IsValid(), True)
+        if self.TraceOn():
+            self.runCmd("image list")
+            self.runCmd("target mod dump sections")
         self.assertEqual(self.target.GetNumModules(), 1)
         fspec = self.target.GetModuleAtIndex(0).GetFileSpec()
         filepath = fspec.GetDirectory() + "/" + fspec.GetFilename()
         self.assertEqual(filepath, self.bout_exe)
+        main_sym = self.target.GetModuleAtIndex(0).FindSymbol("main", lldb.eSymbolTypeAny)
+        main_addr = main_sym.GetStartAddress()
+        self.assertGreater(main_addr.GetLoadAddress(self.target), 0x70000000000)
+        self.assertNotEqual(main_addr.GetLoadAddress(self.target), lldb.LLDB_INVALID_ADDRESS)
Index: lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
===================================================================
--- lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
+++ lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
@@ -21,6 +21,7 @@
 #include "lldb/Symbol/LocateSymbolFile.h"
 #include "lldb/Symbol/ObjectFile.h"
 #include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/SectionLoadList.h"
 #include "lldb/Target/Target.h"
 #include "lldb/Target/Thread.h"
 #include "lldb/Utility/DataBuffer.h"
@@ -36,6 +37,7 @@
 
 #include "Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h"
 #include "Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h"
+#include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h"
 #include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h"
 
 #include <memory>
@@ -188,6 +190,59 @@
   return false;
 }
 
+// We have a hint about a binary -- a UUID, possibly a load address.
+// Try to load a file with that UUID into lldb, and if we have a load
+// address, set it correctly.  Else assume that the binary was loaded
+// with no slide.
+static bool load_standalone_binary(UUID uuid, addr_t addr, Target &target) {
+  if (uuid.IsValid()) {
+    ModuleSpec module_spec;
+    module_spec.GetUUID() = uuid;
+
+    // Look up UUID in global module cache before attempting
+    // dsymForUUID-like action.
+    ModuleSP module_sp;
+    Status error = ModuleList::GetSharedModule(module_spec, module_sp, nullptr,
+                                               nullptr, nullptr);
+
+    if (!module_sp.get()) {
+      // Force a a dsymForUUID lookup, if that tool is available.
+      if (!module_spec.GetSymbolFileSpec())
+        Symbols::DownloadObjectAndSymbolFile(module_spec, true);
+
+      if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
+        module_sp.reset(new Module(module_spec));
+      }
+    }
+
+    if (module_sp.get() && module_sp->GetObjectFile()) {
+      target.SetArchitecture(module_sp->GetObjectFile()->GetArchitecture());
+      target.GetImages().AppendIfNeeded(module_sp, false);
+
+      Address base_addr = module_sp->GetObjectFile()->GetBaseAddress();
+      addr_t slide = 0;
+      if (addr != LLDB_INVALID_ADDRESS && base_addr.IsValid()) {
+        addr_t file_load_addr = base_addr.GetFileAddress();
+        slide = addr - file_load_addr;
+      }
+      bool changed = false;
+      module_sp->SetLoadAddress(target, slide, true, changed);
+
+      ModuleList added_module;
+      added_module.Append(module_sp, false);
+      target.ModulesDidLoad(added_module);
+
+      // Flush info in the process (stack frames, etc).
+      ProcessSP process_sp(target.GetProcessSP());
+      if (process_sp)
+        process_sp->Flush();
+
+      return true;
+    }
+  }
+  return false;
+}
+
 // Process Control
 Status ProcessMachCore::DoLoadCore() {
   Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER |
@@ -285,124 +340,76 @@
   ObjectFile::BinaryType type;
   if (core_objfile->GetCorefileMainBinaryInfo(objfile_binary_addr,
                                               objfile_binary_uuid, type)) {
+    if (log) {
+      log->Printf(
+          "ProcessMachCore::DoLoadCore: using binary hint from 'main bin spec' "
+          "LC_NOTE with UUID %s address 0x%" PRIx64 " and type %d",
+          objfile_binary_uuid.GetAsString().c_str(), objfile_binary_addr, type);
+    }
     if (objfile_binary_addr != LLDB_INVALID_ADDRESS) {
-      if (type == ObjectFile::eBinaryTypeUser)
+      if (type == ObjectFile::eBinaryTypeUser) {
         m_dyld_addr = objfile_binary_addr;
-      else
+        m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic();
+        found_main_binary_definitively = true;
+      }
+      if (type == ObjectFile::eBinaryTypeKernel) {
         m_mach_kernel_addr = objfile_binary_addr;
-      found_main_binary_definitively = true;
-      LLDB_LOGF(log,
-                "ProcessMachCore::DoLoadCore: using kernel address 0x%" PRIx64
-                " from LC_NOTE 'main bin spec' load command.",
-                m_mach_kernel_addr);
+        m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic();
+        found_main_binary_definitively = true;
+      }
+    }
+    if (!found_main_binary_definitively) {
+      // ObjectFile::eBinaryTypeStandalone, undeclared types
+      if (load_standalone_binary(objfile_binary_uuid, objfile_binary_addr,
+                                 GetTarget())) {
+        found_main_binary_definitively = true;
+        m_dyld_plugin_name = DynamicLoaderStatic::GetPluginNameStatic();
+      }
     }
   }
 
   // This checks for the presence of an LC_IDENT string in a core file;
   // LC_IDENT is very obsolete and should not be used in new code, but if the
   // load command is present, let's use the contents.
-  std::string corefile_identifier = core_objfile->GetIdentifierString();
-  if (!found_main_binary_definitively &&
-      corefile_identifier.find("Darwin Kernel") != std::string::npos) {
-    UUID uuid;
-    addr_t addr = LLDB_INVALID_ADDRESS;
+  UUID ident_uuid;
+  addr_t ident_binary_addr = LLDB_INVALID_ADDRESS;
+  if (!found_main_binary_definitively) {
+    std::string corefile_identifier = core_objfile->GetIdentifierString();
+
+    // Search for UUID= and stext= strings in the identifier str.
     if (corefile_identifier.find("UUID=") != std::string::npos) {
       size_t p = corefile_identifier.find("UUID=") + strlen("UUID=");
       std::string uuid_str = corefile_identifier.substr(p, 36);
-      uuid.SetFromStringRef(uuid_str);
+      ident_uuid.SetFromStringRef(uuid_str);
+      if (log)
+        log->Printf("Got a UUID from LC_IDENT/kern ver str LC_NOTE: %s",
+                    ident_uuid.GetAsString().c_str());
     }
     if (corefile_identifier.find("stext=") != std::string::npos) {
       size_t p = corefile_identifier.find("stext=") + strlen("stext=");
       if (corefile_identifier[p] == '0' && corefile_identifier[p + 1] == 'x') {
-        errno = 0;
-        addr = ::strtoul(corefile_identifier.c_str() + p, nullptr, 16);
-        if (errno != 0 || addr == 0)
-          addr = LLDB_INVALID_ADDRESS;
+        ident_binary_addr =
+            ::strtoul(corefile_identifier.c_str() + p, nullptr, 16);
+        if (log)
+          log->Printf("Got a load address from LC_IDENT/kern ver str "
+                      "LC_NOTE: 0x%" PRIx64,
+                      ident_binary_addr);
       }
     }
-    if (uuid.IsValid() && addr != LLDB_INVALID_ADDRESS) {
-      m_mach_kernel_addr = addr;
-      found_main_binary_definitively = true;
-      LLDB_LOGF(
-          log,
-          "ProcessMachCore::DoLoadCore: Using the kernel address 0x%" PRIx64
-          " from LC_IDENT/LC_NOTE 'kern ver str' string: '%s'",
-          addr, corefile_identifier.c_str());
-    }
-  }
 
-  // In the case where we have an LC_NOTE specifying a standalone
-  // binary with only a UUID (and no load address) (iBoot, EFI, etc),
-  // then let's try to force a load of the binary and set its
-  // load address to 0-offset.
-  //
-  // The two forms this can come in is either a
-  //   'kern ver str' LC_NOTE with "EFI UUID=...."
-  //   'main bin spec' LC_NOTE with UUID and no load address.
-
-  if (found_main_binary_definitively == false &&
-      (corefile_identifier.find("EFI ") != std::string::npos ||
-       (objfile_binary_uuid.IsValid() &&
-        objfile_binary_addr == LLDB_INVALID_ADDRESS))) {
-    UUID uuid;
-    if (objfile_binary_uuid.IsValid()) {
-      uuid = objfile_binary_uuid;
-      LLDB_LOGF(log,
-                "ProcessMachCore::DoLoadCore: Using the main bin spec "
-                "LC_NOTE with UUID %s and no load address",
-                uuid.GetAsString().c_str());
-    } else {
-      if (corefile_identifier.find("UUID=") != std::string::npos) {
-        size_t p = corefile_identifier.find("UUID=") + strlen("UUID=");
-        std::string uuid_str = corefile_identifier.substr(p, 36);
-        uuid.SetFromStringRef(uuid_str);
-        if (uuid.IsValid()) {
-          LLDB_LOGF(log,
-                    "ProcessMachCore::DoLoadCore: Using the EFI "
-                    "from LC_IDENT/LC_NOTE 'kern ver str' string: '%s'",
-                    corefile_identifier.c_str());
-        }
-      }
-    }
-
-    if (uuid.IsValid()) {
-      ModuleSpec module_spec;
-      module_spec.GetUUID() = uuid;
-      module_spec.GetArchitecture() = GetTarget().GetArchitecture();
-
-      // Lookup UUID locally, before attempting dsymForUUID-like action
-      FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
-      module_spec.GetSymbolFileSpec() =
-          Symbols::LocateExecutableSymbolFile(module_spec, search_paths);
-      if (module_spec.GetSymbolFileSpec()) {
-        ModuleSpec executable_module_spec =
-            Symbols::LocateExecutableObjectFile(module_spec);
-        if (FileSystem::Instance().Exists(
-                executable_module_spec.GetFileSpec())) {
-          module_spec.GetFileSpec() = executable_module_spec.GetFileSpec();
-        }
-      }
-
-      // Force a a dsymForUUID lookup, if that tool is available.
-      if (!module_spec.GetSymbolFileSpec())
-        Symbols::DownloadObjectAndSymbolFile(module_spec, true);
-
-      // If we found a binary, load it at offset 0 and set our
-      // dyld_plugin to be the static plugin.
-      if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
-        ModuleSP module_sp(new Module(module_spec));
-        if (module_sp.get() && module_sp->GetObjectFile()) {
-          GetTarget().GetImages().AppendIfNeeded(module_sp, true);
-          GetTarget().SetExecutableModule(module_sp, eLoadDependentsNo);
-          found_main_binary_definitively = true;
-          bool changed = true;
-          module_sp->SetLoadAddress(GetTarget(), 0, true, changed);
-          ModuleList added_module;
-          added_module.Append(module_sp, false);
-          GetTarget().ModulesDidLoad(added_module);
-          m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic();
-          found_main_binary_definitively = true;
-        }
+    // Search for a "Darwin Kernel" str indicating kernel; else treat as
+    // standalone
+    if (corefile_identifier.find("Darwin Kernel") != std::string::npos &&
+        ident_uuid.IsValid() && ident_binary_addr != LLDB_INVALID_ADDRESS) {
+      if (log)
+        log->Printf("ProcessMachCore::DoLoadCore: Found kernel binary via "
+                    "LC_IDENT/kern ver str LC_NOTE");
+      m_mach_kernel_addr = ident_binary_addr;
+      found_main_binary_definitively = true;
+    } else if (ident_uuid.IsValid()) {
+      if (load_standalone_binary(ident_uuid, ident_binary_addr, GetTarget())) {
+        found_main_binary_definitively = true;
+        m_dyld_plugin_name = DynamicLoaderStatic::GetPluginNameStatic();
       }
     }
   }
Index: lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp
===================================================================
--- lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp
+++ lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp
@@ -10,6 +10,7 @@
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Core/Section.h"
 #include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/SectionLoadList.h"
 #include "lldb/Target/Target.h"
 
 #include "DynamicLoaderStatic.h"
@@ -105,6 +106,15 @@
             // the file...
             SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
             if (section_sp) {
+              // If this section already has a load address set in the target,
+              // don't re-set it to the file address.  Something may have
+              // set it to a more correct value already.
+              if (m_process->GetTarget()
+                      .GetSectionLoadList()
+                      .GetSectionLoadAddress(section_sp) !=
+                  LLDB_INVALID_ADDRESS) {
+                continue;
+              }
               if (m_process->GetTarget().SetSectionLoadAddress(
                       section_sp, section_sp->GetFileAddress()))
                 changed = true;
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to