|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include <sys/mman.h> |
|
8 #include <unistd.h> |
|
9 #include <sys/types.h> |
|
10 #include <sys/stat.h> |
|
11 #include <fcntl.h> |
|
12 |
|
13 #include "mozilla/Assertions.h" |
|
14 #include "mozilla/NullPtr.h" |
|
15 |
|
16 #include "PlatformMacros.h" |
|
17 #include "AutoObjectMapper.h" |
|
18 |
|
19 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) |
|
20 # include <dlfcn.h> |
|
21 # include "mozilla/Types.h" |
|
22 // FIXME move these out of mozglue/linker/ElfLoader.h into their |
|
23 // own header, so as to avoid conflicts arising from two definitions |
|
24 // of Array |
|
25 extern "C" { |
|
26 MFBT_API size_t |
|
27 __dl_get_mappable_length(void *handle); |
|
28 MFBT_API void * |
|
29 __dl_mmap(void *handle, void *addr, size_t length, off_t offset); |
|
30 MFBT_API void |
|
31 __dl_munmap(void *handle, void *addr, size_t length); |
|
32 } |
|
33 // The following are for get_installation_lib_dir() |
|
34 # include "nsString.h" |
|
35 # include "nsDirectoryServiceUtils.h" |
|
36 # include "nsDirectoryServiceDefs.h" |
|
37 #endif |
|
38 |
|
39 |
|
40 // A helper function for creating failure error messages in |
|
41 // AutoObjectMapper*::Map. |
|
42 static void |
|
43 failedToMessage(void(*aLog)(const char*), |
|
44 const char* aHowFailed, std::string aFileName) |
|
45 { |
|
46 char buf[300]; |
|
47 snprintf(buf, sizeof(buf), "AutoObjectMapper::Map: Failed to %s \'%s\'", |
|
48 aHowFailed, aFileName.c_str()); |
|
49 buf[sizeof(buf)-1] = 0; |
|
50 aLog(buf); |
|
51 } |
|
52 |
|
53 |
|
54 AutoObjectMapperPOSIX::AutoObjectMapperPOSIX(void(*aLog)(const char*)) |
|
55 : mImage(nullptr) |
|
56 , mSize(0) |
|
57 , mLog(aLog) |
|
58 , mIsMapped(false) |
|
59 {} |
|
60 |
|
61 AutoObjectMapperPOSIX::~AutoObjectMapperPOSIX() { |
|
62 if (!mIsMapped) { |
|
63 // There's nothing to do. |
|
64 MOZ_ASSERT(!mImage); |
|
65 MOZ_ASSERT(mSize == 0); |
|
66 return; |
|
67 } |
|
68 MOZ_ASSERT(mSize > 0); |
|
69 // The following assertion doesn't necessarily have to be true, |
|
70 // but we assume (reasonably enough) that no mmap facility would |
|
71 // be crazy enough to map anything at page zero. |
|
72 MOZ_ASSERT(mImage); |
|
73 munmap(mImage, mSize); |
|
74 } |
|
75 |
|
76 bool AutoObjectMapperPOSIX::Map(/*OUT*/void** start, /*OUT*/size_t* length, |
|
77 std::string fileName) |
|
78 { |
|
79 MOZ_ASSERT(!mIsMapped); |
|
80 |
|
81 int fd = open(fileName.c_str(), O_RDONLY); |
|
82 if (fd == -1) { |
|
83 failedToMessage(mLog, "open", fileName); |
|
84 return false; |
|
85 } |
|
86 |
|
87 struct stat st; |
|
88 int err = fstat(fd, &st); |
|
89 size_t sz = (err == 0) ? st.st_size : 0; |
|
90 if (err != 0 || sz == 0) { |
|
91 failedToMessage(mLog, "fstat", fileName); |
|
92 close(fd); |
|
93 return false; |
|
94 } |
|
95 |
|
96 void* image = mmap(nullptr, sz, PROT_READ, MAP_SHARED, fd, 0); |
|
97 if (image == MAP_FAILED) { |
|
98 failedToMessage(mLog, "mmap", fileName); |
|
99 close(fd); |
|
100 return false; |
|
101 } |
|
102 |
|
103 close(fd); |
|
104 mIsMapped = true; |
|
105 mImage = *start = image; |
|
106 mSize = *length = sz; |
|
107 return true; |
|
108 } |
|
109 |
|
110 |
|
111 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) |
|
112 // A helper function for AutoObjectMapperFaultyLib::Map. Finds out |
|
113 // where the installation's lib directory is, since we'll have to look |
|
114 // in there to get hold of libmozglue.so. Returned C string is heap |
|
115 // allocated and the caller must deallocate it. |
|
116 static char* |
|
117 get_installation_lib_dir() |
|
118 { |
|
119 nsCOMPtr<nsIProperties> |
|
120 directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID)); |
|
121 if (!directoryService) { |
|
122 return nullptr; |
|
123 } |
|
124 nsCOMPtr<nsIFile> greDir; |
|
125 nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), |
|
126 getter_AddRefs(greDir)); |
|
127 if (NS_FAILED(rv)) return nullptr; |
|
128 nsCString path; |
|
129 rv = greDir->GetNativePath(path); |
|
130 if (NS_FAILED(rv)) { |
|
131 return nullptr; |
|
132 } |
|
133 return strdup(path.get()); |
|
134 } |
|
135 |
|
136 AutoObjectMapperFaultyLib::AutoObjectMapperFaultyLib(void(*aLog)(const char*)) |
|
137 : AutoObjectMapperPOSIX(aLog) |
|
138 , mHdl(nullptr) |
|
139 {} |
|
140 |
|
141 AutoObjectMapperFaultyLib::~AutoObjectMapperFaultyLib() { |
|
142 if (mHdl) { |
|
143 // We've got an object mapped by faulty.lib. Unmap it via faulty.lib. |
|
144 MOZ_ASSERT(mSize > 0); |
|
145 // Assert on the basis that no valid mapping would start at page zero. |
|
146 MOZ_ASSERT(mImage); |
|
147 __dl_munmap(mHdl, mImage, mSize); |
|
148 dlclose(mHdl); |
|
149 // Stop assertions in ~AutoObjectMapperPOSIX from failing. |
|
150 mImage = nullptr; |
|
151 mSize = 0; |
|
152 } |
|
153 // At this point the parent class destructor, ~AutoObjectMapperPOSIX, |
|
154 // gets called. If that has something mapped in the normal way, it |
|
155 // will unmap it in the normal way. Unfortunately there's no |
|
156 // obvious way to enforce the requirement that the object is mapped |
|
157 // either by faulty.lib or by the parent class, but not by both. |
|
158 } |
|
159 |
|
160 bool AutoObjectMapperFaultyLib::Map(/*OUT*/void** start, /*OUT*/size_t* length, |
|
161 std::string fileName) |
|
162 { |
|
163 MOZ_ASSERT(!mHdl); |
|
164 |
|
165 if (fileName == "libmozglue.so") { |
|
166 |
|
167 // Do (2) in the comment above. |
|
168 char* libdir = get_installation_lib_dir(); |
|
169 if (libdir) { |
|
170 fileName = std::string(libdir) + "/lib/" + fileName; |
|
171 free(libdir); |
|
172 } |
|
173 // Hand the problem off to the standard mapper. |
|
174 return AutoObjectMapperPOSIX::Map(start, length, fileName); |
|
175 |
|
176 } else { |
|
177 |
|
178 // Do cases (1) and (3) in the comment above. We have to |
|
179 // grapple with faulty.lib directly. |
|
180 void* hdl = dlopen(fileName.c_str(), RTLD_GLOBAL | RTLD_LAZY); |
|
181 if (!hdl) { |
|
182 failedToMessage(mLog, "get handle for ELF file", fileName); |
|
183 return false; |
|
184 } |
|
185 |
|
186 size_t sz = __dl_get_mappable_length(hdl); |
|
187 if (sz == 0) { |
|
188 dlclose(hdl); |
|
189 failedToMessage(mLog, "get size for ELF file", fileName); |
|
190 return false; |
|
191 } |
|
192 |
|
193 void* image = __dl_mmap(hdl, nullptr, sz, 0); |
|
194 if (image == MAP_FAILED) { |
|
195 dlclose(hdl); |
|
196 failedToMessage(mLog, "mmap ELF file", fileName); |
|
197 return false; |
|
198 } |
|
199 |
|
200 mHdl = hdl; |
|
201 mImage = *start = image; |
|
202 mSize = *length = sz; |
|
203 return true; |
|
204 } |
|
205 } |
|
206 |
|
207 #endif // defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) |