You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
7.0 KiB
187 lines
7.0 KiB
/*
|
|
* Copyright (C) 2020 The Android Open Source Project
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "linker_note_gnu_property.h"
|
|
|
|
#include <elf.h>
|
|
#include <link.h>
|
|
|
|
#include "linker.h"
|
|
#include "linker_debug.h"
|
|
#include "linker_globals.h"
|
|
#include "linker_soinfo.h"
|
|
|
|
GnuPropertySection::GnuPropertySection(const soinfo* si)
|
|
: GnuPropertySection(si->phdr, si->phnum, si->load_bias, si->get_realpath()) {}
|
|
|
|
GnuPropertySection::GnuPropertySection(const ElfW(Phdr)* phdr, size_t phdr_count,
|
|
const ElfW(Addr) load_bias, const char* name) {
|
|
// Try to find PT_GNU_PROPERTY segment.
|
|
auto note_gnu_property = FindSegment(phdr, phdr_count, load_bias, name);
|
|
// Perform some validity checks.
|
|
if (note_gnu_property && SanityCheck(note_gnu_property, name)) {
|
|
// Parse section.
|
|
Parse(note_gnu_property, name);
|
|
}
|
|
}
|
|
|
|
const ElfW(NhdrGNUProperty)* GnuPropertySection::FindSegment(const ElfW(Phdr)* phdr,
|
|
size_t phdr_count,
|
|
const ElfW(Addr) load_bias,
|
|
const char* name) const {
|
|
// According to Linux gABI extension this segment should contain
|
|
// .note.gnu.property section only.
|
|
if (phdr != nullptr) {
|
|
for (size_t i = 0; i < phdr_count; ++i) {
|
|
if (phdr[i].p_type != PT_GNU_PROPERTY) {
|
|
continue;
|
|
}
|
|
|
|
TRACE("\"%s\" PT_GNU_PROPERTY: found at segment index %zu", name, i);
|
|
|
|
// Check segment size.
|
|
if (phdr[i].p_memsz < sizeof(ElfW(NhdrGNUProperty))) {
|
|
DL_ERR_AND_LOG(
|
|
"\"%s\" PT_GNU_PROPERTY segment is too small. Segment "
|
|
"size is %zu, minimum is %zu.",
|
|
name, static_cast<size_t>(phdr[i].p_memsz), sizeof(ElfW(NhdrGNUProperty)));
|
|
return nullptr;
|
|
}
|
|
|
|
// PT_GNU_PROPERTY contains .note.gnu.property which has SHF_ALLOC
|
|
// attribute, therefore it is loaded.
|
|
auto note_nhdr = reinterpret_cast<ElfW(NhdrGNUProperty)*>(load_bias + phdr[i].p_vaddr);
|
|
|
|
// Check that the n_descsz <= p_memsz
|
|
if ((phdr[i].p_memsz - sizeof(ElfW(NhdrGNUProperty))) < note_nhdr->nhdr.n_descsz) {
|
|
DL_ERR_AND_LOG(
|
|
"\"%s\" PT_GNU_PROPERTY segment p_memsz (%zu) is too small for note n_descsz (%zu).",
|
|
name, static_cast<size_t>(phdr[i].p_memsz),
|
|
static_cast<size_t>(note_nhdr->nhdr.n_descsz));
|
|
return nullptr;
|
|
}
|
|
|
|
return note_nhdr;
|
|
}
|
|
}
|
|
|
|
TRACE("\"%s\" PT_GNU_PROPERTY: not found", name);
|
|
return nullptr;
|
|
}
|
|
|
|
bool GnuPropertySection::SanityCheck(const ElfW(NhdrGNUProperty)* note_nhdr,
|
|
const char* name) const {
|
|
// Check .note section type
|
|
if (note_nhdr->nhdr.n_type != NT_GNU_PROPERTY_TYPE_0) {
|
|
DL_ERR_AND_LOG("\"%s\" .note.gnu.property: unexpected note type. Expected %u, got %u.", name,
|
|
NT_GNU_PROPERTY_TYPE_0, note_nhdr->nhdr.n_type);
|
|
return false;
|
|
}
|
|
|
|
if (note_nhdr->nhdr.n_namesz != 4) {
|
|
DL_ERR_AND_LOG("\"%s\" .note.gnu.property: unexpected name size. Expected 4, got %u.", name,
|
|
note_nhdr->nhdr.n_namesz);
|
|
return false;
|
|
}
|
|
|
|
if (strncmp(note_nhdr->n_name, "GNU", 4) != 0) {
|
|
DL_ERR_AND_LOG("\"%s\" .note.gnu.property: unexpected name. Expected 'GNU', got '%s'.", name,
|
|
note_nhdr->n_name);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GnuPropertySection::Parse(const ElfW(NhdrGNUProperty)* note_nhdr, const char* name) {
|
|
// The total length of the program property array is in _bytes_.
|
|
ElfW(Word) offset = 0;
|
|
while (offset < note_nhdr->nhdr.n_descsz) {
|
|
DEBUG("\"%s\" .note.gnu.property: processing at offset 0x%x", name, offset);
|
|
|
|
// At least the "header" part must fit.
|
|
// The ABI doesn't say that pr_datasz can't be 0.
|
|
if ((note_nhdr->nhdr.n_descsz - offset) < sizeof(ElfW(Prop))) {
|
|
DL_ERR_AND_LOG(
|
|
"\"%s\" .note.gnu.property: no more space left for a "
|
|
"Program Property Note header.",
|
|
name);
|
|
return false;
|
|
}
|
|
|
|
// Loop on program property array.
|
|
const ElfW(Prop)* property = reinterpret_cast<const ElfW(Prop)*>(¬e_nhdr->n_desc[offset]);
|
|
const ElfW(Word) property_size =
|
|
align_up(sizeof(ElfW(Prop)) + property->pr_datasz, sizeof(ElfW(Addr)));
|
|
if ((note_nhdr->nhdr.n_descsz - offset) < property_size) {
|
|
DL_ERR_AND_LOG(
|
|
"\"%s\" .note.gnu.property: property descriptor size is "
|
|
"invalid. Expected at least %u bytes, got %u.",
|
|
name, property_size, note_nhdr->nhdr.n_descsz - offset);
|
|
return false;
|
|
}
|
|
|
|
// Cache found properties.
|
|
switch (property->pr_type) {
|
|
#if defined(__aarch64__)
|
|
case GNU_PROPERTY_AARCH64_FEATURE_1_AND: {
|
|
if (property->pr_datasz != 4) {
|
|
DL_ERR_AND_LOG(
|
|
"\"%s\" .note.gnu.property: property descriptor size is "
|
|
"invalid. Expected %u bytes for GNU_PROPERTY_AARCH64_FEATURE_1_AND, got %u.",
|
|
name, 4, property->pr_datasz);
|
|
return false;
|
|
}
|
|
|
|
const ElfW(Word) flags = *reinterpret_cast<const ElfW(Word)*>(&property->pr_data[0]);
|
|
properties_.bti_compatible = (flags & GNU_PROPERTY_AARCH64_FEATURE_1_BTI) != 0;
|
|
if (properties_.bti_compatible) {
|
|
INFO("[ BTI compatible: \"%s\" ]", name);
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
DEBUG("\"%s\" .note.gnu.property: found property pr_type %u pr_datasz 0x%x", name,
|
|
property->pr_type, property->pr_datasz);
|
|
break;
|
|
}
|
|
|
|
// Move offset, this should be safe to add because of previous checks.
|
|
offset += property_size;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#if defined(__aarch64__)
|
|
bool GnuPropertySection::IsBTICompatible() const {
|
|
return (g_platform_properties.bti_supported && properties_.bti_compatible);
|
|
}
|
|
#endif
|