blob: 56c2bdb6a29941a2db06010edd144e7f45d66c0d [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_elf_view.h"
#include <errno.h>
#include "crazy_linker_debug.h"
#include "crazy_linker_error.h"
#include "linker_phdr.h"
namespace crazy {
bool ElfView::InitUnmapped(ELF::Addr load_address,
const ELF::Phdr* phdr,
size_t phdr_count,
Error* error) {
// Compute load size and bias.
ELF::Addr min_vaddr = 0;
load_size_ = phdr_table_get_load_size(phdr, phdr_count, &min_vaddr, NULL);
if (load_size_ == 0) {
*error = "Invalid program header table";
return false;
}
load_address_ = (load_address ? load_address : min_vaddr);
load_bias_ = load_address - min_vaddr;
// Extract the dynamic table information.
phdr_table_get_dynamic_section(phdr,
phdr_count,
load_bias_,
&dynamic_,
&dynamic_count_,
&dynamic_flags_);
if (!dynamic_) {
*error = "No PT_DYNAMIC section!";
return false;
}
// Compute the program header table address relative to load_address.
// This is different from |phdr|..|phdr + phdr_count| which can actually
// be at a different location.
const ELF::Phdr* phdr0 = NULL;
// First, if there is a PT_PHDR, use it directly.
for (size_t n = 0; n < phdr_count; ++n) {
const ELF::Phdr* entry = &phdr[n];
if (entry->p_type == PT_PHDR) {
phdr0 = entry;
break;
}
}
// Otherwise, check the first loadable segment. If its file offset
// is 0, it starts with the ELF header, and we can trivially find the
// loaded program header from it.
if (!phdr0) {
for (size_t n = 0; n < phdr_count; ++n) {
const ELF::Phdr* entry = &phdr[n];
if (entry->p_type == PT_LOAD) {
if (entry->p_offset == 0) {
ELF::Addr elf_addr = load_bias_ + entry->p_vaddr;
const ELF::Ehdr* ehdr = reinterpret_cast<const ELF::Ehdr*>(elf_addr);
ELF::Addr offset = ehdr->e_phoff;
phdr0 = reinterpret_cast<const ELF::Phdr*>(elf_addr + offset);
}
break;
}
}
}
// Check that the program header table is indeed in a loadable segment,
// this helps catching malformed ELF binaries.
if (phdr0) {
ELF::Addr phdr0_addr = reinterpret_cast<ELF::Addr>(phdr0);
ELF::Addr phdr0_limit = phdr0_addr + sizeof(ELF::Phdr) * phdr_count;
bool found = false;
for (size_t n = 0; n < phdr_count; ++n) {
size_t seg_start = load_bias_ + phdr[n].p_vaddr;
size_t seg_end = seg_start + phdr[n].p_filesz;
if (seg_start <= phdr0_addr && phdr0_limit <= seg_end) {
found = true;
break;
}
}
if (!found)
phdr0 = NULL;
}
if (!phdr0) {
*error = "Malformed ELF binary";
return false;
}
phdr_ = phdr0;
phdr_count_ = phdr_count;
LOG("New ELF view [load_address:%p, load_size:%p, load_bias:%p, phdr:%p, "
"phdr_count:%d, dynamic:%p, dynamic_count:%d, dynamic_flags:%d",
load_address_, load_size_, load_bias_, phdr_, phdr_count_, dynamic_,
dynamic_count_, dynamic_flags_);
return true;
}
bool ElfView::ProtectRelroSection(Error* error) {
LOG("Enabling GNU RELRO protection");
if (phdr_table_protect_gnu_relro(phdr_, phdr_count_, load_bias_) < 0) {
error->Format("Can't enable GNU RELRO protection: %s", strerror(errno));
return false;
}
return true;
}
} // namespace crazy