diff --git a/linker/Android.mk b/linker/Android.mk index 8be53a195..0188a4922 100644 --- a/linker/Android.mk +++ b/linker/Android.mk @@ -12,6 +12,7 @@ LOCAL_SRC_FILES := \ linker_sdk_versions.cpp \ linker_block_allocator.cpp \ linker_libc_support.c \ + linker_mapped_file_fragment.cpp \ linker_memory.cpp \ linker_phdr.cpp \ linker_utils.cpp \ diff --git a/linker/linker_debug.h b/linker/linker_debug.h index 51f8d4c18..17c6986f0 100644 --- a/linker/linker_debug.h +++ b/linker/linker_debug.h @@ -58,6 +58,13 @@ __LIBC_HIDDEN__ extern int g_ld_debug_verbosity; +#define CHECK(predicate) { \ + if (!(predicate)) { \ + __libc_fatal("%s:%d: %s CHECK '" #predicate "' failed", \ + __FILE__, __LINE__, __FUNCTION__); \ + } \ + } + #if LINKER_DEBUG_TO_LOG #define _PRINTVF(v, x...) \ do { \ diff --git a/linker/linker_mapped_file_fragment.cpp b/linker/linker_mapped_file_fragment.cpp new file mode 100644 index 000000000..6a500ef1e --- /dev/null +++ b/linker/linker_mapped_file_fragment.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "linker_mapped_file_fragment.h" +#include "linker_debug.h" + +#include +#include +#include +#include + +constexpr off64_t kPageMask = ~static_cast(PAGE_SIZE-1); + +static off64_t page_start(off64_t offset) { + return offset & kPageMask; +} + +static bool safe_add(off64_t* out, off64_t a, size_t b) { + CHECK(a >= 0); + if (static_cast(INT64_MAX - a) < b) { + return false; + } + + *out = a + b; + return true; +} + +static size_t page_offset(off64_t offset) { + return static_cast(offset & (PAGE_SIZE-1)); +} + +MappedFileFragment::MappedFileFragment() : map_start_(nullptr), map_size_(0), + data_(nullptr), size_ (0) +{ } + +MappedFileFragment::~MappedFileFragment() { + if (map_start_ != nullptr) { + munmap(map_start_, map_size_); + } +} + +bool MappedFileFragment::Map(int fd, off64_t base_offset, size_t elf_offset, size_t size) { + off64_t offset; + CHECK(safe_add(&offset, base_offset, elf_offset)); + + off64_t page_min = page_start(offset); + off64_t end_offset; + + CHECK(safe_add(&end_offset, offset, size)); + CHECK(safe_add(&end_offset, end_offset, page_offset(offset))); + + size_t map_size = static_cast(end_offset - page_min); + CHECK(map_size >= size); + + uint8_t* map_start = static_cast( + mmap64(nullptr, map_size, PROT_READ, MAP_PRIVATE, fd, page_min)); + + if (map_start == MAP_FAILED) { + return false; + } + + map_start_ = map_start; + map_size_ = map_size; + + data_ = map_start + page_offset(offset); + size_ = size; + + return true; +} diff --git a/linker/linker_mapped_file_fragment.h b/linker/linker_mapped_file_fragment.h new file mode 100644 index 000000000..91bd077bd --- /dev/null +++ b/linker/linker_mapped_file_fragment.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LINKER_MAPPED_FILE_FRAGMENT_H +#define LINKER_MAPPED_FILE_FRAGMENT_H + +#include + +#include "private/bionic_macros.h" + +class MappedFileFragment { + public: + MappedFileFragment(); + ~MappedFileFragment(); + + bool Map(int fd, off64_t base_offset, size_t elf_offset, size_t size); + + void* data() const { return data_; } + size_t size() const { return size_; } + private: + void* map_start_; + size_t map_size_; + void* data_; + size_t size_; + + DISALLOW_COPY_AND_ASSIGN(MappedFileFragment); +}; + +#endif /* LINKER_MAPPED_FILE_FRAGMENT_H */ diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp index 30bc6fabd..6fe808421 100644 --- a/linker/linker_phdr.cpp +++ b/linker/linker_phdr.cpp @@ -134,18 +134,11 @@ static int GetTargetElfMachine() { MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE)) ElfReader::ElfReader(const char* name, int fd, off64_t file_offset, off64_t file_size) - : name_(name), fd_(fd), file_offset_(file_offset), file_size_(file_size), - phdr_num_(0), phdr_mmap_(nullptr), phdr_table_(nullptr), phdr_size_(0), - load_start_(nullptr), load_size_(0), load_bias_(0), + : name_(name), fd_(fd), file_offset_(file_offset), file_size_(file_size), phdr_num_(0), + phdr_table_(nullptr), load_start_(nullptr), load_size_(0), load_bias_(0), loaded_phdr_(nullptr) { } -ElfReader::~ElfReader() { - if (phdr_mmap_ != nullptr) { - munmap(phdr_mmap_, phdr_size_); - } -} - bool ElfReader::Load(const android_dlextinfo* extinfo) { return ReadElfHeader() && VerifyElfHeader() && @@ -234,21 +227,12 @@ bool ElfReader::ReadProgramHeader() { return false; } - ElfW(Addr) page_min = PAGE_START(header_.e_phoff); - ElfW(Addr) page_max = PAGE_END(header_.e_phoff + (phdr_num_ * sizeof(ElfW(Phdr)))); - ElfW(Addr) page_offset = PAGE_OFFSET(header_.e_phoff); - - phdr_size_ = page_max - page_min; - - void* mmap_result = - mmap64(nullptr, phdr_size_, PROT_READ, MAP_PRIVATE, fd_, file_offset_ + page_min); - if (mmap_result == MAP_FAILED) { + if (!phdr_fragment_.Map(fd_, file_offset_, header_.e_phoff, phdr_num_ * sizeof(ElfW(Phdr)))) { DL_ERR("\"%s\" phdr mmap failed: %s", name_, strerror(errno)); return false; } - phdr_mmap_ = mmap_result; - phdr_table_ = reinterpret_cast(reinterpret_cast(mmap_result) + page_offset); + phdr_table_ = static_cast(phdr_fragment_.data()); return true; } @@ -829,7 +813,7 @@ bool ElfReader::FindPhdr() { bool ElfReader::CheckPhdr(ElfW(Addr) loaded) { const ElfW(Phdr)* phdr_limit = phdr_table_ + phdr_num_; ElfW(Addr) loaded_end = loaded + (phdr_num_ * sizeof(ElfW(Phdr))); - for (ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) { + for (const ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) { if (phdr->p_type != PT_LOAD) { continue; } diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h index 55196fd9a..4e0219701 100644 --- a/linker/linker_phdr.h +++ b/linker/linker_phdr.h @@ -36,11 +36,11 @@ */ #include "linker.h" +#include "linker_mapped_file_fragment.h" class ElfReader { public: ElfReader(const char* name, int fd, off64_t file_offset, off64_t file_size); - ~ElfReader(); bool Load(const android_dlextinfo* extinfo); @@ -67,9 +67,8 @@ class ElfReader { ElfW(Ehdr) header_; size_t phdr_num_; - void* phdr_mmap_; - ElfW(Phdr)* phdr_table_; - ElfW(Addr) phdr_size_; + MappedFileFragment phdr_fragment_; + const ElfW(Phdr)* phdr_table_; // First page of reserved address space. void* load_start_;