blob: a9f392705125b01c665db4144a15d581463aac50 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "crazy_linker_system.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#ifdef __ANDROID__
#include <android/log.h>
#endif
#include "crazy_linker_util.h"
// Note: unit-testing support files are in crazy_linker_files_mock.cpp
namespace crazy {
String MakeDirectoryPath(const char* parent) {
return MakeDirectoryPath(parent, ::strlen(parent));
}
String MakeDirectoryPath(const char* parent, size_t parent_len) {
if (parent_len == 0) {
// Special case for empty inputs.
return String("./");
}
String result(parent);
if (parent_len > 0 && parent[parent_len - 1] != '/') {
result += '/';
}
return result;
}
String MakeAbsolutePathFrom(const char* path) {
return MakeAbsolutePathFrom(path, ::strlen(path));
}
String MakeAbsolutePathFrom(const char* path, size_t path_len) {
if (path[0] == '/') {
return String(path, path_len);
} else {
String cur_dir = GetCurrentDirectory();
String result = MakeDirectoryPath(cur_dir.c_str(), cur_dir.size());
result.Append(path, path_len);
return result;
}
}
bool IsSystemLibraryPath(const char* lib_path) {
static const char* kSystemPrefixes[] = {
#ifdef __ANDROID__
// From recent Android linker sources ($AOSP/bionic/linker/linker.cpp).
"/system/lib64/", "/odm/lib64/", "/vendor/lib64/",
"/data/asan/system/lib64/", "/data/asan/odm/lib64/",
"/data/asan/vendor/lib64/",
// It's ok to mix 32-bit and 64-bit paths in the same list here.
"/system/lib/", "/odm/lib/", "/vendor/lib/", "/data/asan/system/lib/",
"/data/asan/odm/lib/", "/data/asan/vendor/lib/",
#else
// Typical system library directories for Linux systems.
"/lib/", "/lib32/", "/lib64/",
"/libx32/", "/usr/lib/", "/usr/lib32/",
"/usr/lib64/", "/usr/libx32/", "/usr/local/lib/",
#endif
};
size_t lib_path_len = ::strlen(lib_path);
for (const char* prefix : kSystemPrefixes) {
size_t prefix_len = ::strlen(prefix);
if (prefix_len < lib_path_len && !::memcmp(prefix, lib_path, prefix_len))
return true;
}
return false;
}
} // namespace crazy
#ifndef UNIT_TEST
namespace crazy {
ssize_t FileDescriptor::Read(void* buffer, size_t buffer_size) {
return TEMP_FAILURE_RETRY(::read(fd_, buffer, buffer_size));
}
off_t FileDescriptor::SeekTo(off_t offset) {
return ::lseek(fd_, offset, SEEK_SET);
}
void* FileDescriptor::Map(void* address,
size_t length,
int prot,
int flags,
off_t offset) {
void* mem = ::mmap(address, length, prot, flags, fd_, offset);
return (mem == MAP_FAILED) ? nullptr : mem;
}
int64_t FileDescriptor::GetFileSize() const {
struct stat stat_buf;
if (fstat(fd_, &stat_buf) == -1) {
return -1;
}
// |st_size| is an off_t which is always signed, but can be 32-bit or
// 64-bit depending on the platform. Always convert to a signed 64-bit
// to ensure the client always deal properly with both cases.
return static_cast<int64_t>(stat_buf.st_size);
}
// static
int FileDescriptor::DoOpenReadOnly(const char* path) {
return TEMP_FAILURE_RETRY(::open(path, O_RDONLY));
}
// static
int FileDescriptor::DoOpenReadWrite(const char* path) {
return TEMP_FAILURE_RETRY(::open(path, O_RDWR));
}
// static
void FileDescriptor::DoClose(int fd) {
int old_errno = errno;
// SUBTLE: Do not loop when close() returns EINTR. On Linux, this simply
// means that a corresponding flush operation failed, but the file
// descriptor will always be closed anyway.
//
// Other platforms have different behavior: e.g. on OS X, this could be
// the result of an interrupt, and there is no reliable way to know
// whether the fd was closed or not on exit :-(
(void)close(fd);
errno = old_errno;
}
const char* GetEnv(const char* var_name) { return ::getenv(var_name); }
String GetCurrentDirectory() {
String result;
size_t capacity = 128;
for (;;) {
result.Resize(capacity);
if (getcwd(&result[0], capacity))
break;
capacity *= 2;
}
return result;
}
bool PathExists(const char* path) {
struct stat st;
if (TEMP_FAILURE_RETRY(stat(path, &st)) < 0)
return false;
return S_ISREG(st.st_mode) || S_ISDIR(st.st_mode);
}
bool PathIsFile(const char* path) {
struct stat st;
if (TEMP_FAILURE_RETRY(stat(path, &st)) < 0)
return false;
return S_ISREG(st.st_mode);
}
} // namespace crazy
#if !defined(CRAZY_LINKER_ENABLE_FUZZING)
// Custom implementation of new and malloc, this prevents dragging
// the libc++ implementation, which drags exception-related machine
// code that is not needed here. This helps reduce the size of the
// final binary considerably.
// IMPORTANT: These symbols are not exported by the crazy linker, thus this
// does not affect the libraries that it will load, only the
// linker binary itself!
//
void* operator new(size_t size) {
void* ptr = ::malloc(size);
if (ptr != nullptr)
return ptr;
// Don't assume it is possible to call any C library function like
// snprintf() here, since it might allocate heap memory and crash at
// runtime. Hence our fatal message does not contain the number of
// bytes requested by the allocation.
static const char kFatalMessage[] = "Out of memory!";
#ifdef __ANDROID__
__android_log_write(ANDROID_LOG_FATAL, "crazy_linker", kFatalMessage);
#else
::write(STDERR_FILENO, kFatalMessage, sizeof(kFatalMessage) - 1);
#endif
_exit(1);
#if defined(__GNUC__)
__builtin_unreachable();
#endif
// NOTE: Adding a 'return nullptr' here will make the compiler error
// with a message stating that 'operator new(size_t)' is not allowed
// to return nullptr.
//
// Indeed, an new expression like 'new T' shall never return nullptr,
// according to the C++ specification, and an optimizing compiler will gladly
// remove any null-checks after them (something the Fuschsia team had to
// learn the hard way when writing their kernel in C++). What is meant here
// is something like:
//
// Foo* foo = new Foo(10);
// if (!foo) { <-- entire check and branch
// ... Handle out-of-memory condition. <-- removed by an optimizing
// } <-- compiler.
//
// Note that some C++ library implementations (e.g. recent libc++) recognize
// when they are compiled with -fno-exceptions and provide a simpler version
// of operator new that can return nullptr. However, it is very hard to
// guarantee at build time that this code is linked against such a version
// of the runtime. Moreoever, technically disabling exceptions is completely
// out-of-spec regarding the C++ language, and what the compiler is allowed
// to do in this case is mostly implementation-defined, so better be safe
// than sorry here.
//
// C++ provides a non-throwing new expression that can return a nullptr
// value, but it must be written as 'new (std::nothrow) T' instead of
// 'new T', and thus nobody uses this. This ends up calling
// 'operator new(size_t, const std::nothrow_t&)' which is not implemented
// here.
}
void* operator new[](size_t size) {
return operator new(size);
}
void operator delete(void* ptr) {
// The compiler-generated code already checked that |ptr != nullptr|
// so don't to it a second time.
::free(ptr);
}
void operator delete[](void* ptr) {
::free(ptr);
}
#endif // !CRAZY_LINKER_ENABLE_FUZZING
#endif // !UNIT_TEST