refactoring: introduce MappedFileFragment

Change-Id: I97b0c1131711ed0ac13ab33e3357e99e0b6b1a4e
diff --git a/linker/Android.mk b/linker/Android.mk
index 8be53a1..0188a49 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -12,6 +12,7 @@
     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 51f8d4c..17c6986 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 0000000..6a500ef
--- /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 <inttypes.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+constexpr off64_t kPageMask = ~static_cast<off64_t>(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<uint64_t>(INT64_MAX - a) < b) {
+    return false;
+  }
+
+  *out = a + b;
+  return true;
+}
+
+static size_t page_offset(off64_t offset) {
+  return static_cast<size_t>(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<size_t>(end_offset - page_min);
+  CHECK(map_size >= size);
+
+  uint8_t* map_start = static_cast<uint8_t*>(
+                          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 0000000..91bd077
--- /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 <unistd.h>
+
+#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 30bc6fa..6fe8084 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -134,18 +134,11 @@
                                       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 @@
     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<ElfW(Phdr)*>(reinterpret_cast<char*>(mmap_result) + page_offset);
+  phdr_table_ = static_cast<ElfW(Phdr)*>(phdr_fragment_.data());
   return true;
 }
 
@@ -829,7 +813,7 @@
 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 55196fd..4e02197 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 @@
   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_;