summaryrefslogtreecommitdiffstats
path: root/tools/profiler/lul/AutoObjectMapper.cpp
blob: 1bc5ce62ad5969ff43e70900217dfcd2a0e02146 (plain)
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
/* -*- 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 <sys/mman.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "mozilla/Assertions.h"
#include "mozilla/Sprintf.h"

#include "PlatformMacros.h"
#include "AutoObjectMapper.h"

#if defined(SPS_OS_android)
# include <dlfcn.h>
# include "mozilla/Types.h"
  // FIXME move these out of mozglue/linker/ElfLoader.h into their
  // own header, so as to avoid conflicts arising from two definitions
  // of Array
  extern "C" {
    MFBT_API size_t
    __dl_get_mappable_length(void *handle);
    MFBT_API void *
    __dl_mmap(void *handle, void *addr, size_t length, off_t offset);
    MFBT_API void
    __dl_munmap(void *handle, void *addr, size_t length);
  }
  // The following are for get_installation_lib_dir()
# include "nsString.h"
# include "nsDirectoryServiceUtils.h"
# include "nsDirectoryServiceDefs.h"
#endif


// A helper function for creating failure error messages in
// AutoObjectMapper*::Map.
static void
failedToMessage(void(*aLog)(const char*),
                const char* aHowFailed, std::string aFileName)
{
  char buf[300];
  SprintfLiteral(buf, "AutoObjectMapper::Map: Failed to %s \'%s\'",
                 aHowFailed, aFileName.c_str());
  buf[sizeof(buf)-1] = 0;
  aLog(buf);
}


AutoObjectMapperPOSIX::AutoObjectMapperPOSIX(void(*aLog)(const char*))
  : mImage(nullptr)
  , mSize(0)
  , mLog(aLog)
  , mIsMapped(false)
{}

AutoObjectMapperPOSIX::~AutoObjectMapperPOSIX() {
  if (!mIsMapped) {
    // There's nothing to do.
    MOZ_ASSERT(!mImage);
    MOZ_ASSERT(mSize == 0);
    return;
  }
  MOZ_ASSERT(mSize > 0);
  // The following assertion doesn't necessarily have to be true,
  // but we assume (reasonably enough) that no mmap facility would
  // be crazy enough to map anything at page zero.
  MOZ_ASSERT(mImage);
  munmap(mImage, mSize);
}

bool AutoObjectMapperPOSIX::Map(/*OUT*/void** start, /*OUT*/size_t* length,
                                std::string fileName)
{
  MOZ_ASSERT(!mIsMapped);

  int fd = open(fileName.c_str(), O_RDONLY);
  if (fd == -1) {
    failedToMessage(mLog, "open", fileName);
    return false;
  }

  struct stat st;
  int    err = fstat(fd, &st);
  size_t sz  = (err == 0) ? st.st_size : 0;
  if (err != 0 || sz == 0) {
    failedToMessage(mLog, "fstat", fileName);
    close(fd);
    return false;
  }

  void* image = mmap(nullptr, sz, PROT_READ, MAP_SHARED, fd, 0);
  if (image == MAP_FAILED) {
    failedToMessage(mLog, "mmap", fileName);
    close(fd);
    return false;
  }

  close(fd);
  mIsMapped = true;
  mImage = *start  = image;
  mSize  = *length = sz;
  return true;
}


#if defined(SPS_OS_android)
// A helper function for AutoObjectMapperFaultyLib::Map.  Finds out
// where the installation's lib directory is, since we'll have to look
// in there to get hold of libmozglue.so.  Returned C string is heap
// allocated and the caller must deallocate it.
static char*
get_installation_lib_dir()
{
  nsCOMPtr<nsIProperties>
    directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
  if (!directoryService) {
    return nullptr;
  }
  nsCOMPtr<nsIFile> greDir;
  nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile),
                                      getter_AddRefs(greDir));
  if (NS_FAILED(rv)) return nullptr;
  nsCString path;
  rv = greDir->GetNativePath(path);
  if (NS_FAILED(rv)) {
    return nullptr;
  }
  return strdup(path.get());
}

AutoObjectMapperFaultyLib::AutoObjectMapperFaultyLib(void(*aLog)(const char*))
  : AutoObjectMapperPOSIX(aLog)
  , mHdl(nullptr)
{}

AutoObjectMapperFaultyLib::~AutoObjectMapperFaultyLib() {
  if (mHdl) {
    // We've got an object mapped by faulty.lib.  Unmap it via faulty.lib.
    MOZ_ASSERT(mSize > 0);
    // Assert on the basis that no valid mapping would start at page zero.
    MOZ_ASSERT(mImage);
    __dl_munmap(mHdl, mImage, mSize);
    dlclose(mHdl);
    // Stop assertions in ~AutoObjectMapperPOSIX from failing.
    mImage = nullptr;
    mSize  = 0;
  }
  // At this point the parent class destructor, ~AutoObjectMapperPOSIX,
  // gets called.  If that has something mapped in the normal way, it
  // will unmap it in the normal way.  Unfortunately there's no
  // obvious way to enforce the requirement that the object is mapped
  // either by faulty.lib or by the parent class, but not by both.
}

bool AutoObjectMapperFaultyLib::Map(/*OUT*/void** start, /*OUT*/size_t* length,
                                    std::string fileName)
{
  MOZ_ASSERT(!mHdl);

  if (fileName == "libmozglue.so") {

    // Do (2) in the comment above.
    char* libdir = get_installation_lib_dir();
    if (libdir) {
      fileName = std::string(libdir) + "/lib/" + fileName;
      free(libdir);
    }
    // Hand the problem off to the standard mapper.
    return AutoObjectMapperPOSIX::Map(start, length, fileName);

  } else {

    // Do cases (1) and (3) in the comment above.  We have to
    // grapple with faulty.lib directly.
    void* hdl = dlopen(fileName.c_str(), RTLD_GLOBAL | RTLD_LAZY);
    if (!hdl) {
      failedToMessage(mLog, "get handle for ELF file", fileName);
      return false;
    }

    size_t sz = __dl_get_mappable_length(hdl);
    if (sz == 0) {
      dlclose(hdl);
      failedToMessage(mLog, "get size for ELF file", fileName);
      return false;
    }

    void* image = __dl_mmap(hdl, nullptr, sz, 0);
    if (image == MAP_FAILED) {
      dlclose(hdl);
      failedToMessage(mLog, "mmap ELF file", fileName);
      return false;
    }

    mHdl   = hdl;
    mImage = *start  = image;
    mSize  = *length = sz;
    return true;
  }
}

#endif // defined(SPS_OS_android)