mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-12 16:22:46 +00:00
On Android P, Bionic mistakenly places the vdso first in the list where the executable should be. Also correctly set the section size in the section headers for test module string tables. Bug: chromium:1050178 Change-Id: I83581d05c5ed3e25a237d1ce4a27c45755a3ab3c Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2231525 Reviewed-by: Mark Mentovai <mark@chromium.org> Commit-Queue: Joshua Peraza <jperaza@chromium.org>
231 lines
7.7 KiB
C++
231 lines
7.7 KiB
C++
// 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));
|
|
|
|
VMAddress exe_dynamic_address = 0;
|
|
if (exe_reader.GetDynamicArrayAddress(&exe_dynamic_address)) {
|
|
CheckedLinuxAddressRange exe_range(
|
|
connection->Is64Bit(), exe_reader.Address(), exe_reader.Size());
|
|
EXPECT_TRUE(exe_range.ContainsValue(exe_dynamic_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 doesn't set the dynamic array for the executable in the
|
|
// link map until Android 10.0 (API 29).
|
|
if (android_runtime_api >= 29) {
|
|
EXPECT_EQ(debug.Executable()->dynamic_array, exe_dynamic_address);
|
|
} else {
|
|
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());
|
|
EXPECT_EQ(debug.Executable()->dynamic_array, exe_dynamic_address);
|
|
#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(), possible_mappings->Count() == 0) &&
|
|
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
|