1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // A set of common helper functions used by crazy_linker tests.
6 // IMPORTANT: ALL FUNCTIONS HERE ARE INLINED. This avoids adding a
7 // dependency on another source file for all tests that include this
13 #include <crazy_linker.h>
22 #include <sys/socket.h>
29 // Print an error message and exit the process.
30 // Message must be terminated by a newline.
31 inline void Panic(const char* fmt
, ...) {
33 fprintf(stderr
, "PANIC: ");
35 vfprintf(stderr
, fmt
, args
);
40 // Print an error message, the errno message, then exit the process.
41 // Message must not be terminated by a newline.
42 inline void PanicErrno(const char* fmt
, ...) {
43 int old_errno
= errno
;
45 fprintf(stderr
, "PANIC: ");
47 vfprintf(stderr
, fmt
, args
);
49 fprintf(stderr
, ": %s\n", strerror(old_errno
));
53 // Simple string class.
56 String() : str_(NULL
), len_(0) {}
58 String(const String
& other
) { String(other
.str_
, other
.len_
); }
60 String(const char* str
) { String(str
, strlen(str
)); }
62 String(const char* str
, size_t len
) : str_(NULL
), len_(0) {
73 String
& operator+=(const char* str
) {
74 Append(str
, strlen(str
));
78 String
& operator+=(const String
& other
) {
79 Append(other
.str_
, other
.len_
);
83 String
& operator+=(char ch
) {
88 const char* c_str() const { return len_
? str_
: ""; }
89 char* ptr() { return str_
; }
90 size_t size() const { return len_
; }
92 void Append(const char* str
, size_t len
) {
93 size_t old_len
= len_
;
95 memcpy(str_
+ old_len
, str
, len
);
98 void Resize(size_t len
) {
99 str_
= reinterpret_cast<char*>(realloc(str_
, len
+ 1));
101 memset(str_
+ len_
, '\0', len
- len_
);
106 void Format(const char* fmt
, ...) {
112 va_copy(args2
, args
);
113 int ret
= vsnprintf(str_
, len_
+ 1, fmt
, args2
);
115 if (ret
< static_cast<int>(len_
+ 1))
127 // Helper class to create a temporary directory that gets deleted on scope exit,
128 // as well as all regular files it contains.
129 class TempDirectory
{
132 snprintf(path_
, sizeof path_
, "/data/local/tmp/temp-XXXXXX");
134 Panic("Could not create temporary directory name: %s\n", strerror(errno
));
135 if (mkdir(path_
, 0700) < 0)
136 Panic("Could not create temporary directory %s: %s\n", strerror(errno
));
140 // Remove any file in this directory.
141 DIR* d
= opendir(path_
);
143 Panic("Could not open directory %s: %s\n", strerror(errno
));
145 struct dirent
* entry
;
146 while ((entry
= readdir(d
)) != NULL
) {
147 if (!strcmp(entry
->d_name
, ".") || !strcmp(entry
->d_name
, ".."))
150 file_path
.Format("%s/%s", path_
, entry
->d_name
);
151 if (unlink(file_path
.c_str()) < 0)
152 Panic("Could not remove %s: %s\n", file_path
.c_str(), strerror(errno
));
156 if (rmdir(path_
) < 0)
157 Panic("Could not remove dir %s: %s\n", path_
, strerror(errno
));
160 const char* path() const { return path_
; }
163 char path_
[PATH_MAX
];
166 // Scoped FILE* class. Always closed on destruction.
169 ScopedFILE() : file_(NULL
) {}
178 void Open(const char* path
, const char* mode
) {
179 file_
= fopen(path
, mode
);
181 Panic("Could not open file for reading: %s: %s\n", path
, strerror(errno
));
184 FILE* file() const { return file_
; }
190 // Retrieve current executable path as a String.
191 inline String
GetCurrentExecutable() {
193 path
.Resize(PATH_MAX
);
195 TEMP_FAILURE_RETRY(readlink("/proc/self/exe", path
.ptr(), path
.size()));
197 Panic("Could not read /proc/self/exe: %s\n", strerror(errno
));
202 // Retrieve current executable directory as a String.
203 inline String
GetCurrentExecutableDirectory() {
204 String path
= GetCurrentExecutable();
206 const char* p
= reinterpret_cast<const char*>(strrchr(path
.c_str(), '/'));
208 Panic("Current executable does not have directory root?: %s\n",
211 path
.Resize(p
- path
.c_str());
215 // Copy a file named |src_file_name| in directory |src_file_dir| into
216 // a file named |dst_file_name| in directory |dst_file_dir|. Panics on error.
217 inline void CopyFile(const char* src_file_name
,
218 const char* src_file_dir
,
219 const char* dst_file_name
,
220 const char* dst_file_dir
) {
222 src_path
.Format("%s/%s", src_file_dir
, src_file_name
);
225 src_file
.Open(src_path
.c_str(), "rb");
228 dst_path
.Format("%s/%s", dst_file_dir
, dst_file_name
);
230 dst_file
.Open(dst_path
.c_str(), "wb");
234 size_t read
= fread(buffer
, 1, sizeof buffer
, src_file
.file());
236 size_t written
= fwrite(buffer
, 1, read
, dst_file
.file());
238 Panic("Wrote %d bytes instead of %d into %s\n",
243 if (read
< sizeof buffer
)
248 // Send a file descriptor |fd| through |socket|.
249 // Return 0 on success, -1/errno on failure.
250 inline int SendFd(int socket
, int fd
) {
256 iov
.iov_base
= buffer
;
260 struct cmsghdr
* cmsg
;
261 char cms
[CMSG_SPACE(sizeof(int))];
263 ::memset(&msg
, 0, sizeof(msg
));
266 msg
.msg_control
= reinterpret_cast<caddr_t
>(cms
);
267 msg
.msg_controllen
= CMSG_LEN(sizeof(int));
269 cmsg
= CMSG_FIRSTHDR(&msg
);
270 cmsg
->cmsg_len
= CMSG_LEN(sizeof(int));
271 cmsg
->cmsg_level
= SOL_SOCKET
;
272 cmsg
->cmsg_type
= SCM_RIGHTS
;
273 ::memcpy(CMSG_DATA(cmsg
), &fd
, sizeof(int));
275 int ret
= sendmsg(socket
, &msg
, 0);
279 if (ret
!= iov
.iov_len
) {
287 inline int ReceiveFd(int socket
, int* fd
) {
291 iov
.iov_base
= buffer
;
295 struct cmsghdr
* cmsg
;
296 char cms
[CMSG_SPACE(sizeof(int))];
298 ::memset(&msg
, 0, sizeof msg
);
304 msg
.msg_control
= reinterpret_cast<caddr_t
>(cms
);
305 msg
.msg_controllen
= sizeof(cms
);
307 int ret
= recvmsg(socket
, &msg
, 0);
315 cmsg
= CMSG_FIRSTHDR(&msg
);
316 ::memcpy(fd
, CMSG_DATA(cmsg
), sizeof(int));
320 // Check that there are exactly |expected_count| memory mappings in
321 // /proc/self/maps that point to a RELRO ashmem region.
322 inline void CheckRelroMaps(int expected_count
) {
323 printf("Checking for %d RELROs in /proc/self/maps\n", expected_count
);
325 FILE* file
= fopen("/proc/self/maps", "rb");
327 Panic("Could not open /proc/self/maps (pid %d): %s\n",
332 int count_relros
= 0;
333 printf("proc/%d/maps:\n", getpid());
334 while (fgets(line
, sizeof line
, file
)) {
335 if (strstr(line
, "with_relro")) {
336 // The supported library names are "lib<name>_with_relro.so".
338 if (strstr(line
, "/dev/ashmem/RELRO:")) {
340 // Check that they are read-only mappings.
341 if (!strstr(line
, " r--"))
342 Panic("Shared RELRO mapping is not readonly!\n");
343 // Check that they can't be remapped read-write.
344 unsigned vma_start
, vma_end
;
345 if (sscanf(line
, "%x-%x", &vma_start
, &vma_end
) != 2)
346 Panic("Could not parse VM address range!\n");
347 int ret
= ::mprotect(
348 (void*)vma_start
, vma_end
- vma_start
, PROT_READ
| PROT_WRITE
);
350 Panic("Could remap shared RELRO as writable, should not happen!\n");
353 Panic("remapping shared RELRO to writable failed with: %s\n",
360 if (count_relros
!= expected_count
)
362 "Invalid shared RELRO sections in /proc/self/maps: %d"
367 printf("RELRO count check ok!\n");
376 struct RelroLibrary
{
378 crazy_library_t
* library
;
381 void Init(const char* name
, crazy_context_t
* context
) {
382 printf("Loading %s\n", name
);
384 if (!crazy_library_open(&this->library
, name
, context
)) {
385 Panic("Could not open %s: %s\n", name
, crazy_context_get_error(context
));
389 void Close() { crazy_library_close(this->library
); }
391 void CreateSharedRelro(crazy_context_t
* context
, size_t load_address
) {
392 if (!crazy_library_create_shared_relro(this->library
,
398 Panic("Could not create shared RELRO for %s: %s",
400 crazy_context_get_error(context
));
403 printf("Parent %s relro info relro_start=%p relro_size=%p relro_fd=%d\n",
405 (void*)this->relro
.start
,
406 (void*)this->relro
.size
,
410 void EnableSharedRelro(crazy_context_t
* context
) {
411 CreateSharedRelro(context
, 0);
412 UseSharedRelro(context
);
415 void SendRelroInfo(int fd
) {
416 if (SendFd(fd
, this->relro
.fd
) < 0) {
417 Panic("Could not send %s RELRO fd: %s", this->name
, strerror(errno
));
421 TEMP_FAILURE_RETRY(::write(fd
, &this->relro
, sizeof(this->relro
)));
422 if (ret
!= static_cast<int>(sizeof(this->relro
))) {
423 Panic("Parent could not send %s RELRO info: %s",
429 void ReceiveRelroInfo(int fd
) {
430 // Receive relro information from parent.
432 if (ReceiveFd(fd
, &relro_fd
) < 0) {
433 Panic("Could not receive %s relro descriptor from parent", this->name
);
436 printf("Child received %s relro fd %d\n", this->name
, relro_fd
);
438 int ret
= TEMP_FAILURE_RETRY(::read(fd
, &this->relro
, sizeof(this->relro
)));
439 if (ret
!= static_cast<int>(sizeof(this->relro
))) {
440 Panic("Could not receive %s relro information from parent", this->name
);
443 this->relro
.fd
= relro_fd
;
444 printf("Child received %s relro start=%p size=%p\n",
446 (void*)this->relro
.start
,
447 (void*)this->relro
.size
);
450 void UseSharedRelro(crazy_context_t
* context
) {
451 if (!crazy_library_use_shared_relro(this->library
,
456 Panic("Could not use %s shared RELRO: %s\n",
458 crazy_context_get_error(context
));
465 #endif // TEST_UTIL_H