DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (6863f516ba38)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "shared-libraries.h"

#define PATH_MAX_TOSTRING(x) #x
#define PATH_MAX_STRING(x) PATH_MAX_TOSTRING(x)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <fstream>
#include "platform.h"
#include "shared-libraries.h"
#include "mozilla/Sprintf.h"
#include "mozilla/Unused.h"
#include "nsDebug.h"
#include "nsNativeCharsetUtils.h"
#include <nsTArray.h>

#include "common/linux/file_id.h"
#include <algorithm>
#include <dlfcn.h>
#include <features.h>
#include <sys/types.h>

#if defined(GP_OS_linux)
#  include <link.h>  // dl_phdr_info
#elif defined(GP_OS_android)
#  include "AutoObjectMapper.h"
#  include "ElfLoader.h"  // dl_phdr_info
extern "C" MOZ_EXPORT __attribute__((weak)) int dl_iterate_phdr(
    int (*callback)(struct dl_phdr_info* info, size_t size, void* data),
    void* data);
#else
#  error "Unexpected configuration"
#endif

struct LoadedLibraryInfo {
  LoadedLibraryInfo(const char* aName, unsigned long aBaseAddress,
                    unsigned long aFirstMappingStart,
                    unsigned long aLastMappingEnd)
      : mName(aName),
        mBaseAddress(aBaseAddress),
        mFirstMappingStart(aFirstMappingStart),
        mLastMappingEnd(aLastMappingEnd) {}

  nsCString mName;
  unsigned long mBaseAddress;
  unsigned long mFirstMappingStart;
  unsigned long mLastMappingEnd;
};

#if defined(GP_OS_android)
static void outputMapperLog(const char* aBuf) { LOG("%s", aBuf); }
#endif

static nsCString IDtoUUIDString(
    const google_breakpad::wasteful_vector<uint8_t>& aIdentifier) {
  using namespace google_breakpad;

  nsCString uuid;
  const std::string str = FileID::ConvertIdentifierToUUIDString(aIdentifier);
  uuid.Append(str.c_str(), str.size());
  // This is '0', not '\0', since it represents the breakpad id age.
  uuid.Append('0');
  return uuid;
}

// Get the breakpad Id for the binary file pointed by bin_name
static nsCString getId(const char* bin_name) {
  using namespace google_breakpad;

  PageAllocator allocator;
  auto_wasteful_vector<uint8_t, kDefaultBuildIdSize> identifier(&allocator);

#if defined(GP_OS_android)
  if (nsDependentCString(bin_name).Find("!/") != kNotFound) {
    AutoObjectMapperFaultyLib mapper(outputMapperLog);
    void* image = nullptr;
    size_t size = 0;
    if (mapper.Map(&image, &size, bin_name) && image && size) {
      if (FileID::ElfFileIdentifierFromMappedFile(image, identifier)) {
        return IDtoUUIDString(identifier);
      }
    }
  }
#endif

  FileID file_id(bin_name);
  if (file_id.ElfFileIdentifier(identifier)) {
    return IDtoUUIDString(identifier);
  }

  return EmptyCString();
}

static SharedLibrary SharedLibraryAtPath(const char* path,
                                         unsigned long libStart,
                                         unsigned long libEnd,
                                         unsigned long offset = 0) {
  nsAutoString pathStr;
  mozilla::Unused << NS_WARN_IF(
      NS_FAILED(NS_CopyNativeToUnicode(nsDependentCString(path), pathStr)));

  nsAutoString nameStr = pathStr;
  int32_t pos = nameStr.RFindChar('/');
  if (pos != kNotFound) {
    nameStr.Cut(0, pos + 1);
  }

  return SharedLibrary(libStart, libEnd, offset, getId(path), nameStr, pathStr,
                       nameStr, pathStr, EmptyCString(), "");
}

static int dl_iterate_callback(struct dl_phdr_info* dl_info, size_t size,
                               void* data) {
  auto libInfoList = reinterpret_cast<nsTArray<LoadedLibraryInfo>*>(data);

  if (dl_info->dlpi_phnum <= 0) return 0;

  unsigned long baseAddress = dl_info->dlpi_addr;
  unsigned long firstMappingStart = -1;
  unsigned long lastMappingEnd = 0;

  for (size_t i = 0; i < dl_info->dlpi_phnum; i++) {
    if (dl_info->dlpi_phdr[i].p_type != PT_LOAD) {
      continue;
    }
    unsigned long start = dl_info->dlpi_addr + dl_info->dlpi_phdr[i].p_vaddr;
    unsigned long end = start + dl_info->dlpi_phdr[i].p_memsz;
    if (start < firstMappingStart) {
      firstMappingStart = start;
    }
    if (end > lastMappingEnd) {
      lastMappingEnd = end;
    }
  }

  libInfoList->AppendElement(LoadedLibraryInfo(
      dl_info->dlpi_name, baseAddress, firstMappingStart, lastMappingEnd));

  return 0;
}

SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
  SharedLibraryInfo info;

#if defined(GP_OS_linux)
  // We need to find the name of the executable (exeName, exeNameLen) and the
  // address of its executable section (exeExeAddr) in the running image.
  char exeName[PATH_MAX];
  memset(exeName, 0, sizeof(exeName));

  ssize_t exeNameLen = readlink("/proc/self/exe", exeName, sizeof(exeName) - 1);
  if (exeNameLen == -1) {
    // readlink failed for whatever reason.  Note this, but keep going.
    exeName[0] = '\0';
    exeNameLen = 0;
    LOG("SharedLibraryInfo::GetInfoForSelf(): readlink failed");
  } else {
    // Assert no buffer overflow.
    MOZ_RELEASE_ASSERT(exeNameLen >= 0 &&
                       exeNameLen < static_cast<ssize_t>(sizeof(exeName)));
  }

  unsigned long exeExeAddr = 0;
#endif

#if defined(GP_OS_android)
  // If dl_iterate_phdr doesn't exist, we give up immediately.
  if (!dl_iterate_phdr) {
    // On ARM Android, dl_iterate_phdr is provided by the custom linker.
    // So if libxul was loaded by the system linker (e.g. as part of
    // xpcshell when running tests), it won't be available and we should
    // not call it.
    return info;
  }
#endif

  // Read info from /proc/self/maps. We ignore most of it.
  pid_t pid = profiler_current_process_id();
  char path[PATH_MAX];
  SprintfLiteral(path, "/proc/%d/maps", pid);
  std::ifstream maps(path);
  std::string line;
  while (std::getline(maps, line)) {
    int ret;
    unsigned long start;
    unsigned long end;
    char perm[6 + 1] = "";
    unsigned long offset;
    char modulePath[PATH_MAX + 1] = "";
    ret = sscanf(line.c_str(),
                 "%lx-%lx %6s %lx %*s %*x %" PATH_MAX_STRING(PATH_MAX) "s\n",
                 &start, &end, perm, &offset, modulePath);
    if (!strchr(perm, 'x')) {
      // Ignore non executable entries
      continue;
    }
    if (ret != 5 && ret != 4) {
      LOG("SharedLibraryInfo::GetInfoForSelf(): "
          "reading /proc/self/maps failed");
      continue;
    }

#if defined(GP_OS_linux)
    // Try to establish the main executable's load address.
    if (exeNameLen > 0 && strcmp(modulePath, exeName) == 0) {
      exeExeAddr = start;
    }
#elif defined(GP_OS_android)
    // Use /proc/pid/maps to get the dalvik-jit section since it has no
    // associated phdrs.
    if (0 == strcmp(modulePath, "/dev/ashmem/dalvik-jit-code-cache")) {
      info.AddSharedLibrary(
          SharedLibraryAtPath(modulePath, start, end, offset));
      if (info.GetSize() > 10000) {
        LOG("SharedLibraryInfo::GetInfoForSelf(): "
            "implausibly large number of mappings acquired");
        break;
      }
    }
#endif
  }

  nsTArray<LoadedLibraryInfo> libInfoList;

  // We collect the bulk of the library info using dl_iterate_phdr.
  dl_iterate_phdr(dl_iterate_callback, &libInfoList);

  for (const auto& libInfo : libInfoList) {
    info.AddSharedLibrary(
        SharedLibraryAtPath(libInfo.mName.get(), libInfo.mFirstMappingStart,
                            libInfo.mLastMappingEnd,
                            libInfo.mFirstMappingStart - libInfo.mBaseAddress));
  }

#if defined(GP_OS_linux)
  // Make another pass over the information we just harvested from
  // dl_iterate_phdr.  If we see a nameless object mapped at what we earlier
  // established to be the main executable's load address, attach the
  // executable's name to that entry.
  for (size_t i = 0; i < info.GetSize(); i++) {
    SharedLibrary& lib = info.GetMutableEntry(i);
    if (lib.GetStart() == exeExeAddr && lib.GetNativeDebugPath().empty()) {
      lib = SharedLibraryAtPath(exeName, lib.GetStart(), lib.GetEnd(),
                                lib.GetOffset());

      // We only expect to see one such entry.
      break;
    }
  }
#endif

  return info;
}

void SharedLibraryInfo::Initialize() { /* do nothing */
}