text: // Copyright 2017 The Crashpad Authors. All rights reserved.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //     http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  #include "snapshot/linux/debug_rendezvous.h"
  
  #include <linux/auxvec.h>
  #include <string.h>
  #include <unistd.h>
  
  #include <limits>
  
  #include "base/format_macros.h"
  #include "base/strings/string_number_conversions.h"
  #include "base/strings/string_piece.h"
  #include "base/strings/stringprintf.h"
  #include "build/build_config.h"
  #include "gtest/gtest.h"
  #include "snapshot/elf/elf_image_reader.h"
  #include "test/linux/fake_ptrace_connection.h"
  #include "test/multiprocess.h"
  #include "util/linux/address_types.h"
  #include "util/linux/auxiliary_vector.h"
  #include "util/linux/direct_ptrace_connection.h"
  #include "util/linux/memory_map.h"
  #include "util/process/process_memory_linux.h"
  #include "util/process/process_memory_range.h"
  
  #if defined(OS_ANDROID)
  #include <android/api-level.h>
  #endif
  
  namespace crashpad {
  namespace test {
  namespace {
  
  void TestAgainstTarget(PtraceConnection* connection) {
    // Use ElfImageReader on the main executable which can tell us the debug
    // address. glibc declares the symbol _r_debug in link.h which we can use to
    // get the address, but Android does not.
    AuxiliaryVector aux;
    ASSERT_TRUE(aux.Initialize(connection));
  
    LinuxVMAddress phdrs;
    ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs));
  
    MemoryMap mappings;
    ASSERT_TRUE(mappings.Initialize(connection));
  
    const MemoryMap::Mapping* phdr_mapping = mappings.FindMapping(phdrs);
    ASSERT_TRUE(phdr_mapping);
  
    auto exe_mappings = mappings.FindFilePossibleMmapStarts(*phdr_mapping);
    ASSERT_EQ(exe_mappings->Count(), 1u);
    LinuxVMAddress elf_address = exe_mappings->Next()->range.Base();
  
    ProcessMemoryLinux memory;
    ASSERT_TRUE(memory.Initialize(connection->GetProcessID()));
    ProcessMemoryRange range;
    ASSERT_TRUE(range.Initialize(&memory, connection->Is64Bit()));
  
    ElfImageReader exe_reader;
    ASSERT_TRUE(exe_reader.Initialize(range, elf_address));
    LinuxVMAddress debug_address;
    ASSERT_TRUE(exe_reader.GetDebugAddress(&debug_address));
  
    // start the actual tests
    DebugRendezvous debug;
    ASSERT_TRUE(debug.Initialize(range, debug_address));
  
  #if defined(OS_ANDROID)
    const int android_runtime_api = android_get_device_api_level();
    ASSERT_GE(android_runtime_api, 1);
  
    EXPECT_NE(debug.Executable()->name.find("crashpad_snapshot_test"),
              std::string::npos);
  
    // Android's loader never sets the dynamic array for the executable.
    EXPECT_EQ(debug.Executable()->dynamic_array, 0u);
  #else
    // glibc's loader implements most of the tested features that Android's was
    // missing but has since gained.
    const int android_runtime_api = std::numeric_limits<int>::max();
  
    // glibc's loader does not set the name for the executable.
    EXPECT_TRUE(debug.Executable()->name.empty());
    CheckedLinuxAddressRange exe_range(
        connection->Is64Bit(), exe_reader.Address(), exe_reader.Size());
    EXPECT_TRUE(exe_range.ContainsValue(debug.Executable()->dynamic_array));
  #endif  // OS_ANDROID
  
    // Android's loader doesn't set the load bias until Android 4.3 (API 18).
    if (android_runtime_api >= 18) {
      EXPECT_EQ(debug.Executable()->load_bias, exe_reader.GetLoadBias());
    } else {
      EXPECT_EQ(debug.Executable()->load_bias, 0);
    }
  
    for (const DebugRendezvous::LinkEntry& module : debug.Modules()) {
      SCOPED_TRACE(base::StringPrintf("name %s, load_bias 0x%" PRIx64
                                      ", dynamic_array 0x%" PRIx64,
                                      module.name.c_str(),
                                      module.load_bias,
                                      module.dynamic_array));
      const bool is_android_loader = (module.name == "/system/bin/linker" ||
                                      module.name == "/system/bin/linker64");
  
      // Android's loader doesn't set its own dynamic array until Android 4.2
      // (API 17).
      if (is_android_loader && android_runtime_api < 17) {
        EXPECT_EQ(module.dynamic_array, 0u);
        EXPECT_EQ(module.load_bias, 0);
        continue;
      }
  
      ASSERT_TRUE(module.dynamic_array);
      const MemoryMap::Mapping* dyn_mapping =
          mappings.FindMapping(module.dynamic_array);
      ASSERT_TRUE(dyn_mapping);
  
      auto possible_mappings = mappings.FindFilePossibleMmapStarts(*dyn_mapping);
      ASSERT_GE(possible_mappings->Count(), 1u);
  
      std::unique_ptr<ElfImageReader> module_reader;
      const MemoryMap::Mapping* module_mapping = nullptr;
      const MemoryMap::Mapping* mapping = nullptr;
      while ((mapping = possible_mappings->Next())) {
        auto parsed_module = std::make_unique<ElfImageReader>();
        VMAddress dynamic_address;
        if (parsed_module->Initialize(range, mapping->range.Base()) &&
            parsed_module->GetDynamicArrayAddress(&dynamic_address) &&
            dynamic_address == module.dynamic_array) {
          module_reader = std::move(parsed_module);
          module_mapping = mapping;
          break;
        }
      }
      ASSERT_TRUE(module_reader.get());
  
  #if defined(OS_ANDROID)
      EXPECT_FALSE(module.name.empty());
  #else
      // glibc's loader doesn't always set the name in the link map for the vdso.
      EXPECT_PRED4(
          [](const std::string mapping_name,
             int device,
             int inode,
             const std::string& module_name) {
            const bool is_vdso_mapping =
                device == 0 && inode == 0 && mapping_name == "[vdso]";
            static constexpr char kPrefix[] = "linux-vdso.so.";
            return is_vdso_mapping ==
                   (module_name.empty() ||
                    module_name.compare(0, strlen(kPrefix), kPrefix) == 0);
          },
          module_mapping->name,
          module_mapping->device,
          module_mapping->inode,
          module.name);
  #endif  // OS_ANDROID
  
      // Android's loader stops setting its own load bias after Android 4.4.4
      // (API 20) until Android 6.0 (API 23).
      if (is_android_loader && android_runtime_api > 20 &&
          android_runtime_api < 23) {
        EXPECT_EQ(module.load_bias, 0);
      } else {
        EXPECT_EQ(module.load_bias, module_reader->GetLoadBias());
      }
  
      CheckedLinuxAddressRange module_range(
          connection->Is64Bit(), module_reader->Address(), module_reader->Size());
      EXPECT_TRUE(module_range.ContainsValue(module.dynamic_array));
    }
  }
  
  TEST(DebugRendezvous, Self) {
    FakePtraceConnection connection;
    ASSERT_TRUE(connection.Initialize(getpid()));
  
    TestAgainstTarget(&connection);
  }
  
  class ChildTest : public Multiprocess {
   public:
    ChildTest() {}
    ~ChildTest() {}
  
   private:
    void MultiprocessParent() {
      DirectPtraceConnection connection;
      ASSERT_TRUE(connection.Initialize(ChildPID()));
  
      TestAgainstTarget(&connection);
    }
  
    void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); }
  
    DISALLOW_COPY_AND_ASSIGN(ChildTest);
  };
  
  TEST(DebugRendezvous, Child) {
    ChildTest test;
    test.Run();
  }
  
  }  // namespace
  }  // namespace test
  }  // namespace crashpad
  