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>
26 #ifndef __STDC_FORMAT_MACROS
27 #define __STDC_FORMAT_MACROS // to get PRI and SCN in 32-bit inttypes.h
33 // Print an error message and exit the process.
34 // Message must be terminated by a newline.
35 inline void Panic(const char* fmt
, ...) {
37 fprintf(stderr
, "PANIC: ");
39 vfprintf(stderr
, fmt
, args
);
44 // Print an error message, the errno message, then exit the process.
45 // Message must not be terminated by a newline.
46 inline void PanicErrno(const char* fmt
, ...) {
47 int old_errno
= errno
;
49 fprintf(stderr
, "PANIC: ");
51 vfprintf(stderr
, fmt
, args
);
53 fprintf(stderr
, ": %s\n", strerror(old_errno
));
57 // Simple string class.
60 String() : str_(NULL
), len_(0) {}
62 String(const String
& other
) { String(other
.str_
, other
.len_
); }
64 String(const char* str
) { String(str
, strlen(str
)); }
66 String(const char* str
, size_t len
) : str_(NULL
), len_(0) {
77 String
& operator+=(const char* str
) {
78 Append(str
, strlen(str
));
82 String
& operator+=(const String
& other
) {
83 Append(other
.str_
, other
.len_
);
87 String
& operator+=(char ch
) {
92 const char* c_str() const { return len_
? str_
: ""; }
93 char* ptr() { return str_
; }
94 size_t size() const { return len_
; }
96 void Append(const char* str
, size_t len
) {
97 size_t old_len
= len_
;
99 memcpy(str_
+ old_len
, str
, len
);
102 void Resize(size_t len
) {
103 str_
= reinterpret_cast<char*>(realloc(str_
, len
+ 1));
105 memset(str_
+ len_
, '\0', len
- len_
);
110 void Format(const char* fmt
, ...) {
116 va_copy(args2
, args
);
117 int ret
= vsnprintf(str_
, len_
+ 1, fmt
, args2
);
119 if (ret
< static_cast<int>(len_
+ 1))
131 // Helper class to create a temporary directory that gets deleted on scope exit,
132 // as well as all regular files it contains.
133 class TempDirectory
{
136 snprintf(path_
, sizeof path_
, "/data/local/tmp/temp-XXXXXX");
138 Panic("Could not create temporary directory name: %s\n", strerror(errno
));
139 if (mkdir(path_
, 0700) < 0)
140 Panic("Could not create temporary directory %s: %s\n", strerror(errno
));
144 // Remove any file in this directory.
145 DIR* d
= opendir(path_
);
147 Panic("Could not open directory %s: %s\n", strerror(errno
));
149 struct dirent
* entry
;
150 while ((entry
= readdir(d
)) != NULL
) {
151 if (!strcmp(entry
->d_name
, ".") || !strcmp(entry
->d_name
, ".."))
154 file_path
.Format("%s/%s", path_
, entry
->d_name
);
155 if (unlink(file_path
.c_str()) < 0)
156 Panic("Could not remove %s: %s\n", file_path
.c_str(), strerror(errno
));
160 if (rmdir(path_
) < 0)
161 Panic("Could not remove dir %s: %s\n", path_
, strerror(errno
));
164 const char* path() const { return path_
; }
167 char path_
[PATH_MAX
];
170 // Scoped FILE* class. Always closed on destruction.
173 ScopedFILE() : file_(NULL
) {}
182 void Open(const char* path
, const char* mode
) {
183 file_
= fopen(path
, mode
);
185 Panic("Could not open file for reading: %s: %s\n", path
, strerror(errno
));
188 FILE* file() const { return file_
; }
194 // Retrieve current executable path as a String.
195 inline String
GetCurrentExecutable() {
197 path
.Resize(PATH_MAX
);
199 TEMP_FAILURE_RETRY(readlink("/proc/self/exe", path
.ptr(), path
.size()));
201 Panic("Could not read /proc/self/exe: %s\n", strerror(errno
));
206 // Retrieve current executable directory as a String.
207 inline String
GetCurrentExecutableDirectory() {
208 String path
= GetCurrentExecutable();
210 const char* p
= reinterpret_cast<const char*>(strrchr(path
.c_str(), '/'));
212 Panic("Current executable does not have directory root?: %s\n",
215 path
.Resize(p
- path
.c_str());
219 // Copy a file named |src_file_name| in directory |src_file_dir| into
220 // a file named |dst_file_name| in directory |dst_file_dir|. Panics on error.
221 inline void CopyFile(const char* src_file_name
,
222 const char* src_file_dir
,
223 const char* dst_file_name
,
224 const char* dst_file_dir
) {
226 src_path
.Format("%s/%s", src_file_dir
, src_file_name
);
229 src_file
.Open(src_path
.c_str(), "rb");
232 dst_path
.Format("%s/%s", dst_file_dir
, dst_file_name
);
234 dst_file
.Open(dst_path
.c_str(), "wb");
238 size_t read
= fread(buffer
, 1, sizeof buffer
, src_file
.file());
240 size_t written
= fwrite(buffer
, 1, read
, dst_file
.file());
242 Panic("Wrote %d bytes instead of %d into %s\n",
247 if (read
< sizeof buffer
)
252 // Send a file descriptor |fd| through |socket|.
253 // Return 0 on success, -1/errno on failure.
254 inline int SendFd(int socket
, int fd
) {
260 iov
.iov_base
= buffer
;
264 struct cmsghdr
* cmsg
;
265 char cms
[CMSG_SPACE(sizeof(int))];
267 ::memset(&msg
, 0, sizeof(msg
));
270 msg
.msg_control
= reinterpret_cast<caddr_t
>(cms
);
271 msg
.msg_controllen
= CMSG_LEN(sizeof(int));
273 cmsg
= CMSG_FIRSTHDR(&msg
);
274 cmsg
->cmsg_len
= CMSG_LEN(sizeof(int));
275 cmsg
->cmsg_level
= SOL_SOCKET
;
276 cmsg
->cmsg_type
= SCM_RIGHTS
;
277 ::memcpy(CMSG_DATA(cmsg
), &fd
, sizeof(int));
279 int ret
= sendmsg(socket
, &msg
, 0);
283 if (ret
!= iov
.iov_len
) {
291 inline int ReceiveFd(int socket
, int* fd
) {
295 iov
.iov_base
= buffer
;
299 struct cmsghdr
* cmsg
;
300 char cms
[CMSG_SPACE(sizeof(int))];
302 ::memset(&msg
, 0, sizeof msg
);
308 msg
.msg_control
= reinterpret_cast<caddr_t
>(cms
);
309 msg
.msg_controllen
= sizeof(cms
);
311 int ret
= recvmsg(socket
, &msg
, 0);
319 cmsg
= CMSG_FIRSTHDR(&msg
);
320 ::memcpy(fd
, CMSG_DATA(cmsg
), sizeof(int));
324 // Check that there are exactly |expected_count| memory mappings in
325 // /proc/self/maps that point to a RELRO ashmem region.
326 inline void CheckRelroMaps(int expected_count
) {
327 printf("Checking for %d RELROs in /proc/self/maps\n", expected_count
);
329 FILE* file
= fopen("/proc/self/maps", "rb");
331 Panic("Could not open /proc/self/maps (pid %d): %s\n",
336 int count_relros
= 0;
337 printf("proc/%d/maps:\n", getpid());
338 while (fgets(line
, sizeof line
, file
)) {
339 if (strstr(line
, "with_relro")) {
340 // The supported library names are "lib<name>_with_relro.so".
342 if (strstr(line
, "/dev/ashmem/RELRO:")) {
344 // Check that they are read-only mappings.
345 if (!strstr(line
, " r--"))
346 Panic("Shared RELRO mapping is not readonly!\n");
347 // Check that they can't be remapped read-write.
348 uint64_t vma_start
, vma_end
;
349 if (sscanf(line
, "%" SCNx64
"-%" SCNx64
, &vma_start
, &vma_end
) != 2)
350 Panic("Could not parse VM address range!\n");
351 int ret
= ::mprotect(
352 (void*)vma_start
, vma_end
- vma_start
, PROT_READ
| PROT_WRITE
);
354 Panic("Could remap shared RELRO as writable, should not happen!\n");
357 Panic("remapping shared RELRO to writable failed with: %s\n",
364 if (count_relros
!= expected_count
)
366 "Invalid shared RELRO sections in /proc/self/maps: %d"
371 printf("RELRO count check ok!\n");
380 struct RelroLibrary
{
382 crazy_library_t
* library
;
385 void Init(const char* name
, crazy_context_t
* context
) {
386 printf("Loading %s\n", name
);
388 if (!crazy_library_open(&this->library
, name
, context
)) {
389 Panic("Could not open %s: %s\n", name
, crazy_context_get_error(context
));
393 void Close() { crazy_library_close(this->library
); }
395 void CreateSharedRelro(crazy_context_t
* context
, size_t load_address
) {
396 if (!crazy_library_create_shared_relro(this->library
,
402 Panic("Could not create shared RELRO for %s: %s",
404 crazy_context_get_error(context
));
407 printf("Parent %s relro info relro_start=%p relro_size=%p relro_fd=%d\n",
409 (void*)this->relro
.start
,
410 (void*)this->relro
.size
,
414 void EnableSharedRelro(crazy_context_t
* context
) {
415 CreateSharedRelro(context
, 0);
416 UseSharedRelro(context
);
419 void SendRelroInfo(int fd
) {
420 if (SendFd(fd
, this->relro
.fd
) < 0) {
421 Panic("Could not send %s RELRO fd: %s", this->name
, strerror(errno
));
425 TEMP_FAILURE_RETRY(::write(fd
, &this->relro
, sizeof(this->relro
)));
426 if (ret
!= static_cast<int>(sizeof(this->relro
))) {
427 Panic("Parent could not send %s RELRO info: %s",
433 void ReceiveRelroInfo(int fd
) {
434 // Receive relro information from parent.
436 if (ReceiveFd(fd
, &relro_fd
) < 0) {
437 Panic("Could not receive %s relro descriptor from parent", this->name
);
440 printf("Child received %s relro fd %d\n", this->name
, relro_fd
);
442 int ret
= TEMP_FAILURE_RETRY(::read(fd
, &this->relro
, sizeof(this->relro
)));
443 if (ret
!= static_cast<int>(sizeof(this->relro
))) {
444 Panic("Could not receive %s relro information from parent", this->name
);
447 this->relro
.fd
= relro_fd
;
448 printf("Child received %s relro start=%p size=%p\n",
450 (void*)this->relro
.start
,
451 (void*)this->relro
.size
);
454 void UseSharedRelro(crazy_context_t
* context
) {
455 if (!crazy_library_use_shared_relro(this->library
,
460 Panic("Could not use %s shared RELRO: %s\n",
462 crazy_context_get_error(context
));
469 #endif // TEST_UTIL_H