169 lines
6.1 KiB
C++
169 lines
6.1 KiB
C++
// Copyright 2018 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "base/profiler/module_cache.h"
|
|
|
|
#include <dlfcn.h>
|
|
#include <elf.h>
|
|
|
|
#include "base/debug/elf_reader.h"
|
|
#include "base/strings/string_piece.h"
|
|
#include "build/build_config.h"
|
|
#include "third_party/abseil-cpp/absl/types/optional.h"
|
|
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
extern "C" {
|
|
// &__executable_start is the start address of the current module.
|
|
extern const char __executable_start;
|
|
// &__etext is the end addesss of the code segment in the current module.
|
|
extern const char _etext;
|
|
}
|
|
#endif
|
|
|
|
namespace base {
|
|
|
|
namespace {
|
|
|
|
// Returns the unique build ID for a module loaded at |module_addr|. Returns the
|
|
// empty string if the function fails to get the build ID.
|
|
//
|
|
// Build IDs follow a cross-platform format consisting of several fields
|
|
// concatenated together:
|
|
// - the module's unique ID, and
|
|
// - the age suffix for incremental builds.
|
|
//
|
|
// On POSIX, the unique ID is read from the ELF binary located at |module_addr|.
|
|
// The age field is always 0.
|
|
std::string GetUniqueBuildId(const void* module_addr) {
|
|
debug::ElfBuildIdBuffer build_id;
|
|
size_t build_id_length = debug::ReadElfBuildId(module_addr, true, build_id);
|
|
if (!build_id_length)
|
|
return std::string();
|
|
|
|
// Append 0 for the age value.
|
|
return std::string(build_id, build_id_length) + "0";
|
|
}
|
|
|
|
// Returns the offset from |module_addr| to the first byte following the last
|
|
// executable segment from the ELF file mapped at |module_addr|.
|
|
// It's defined this way so that any executable address from this module is in
|
|
// range [addr, addr + GetLastExecutableOffset(addr)).
|
|
// If no executable segment is found, returns 0.
|
|
size_t GetLastExecutableOffset(const void* module_addr) {
|
|
const size_t relocation_offset = debug::GetRelocationOffset(module_addr);
|
|
size_t max_offset = 0;
|
|
for (const Phdr& header : debug::GetElfProgramHeaders(module_addr)) {
|
|
if (header.p_type != PT_LOAD || !(header.p_flags & PF_X))
|
|
continue;
|
|
|
|
max_offset = std::max(
|
|
max_offset, static_cast<size_t>(
|
|
header.p_vaddr + relocation_offset + header.p_memsz -
|
|
reinterpret_cast<uintptr_t>(module_addr)));
|
|
}
|
|
|
|
return max_offset;
|
|
}
|
|
|
|
FilePath GetDebugBasenameForModule(const void* base_address,
|
|
base::StringPiece file) {
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
// Preferentially identify the library using its soname on Android. Libraries
|
|
// mapped directly from apks have the apk filename in |dl_info.dli_fname|, and
|
|
// this doesn't distinguish the particular library.
|
|
absl::optional<StringPiece> library_name =
|
|
debug::ReadElfLibraryName(base_address);
|
|
if (library_name)
|
|
return FilePath(*library_name);
|
|
#endif // BUILDFLAG(IS_ANDROID)
|
|
|
|
#if BUILDFLAG(IS_CHROMEOS)
|
|
// SetProcessTitleFromCommandLine() does not play well with dladdr(). In
|
|
// particular, after calling our setproctitle(), calling dladdr() with an
|
|
// address in the main binary will return the complete command line of the
|
|
// program, including all arguments, in dli_fname. If we get a complete
|
|
// command-line like "/opt/google/chrome/chrome --type=gpu-process
|
|
// --gpu-sandbox-failures-fatal=yes --enable-logging ...", strip off
|
|
// everything that looks like an argument. This is safe on ChromeOS, where we
|
|
// control the directory and file names and know that no chrome binary or
|
|
// system library will have a " --" in the path.
|
|
base::StringPiece::size_type pos = file.find(" --");
|
|
if (pos != base::StringPiece::npos) {
|
|
file = file.substr(0, pos);
|
|
}
|
|
#endif // BUILDFLAG(IS_CHROMEOS)
|
|
|
|
return FilePath(file).BaseName();
|
|
}
|
|
|
|
class PosixModule : public ModuleCache::Module {
|
|
public:
|
|
PosixModule(uintptr_t base_address,
|
|
const std::string& build_id,
|
|
const FilePath& debug_basename,
|
|
size_t size);
|
|
|
|
PosixModule(const PosixModule&) = delete;
|
|
PosixModule& operator=(const PosixModule&) = delete;
|
|
|
|
// ModuleCache::Module
|
|
uintptr_t GetBaseAddress() const override { return base_address_; }
|
|
std::string GetId() const override { return id_; }
|
|
FilePath GetDebugBasename() const override { return debug_basename_; }
|
|
size_t GetSize() const override { return size_; }
|
|
bool IsNative() const override { return true; }
|
|
|
|
private:
|
|
uintptr_t base_address_;
|
|
std::string id_;
|
|
FilePath debug_basename_;
|
|
size_t size_;
|
|
};
|
|
|
|
PosixModule::PosixModule(uintptr_t base_address,
|
|
const std::string& build_id,
|
|
const FilePath& debug_basename,
|
|
size_t size)
|
|
: base_address_(base_address),
|
|
id_(build_id),
|
|
debug_basename_(debug_basename),
|
|
size_(size) {}
|
|
|
|
} // namespace
|
|
|
|
// static
|
|
std::unique_ptr<const ModuleCache::Module> ModuleCache::CreateModuleForAddress(
|
|
uintptr_t address) {
|
|
Dl_info info;
|
|
if (!dladdr(reinterpret_cast<const void*>(address), &info)) {
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
// dladdr doesn't know about the Chrome module in Android targets using the
|
|
// crazy linker. Explicitly check against the module's extents in that case.
|
|
// This is checked after dladdr because if dladdr CAN find the Chrome
|
|
// module, it will return a better fallback basename in `info.dli_fname`.
|
|
if (address >= reinterpret_cast<uintptr_t>(&__executable_start) &&
|
|
address < reinterpret_cast<uintptr_t>(&_etext)) {
|
|
const void* const base_address =
|
|
reinterpret_cast<const void*>(&__executable_start);
|
|
return std::make_unique<PosixModule>(
|
|
reinterpret_cast<uintptr_t>(&__executable_start),
|
|
GetUniqueBuildId(base_address),
|
|
// Extract the soname from the module. It is expected to exist, but if
|
|
// it doesn't use an empty string.
|
|
GetDebugBasenameForModule(base_address, /* file = */ ""),
|
|
GetLastExecutableOffset(base_address));
|
|
}
|
|
#endif
|
|
return nullptr;
|
|
}
|
|
|
|
return std::make_unique<PosixModule>(
|
|
reinterpret_cast<uintptr_t>(info.dli_fbase),
|
|
GetUniqueBuildId(info.dli_fbase),
|
|
GetDebugBasenameForModule(info.dli_fbase, info.dli_fname),
|
|
GetLastExecutableOffset(info.dli_fbase));
|
|
}
|
|
|
|
} // namespace base
|