Merge "Enable BTI in bionic linker" am: 7241ac6c07
am: dc0dd76dfb
Original change: https://android-review.googlesource.com/c/platform/bionic/+/1242754 Change-Id: I5c3182ee75ab371af884ec20e2f62f381c95cbf4
This commit is contained in:
commit
dcf91e88e4
12 changed files with 816 additions and 13 deletions
|
@ -89,4 +89,9 @@
|
|||
#define R_AARCH64_TLSDESC 1031 /* 16-byte descriptor: resolver func + arg. */
|
||||
#define R_AARCH64_IRELATIVE 1032
|
||||
|
||||
/* Dynamic array tags */
|
||||
#define DT_AARCH64_BTI_PLT 0x70000001
|
||||
#define DT_AARCH64_PAC_PLT 0x70000003
|
||||
#define DT_AARCH64_VARIANT_PCS 0x70000005
|
||||
|
||||
#endif /* _AARCH64_ELF_MACHDEP_H_ */
|
||||
|
|
|
@ -169,6 +169,7 @@ filegroup {
|
|||
"linker_namespaces.cpp",
|
||||
"linker_logger.cpp",
|
||||
"linker_mapped_file_fragment.cpp",
|
||||
"linker_note_gnu_property.cpp",
|
||||
"linker_phdr.cpp",
|
||||
"linker_relocate.cpp",
|
||||
"linker_sdk_versions.cpp",
|
||||
|
@ -447,6 +448,7 @@ cc_test {
|
|||
"linker_block_allocator_test.cpp",
|
||||
"linker_config_test.cpp",
|
||||
"linked_list_test.cpp",
|
||||
"linker_note_gnu_property_test.cpp",
|
||||
"linker_sleb128_test.cpp",
|
||||
"linker_utils_test.cpp",
|
||||
"linker_gnu_hash_test.cpp",
|
||||
|
@ -455,6 +457,7 @@ cc_test {
|
|||
"linker_block_allocator.cpp",
|
||||
"linker_config.cpp",
|
||||
"linker_debug.cpp",
|
||||
"linker_note_gnu_property.cpp",
|
||||
"linker_test_globals.cpp",
|
||||
"linker_utils.cpp",
|
||||
],
|
||||
|
|
|
@ -3141,6 +3141,14 @@ bool soinfo::prelink_image() {
|
|||
// resolves everything eagerly, so these can be ignored.
|
||||
break;
|
||||
|
||||
#if defined(__aarch64__)
|
||||
case DT_AARCH64_BTI_PLT:
|
||||
case DT_AARCH64_PAC_PLT:
|
||||
case DT_AARCH64_VARIANT_PCS:
|
||||
// Ignored: AArch64 processor-specific dynamic array tags.
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
if (!relocating_linker) {
|
||||
const char* tag_name;
|
||||
|
|
|
@ -181,3 +181,9 @@ struct address_space_params {
|
|||
int get_application_target_sdk_version();
|
||||
ElfW(Versym) find_verdef_version_index(const soinfo* si, const version_info* vi);
|
||||
bool validate_verdef_section(const soinfo* si);
|
||||
|
||||
struct platform_properties {
|
||||
#if defined(__aarch64__)
|
||||
bool bti_supported = false;
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -40,6 +40,8 @@ android_namespace_t g_default_namespace;
|
|||
|
||||
std::unordered_map<uintptr_t, soinfo*> g_soinfo_handles_map;
|
||||
|
||||
platform_properties g_platform_properties;
|
||||
|
||||
static char __linker_dl_err_buf[768];
|
||||
|
||||
char* linker_get_error_buffer() {
|
||||
|
|
|
@ -79,11 +79,14 @@ extern char** g_envp;
|
|||
|
||||
struct soinfo;
|
||||
struct android_namespace_t;
|
||||
struct platform_properties;
|
||||
|
||||
extern android_namespace_t g_default_namespace;
|
||||
|
||||
extern std::unordered_map<uintptr_t, soinfo*> g_soinfo_handles_map;
|
||||
|
||||
extern platform_properties g_platform_properties;
|
||||
|
||||
// Error buffer "variable"
|
||||
char* linker_get_error_buffer();
|
||||
size_t linker_get_error_buffer_size();
|
||||
|
|
|
@ -297,6 +297,13 @@ static ExecutableInfo load_executable(const char* orig_path) {
|
|||
return result;
|
||||
}
|
||||
|
||||
static void platform_properties_init() {
|
||||
#if defined(__aarch64__)
|
||||
const unsigned long hwcap2 = getauxval(AT_HWCAP2);
|
||||
g_platform_properties.bti_supported = (hwcap2 & HWCAP2_BTI) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load) {
|
||||
ProtectedDataGuard guard;
|
||||
|
||||
|
@ -311,6 +318,9 @@ static ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load
|
|||
// Initialize system properties
|
||||
__system_properties_init(); // may use 'environ'
|
||||
|
||||
// Initialize platform properties.
|
||||
platform_properties_init();
|
||||
|
||||
// Register the debuggerd signal handler.
|
||||
linker_debuggerd_init();
|
||||
|
||||
|
@ -381,6 +391,20 @@ static ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load
|
|||
solinker->set_realpath(interp);
|
||||
init_link_map_head(*solinker);
|
||||
|
||||
#if defined(__aarch64__)
|
||||
if (exe_to_load == nullptr) {
|
||||
// Kernel does not add PROT_BTI to executable pages of the loaded ELF.
|
||||
// Apply appropriate protections here if it is needed.
|
||||
auto note_gnu_property = GnuPropertySection(somain);
|
||||
if (note_gnu_property.IsBTICompatible() &&
|
||||
(phdr_table_protect_segments(somain->phdr, somain->phnum, somain->load_bias,
|
||||
¬e_gnu_property) < 0)) {
|
||||
__linker_error("error: can't protect segments for \"%s\": %s", exe_info.path.c_str(),
|
||||
strerror(errno));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Register the main executable and the linker upfront to have
|
||||
// gdb aware of them before loading the rest of the dependency
|
||||
// tree.
|
||||
|
|
186
linker/linker_note_gnu_property.cpp
Normal file
186
linker/linker_note_gnu_property.cpp
Normal file
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* 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
|
93
linker/linker_note_gnu_property.h
Normal file
93
linker/linker_note_gnu_property.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <elf.h>
|
||||
#include <link.h>
|
||||
|
||||
#include "linker_soinfo.h"
|
||||
|
||||
// The Elf* structures below are derived from the document
|
||||
// Linux Extensions to gABI (https://github.com/hjl-tools/linux-abi/wiki).
|
||||
// Essentially, these types would be defined in <elf.h>, but this is not
|
||||
// the case at the moment.
|
||||
|
||||
struct Elf32_Prop {
|
||||
Elf32_Word pr_type;
|
||||
Elf32_Word pr_datasz;
|
||||
char pr_data[0];
|
||||
};
|
||||
|
||||
// On 32-bit machines this should be 4-byte aligned.
|
||||
struct Elf32_NhdrGNUProperty {
|
||||
Elf32_Nhdr nhdr;
|
||||
char n_name[4];
|
||||
char n_desc[0];
|
||||
};
|
||||
|
||||
struct Elf64_Prop {
|
||||
Elf64_Word pr_type;
|
||||
Elf64_Word pr_datasz;
|
||||
char pr_data[0];
|
||||
};
|
||||
|
||||
// On 64-bit machines this should be 8-byte aligned.
|
||||
struct Elf64_NhdrGNUProperty {
|
||||
Elf64_Nhdr nhdr;
|
||||
char n_name[4];
|
||||
char n_desc[0];
|
||||
};
|
||||
|
||||
struct ElfProgramProperty {
|
||||
#if defined(__aarch64__)
|
||||
bool bti_compatible = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Representation of the .note.gnu.property section found in the segment
|
||||
// with p_type = PT_GNU_PROPERTY.
|
||||
class GnuPropertySection {
|
||||
public:
|
||||
GnuPropertySection(){};
|
||||
explicit GnuPropertySection(const soinfo* si);
|
||||
GnuPropertySection(const ElfW(Phdr)* phdr, size_t phdr_count, const ElfW(Addr) load_bias,
|
||||
const char* name);
|
||||
|
||||
#if defined(__aarch64__)
|
||||
bool IsBTICompatible() const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
const ElfW(NhdrGNUProperty)* FindSegment(const ElfW(Phdr)* phdr, size_t phdr_count,
|
||||
const ElfW(Addr) load_bias, const char* name) const;
|
||||
bool SanityCheck(const ElfW(NhdrGNUProperty)* note_nhdr, const char* name) const;
|
||||
bool Parse(const ElfW(NhdrGNUProperty)* note_nhdr, const char* name);
|
||||
|
||||
ElfProgramProperty properties_ __unused;
|
||||
};
|
435
linker/linker_note_gnu_property_test.cpp
Normal file
435
linker/linker_note_gnu_property_test.cpp
Normal file
|
@ -0,0 +1,435 @@
|
|||
/*
|
||||
* 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 <stdlib.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "linker.h"
|
||||
#include "linker_globals.h"
|
||||
#include "linker_note_gnu_property.h"
|
||||
#include "platform/bionic/macros.h"
|
||||
|
||||
#define SONAME "test_so"
|
||||
|
||||
static char error_buffer[1024];
|
||||
|
||||
char* linker_get_error_buffer() {
|
||||
return error_buffer;
|
||||
}
|
||||
|
||||
size_t linker_get_error_buffer_size() {
|
||||
return std::size(error_buffer);
|
||||
}
|
||||
|
||||
static void reset_error_buffer() {
|
||||
error_buffer[0] = '\0';
|
||||
}
|
||||
|
||||
platform_properties g_platform_properties {
|
||||
#if defined(__aarch64__)
|
||||
// Assume "hardware" supports Armv8.5-A BTI.
|
||||
.bti_supported = true
|
||||
#endif
|
||||
};
|
||||
|
||||
// Helper macro to make the test cleaner.
|
||||
#define PHDR_WITH_NOTE_GNU_PROPERTY(__prop) \
|
||||
reset_error_buffer(); \
|
||||
ElfW(Phdr) phdrs[] = { \
|
||||
{.p_type = PT_LOAD}, \
|
||||
{ \
|
||||
.p_type = PT_GNU_PROPERTY, \
|
||||
.p_vaddr = reinterpret_cast<ElfW(Addr)>(__prop), \
|
||||
.p_memsz = sizeof(ElfW(NhdrGNUProperty)) + (__prop)->nhdr.n_descsz, \
|
||||
}, \
|
||||
{.p_type = PT_NULL}, \
|
||||
}; \
|
||||
auto note = GnuPropertySection(&phdrs[0], std::size(phdrs), 0, SONAME)
|
||||
|
||||
// Helper to check for no error message.
|
||||
#define ASSERT_NO_ERROR_MSG() ASSERT_STREQ(error_buffer, "")
|
||||
|
||||
// Helper to check expected error message.
|
||||
#define ASSERT_ERROR_MSG_EQ(__expected) ASSERT_STREQ(error_buffer, "\"" SONAME "\" " __expected)
|
||||
|
||||
static void test_bti_not_supported(GnuPropertySection& note __unused) {
|
||||
#if defined(__aarch64__)
|
||||
ASSERT_FALSE(note.IsBTICompatible());
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__aarch64__)
|
||||
static void test_bti_supported(GnuPropertySection& note __unused) {
|
||||
ASSERT_TRUE(note.IsBTICompatible());
|
||||
}
|
||||
#endif
|
||||
|
||||
// Helper class to build a well-formed .note.gnu.property section.
|
||||
class GnuPropertySectionBuilder {
|
||||
public:
|
||||
GnuPropertySectionBuilder() {
|
||||
note = reinterpret_cast<ElfW(NhdrGNUProperty)*>(§ion[0]);
|
||||
note->nhdr.n_namesz = 4;
|
||||
note->nhdr.n_descsz = 0;
|
||||
note->nhdr.n_type = NT_GNU_PROPERTY_TYPE_0;
|
||||
memcpy(note->n_name, "GNU", 4);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool push(ElfW(Word) pr_type, ElfW(Word) pr_datasz, const T* pr_data) {
|
||||
// Must be aligned.
|
||||
const uintptr_t addition = align_up(pr_datasz, sizeof(ElfW(Addr)));
|
||||
if ((offset() + addition) > kMaxSectionSize) {
|
||||
return false;
|
||||
}
|
||||
++entries;
|
||||
ElfW(Prop)* prop = reinterpret_cast<ElfW(Prop)*>(§ion[offset()]);
|
||||
// Header
|
||||
prop->pr_type = pr_type;
|
||||
prop->pr_datasz = pr_datasz;
|
||||
step(2 * sizeof(ElfW(Word)));
|
||||
// Data
|
||||
memcpy(§ion[offset()], reinterpret_cast<const void*>(pr_data), pr_datasz);
|
||||
step(pr_datasz);
|
||||
// Padding
|
||||
memset(§ion[offset()], 0xAA, addition - pr_datasz);
|
||||
step(addition - pr_datasz);
|
||||
return true;
|
||||
}
|
||||
|
||||
ElfW(NhdrGNUProperty)* data() const { return note; }
|
||||
|
||||
void dump() const {
|
||||
std::cout << ".note.gnu.property\n";
|
||||
dump_member("n_namesz", note->nhdr.n_namesz);
|
||||
dump_member("n_descsz", note->nhdr.n_descsz);
|
||||
dump_member("n_type ", note->nhdr.n_type);
|
||||
dump_member("n_name ", note->n_name);
|
||||
dump_member("entries ", entries);
|
||||
if (entries > 0) {
|
||||
std::cout << " raw data:";
|
||||
const uintptr_t offset = note->nhdr.n_descsz + 16;
|
||||
for (uintptr_t offs = 16; offs < offset; ++offs) {
|
||||
std::cout << std::hex;
|
||||
if ((offs % 8) == 0) {
|
||||
std::cout << "\n ";
|
||||
}
|
||||
auto value = static_cast<unsigned>(section[offs]);
|
||||
std::cout << " ";
|
||||
if (value < 0x10) {
|
||||
std::cout << "0";
|
||||
}
|
||||
std::cout << static_cast<unsigned>(section[offs]);
|
||||
}
|
||||
std::cout << std::dec << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void corrupt_n_descsz(ElfW(Word) n_descsz) { note->nhdr.n_descsz = n_descsz; }
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void dump_member(const char* name, T value) const {
|
||||
std::cout << " " << name << " " << value << "\n";
|
||||
}
|
||||
|
||||
ElfW(Word) offset() const { return note->nhdr.n_descsz + 16; }
|
||||
|
||||
template <typename T>
|
||||
void step(T value) {
|
||||
note->nhdr.n_descsz += static_cast<ElfW(Word)>(value);
|
||||
}
|
||||
|
||||
static const size_t kMaxSectionSize = 1024;
|
||||
|
||||
alignas(8) uint8_t section[kMaxSectionSize];
|
||||
ElfW(NhdrGNUProperty)* note;
|
||||
size_t entries = 0;
|
||||
};
|
||||
|
||||
// Tests that the default constructed instance does not report support
|
||||
// for Armv8.5-A BTI.
|
||||
TEST(note_gnu_property, default) {
|
||||
GnuPropertySection note;
|
||||
test_bti_not_supported(note);
|
||||
ASSERT_NO_ERROR_MSG();
|
||||
}
|
||||
|
||||
// Tests that an instance without valid phdr pointer does not report
|
||||
// support for Armv8.5-A BTI.
|
||||
TEST(note_gnu_property, phdr_null) {
|
||||
auto note = GnuPropertySection(nullptr, 0, 0, SONAME);
|
||||
test_bti_not_supported(note);
|
||||
ASSERT_NO_ERROR_MSG();
|
||||
}
|
||||
|
||||
// Tests that an instance without finding PT_GNU_PROPERTY does not
|
||||
// report support for Armv8.5-A BTI.
|
||||
TEST(note_gnu_property, no_pt_gnu_property) {
|
||||
ElfW(Phdr) phdrs[] = {
|
||||
{.p_type = PT_LOAD},
|
||||
{.p_type = PT_NULL},
|
||||
};
|
||||
|
||||
reset_error_buffer();
|
||||
auto note = GnuPropertySection(&phdrs[0], std::size(phdrs), 0, SONAME);
|
||||
test_bti_not_supported(note);
|
||||
ASSERT_NO_ERROR_MSG();
|
||||
}
|
||||
|
||||
// Tests the validity check for invalid PT_GNU_PROPERTY size.
|
||||
TEST(note_gnu_property, pt_gnu_property_bad_size) {
|
||||
ElfW(Phdr) phdrs[] = {
|
||||
{.p_type = PT_LOAD},
|
||||
{
|
||||
.p_type = PT_GNU_PROPERTY,
|
||||
.p_vaddr = 0,
|
||||
.p_memsz = sizeof(ElfW(NhdrGNUProperty)) - 1, // Invalid
|
||||
},
|
||||
{.p_type = PT_NULL},
|
||||
};
|
||||
|
||||
reset_error_buffer();
|
||||
auto note = GnuPropertySection(&phdrs[0], std::size(phdrs), 0, SONAME);
|
||||
test_bti_not_supported(note);
|
||||
ASSERT_ERROR_MSG_EQ("PT_GNU_PROPERTY segment is too small. Segment size is 15, minimum is 16.");
|
||||
}
|
||||
|
||||
// Tests that advertised n_descsz should still fit into p_memsz.
|
||||
TEST(note_gnu_property, pt_gnu_property_too_small) {
|
||||
ElfW(NhdrGNUProperty) prop = {
|
||||
.nhdr = {.n_namesz = PT_GNU_PROPERTY, .n_descsz = 1, .n_type = NT_GNU_PROPERTY_TYPE_0},
|
||||
.n_name = "GNU",
|
||||
};
|
||||
ElfW(Phdr) phdrs[] = {
|
||||
{
|
||||
.p_type = PT_GNU_PROPERTY,
|
||||
.p_vaddr = reinterpret_cast<ElfW(Addr)>(&prop),
|
||||
.p_memsz = sizeof(ElfW(NhdrGNUProperty)), // Off by one
|
||||
},
|
||||
};
|
||||
|
||||
reset_error_buffer();
|
||||
auto note = GnuPropertySection(&phdrs[0], std::size(phdrs), 0, SONAME);
|
||||
test_bti_not_supported(note);
|
||||
ASSERT_ERROR_MSG_EQ("PT_GNU_PROPERTY segment p_memsz (16) is too small for note n_descsz (1).");
|
||||
}
|
||||
|
||||
// Tests the validity check for invalid .note.gnu.property type.
|
||||
TEST(note_gnu_property, pt_gnu_property_bad_type) {
|
||||
ElfW(NhdrGNUProperty) prop = {
|
||||
.nhdr =
|
||||
{
|
||||
.n_namesz = 4,
|
||||
.n_descsz = 0,
|
||||
.n_type = NT_GNU_PROPERTY_TYPE_0 - 1 // Invalid
|
||||
},
|
||||
.n_name = "GNU",
|
||||
};
|
||||
PHDR_WITH_NOTE_GNU_PROPERTY(&prop);
|
||||
test_bti_not_supported(note);
|
||||
ASSERT_ERROR_MSG_EQ(".note.gnu.property: unexpected note type. Expected 5, got 4.");
|
||||
}
|
||||
|
||||
// Tests the validity check for invalid .note.gnu.property name size.
|
||||
TEST(note_gnu_property, pt_gnu_property_bad_namesz) {
|
||||
ElfW(NhdrGNUProperty) prop = {
|
||||
.nhdr = {.n_namesz = 3, // Invalid
|
||||
.n_descsz = 0,
|
||||
.n_type = NT_GNU_PROPERTY_TYPE_0},
|
||||
.n_name = "GNU",
|
||||
};
|
||||
PHDR_WITH_NOTE_GNU_PROPERTY(&prop);
|
||||
test_bti_not_supported(note);
|
||||
ASSERT_ERROR_MSG_EQ(".note.gnu.property: unexpected name size. Expected 4, got 3.");
|
||||
}
|
||||
|
||||
// Tests the validity check for invalid .note.gnu.property name.
|
||||
TEST(note_gnu_property, pt_gnu_property_bad_name) {
|
||||
ElfW(NhdrGNUProperty) prop = {
|
||||
.nhdr = {.n_namesz = 4, .n_descsz = 0, .n_type = NT_GNU_PROPERTY_TYPE_0},
|
||||
.n_name = "ABC", // Invalid
|
||||
};
|
||||
PHDR_WITH_NOTE_GNU_PROPERTY(&prop);
|
||||
test_bti_not_supported(note);
|
||||
ASSERT_ERROR_MSG_EQ(".note.gnu.property: unexpected name. Expected 'GNU', got 'ABC'.");
|
||||
}
|
||||
|
||||
// Tests the validity check for not enough space for a Program Property header.
|
||||
TEST(note_gnu_property, pt_gnu_property_pphdr_no_space) {
|
||||
ElfW(NhdrGNUProperty) prop = {
|
||||
.nhdr = {.n_namesz = 4,
|
||||
.n_descsz = 7, // Invalid
|
||||
.n_type = NT_GNU_PROPERTY_TYPE_0},
|
||||
.n_name = "GNU",
|
||||
};
|
||||
PHDR_WITH_NOTE_GNU_PROPERTY(&prop);
|
||||
test_bti_not_supported(note);
|
||||
ASSERT_ERROR_MSG_EQ(".note.gnu.property: no more space left for a Program Property Note header.");
|
||||
}
|
||||
|
||||
// Tests an empty .note.gnu.property.
|
||||
TEST(note_gnu_property, pt_gnu_property_no_data) {
|
||||
GnuPropertySectionBuilder prop;
|
||||
PHDR_WITH_NOTE_GNU_PROPERTY(prop.data());
|
||||
test_bti_not_supported(note);
|
||||
ASSERT_NO_ERROR_MSG();
|
||||
}
|
||||
|
||||
// Tests a .note.gnu.property section with elements with pr_datasz = 0.
|
||||
TEST(note_gnu_property, pt_gnu_property_no_prop) {
|
||||
GnuPropertySectionBuilder prop;
|
||||
ASSERT_TRUE(prop.push(1, 0, (void*)nullptr));
|
||||
ASSERT_TRUE(prop.push(2, 0, (void*)nullptr));
|
||||
ASSERT_TRUE(prop.push(3, 0, (void*)nullptr));
|
||||
PHDR_WITH_NOTE_GNU_PROPERTY(prop.data());
|
||||
test_bti_not_supported(note);
|
||||
ASSERT_NO_ERROR_MSG();
|
||||
}
|
||||
|
||||
// Tests that GNU_PROPERTY_AARCH64_FEATURE_1_AND must have pr_datasz = 4.
|
||||
TEST(note_gnu_property, pt_gnu_property_bad_pr_datasz) {
|
||||
#if defined(__aarch64__)
|
||||
GnuPropertySectionBuilder prop;
|
||||
ElfW(Word) pr_data[] = {GNU_PROPERTY_AARCH64_FEATURE_1_BTI, 0, 0};
|
||||
ASSERT_TRUE(prop.push(GNU_PROPERTY_AARCH64_FEATURE_1_AND, 12, &pr_data));
|
||||
PHDR_WITH_NOTE_GNU_PROPERTY(prop.data());
|
||||
test_bti_not_supported(note);
|
||||
ASSERT_ERROR_MSG_EQ(
|
||||
".note.gnu.property: property descriptor size is invalid. Expected 4 bytes for "
|
||||
"GNU_PROPERTY_AARCH64_FEATURE_1_AND, got 12.");
|
||||
#else
|
||||
GTEST_SKIP() << "BTI is not supported on this architecture.";
|
||||
#endif
|
||||
}
|
||||
|
||||
// Tests a .note.gnu.property section with only GNU_PROPERTY_AARCH64_FEATURE_1_BTI property array.
|
||||
TEST(note_gnu_property, pt_gnu_property_ok_1) {
|
||||
#if defined(__aarch64__)
|
||||
GnuPropertySectionBuilder prop;
|
||||
ElfW(Word) pr_data[] = {GNU_PROPERTY_AARCH64_FEATURE_1_BTI};
|
||||
ASSERT_TRUE(prop.push(GNU_PROPERTY_AARCH64_FEATURE_1_AND, sizeof(pr_data), &pr_data));
|
||||
PHDR_WITH_NOTE_GNU_PROPERTY(prop.data());
|
||||
ASSERT_NO_ERROR_MSG();
|
||||
test_bti_supported(note);
|
||||
#else
|
||||
GTEST_SKIP() << "BTI is not supported on this architecture.";
|
||||
#endif
|
||||
}
|
||||
|
||||
// Tests a .note.gnu.property section with only GNU_PROPERTY_AARCH64_FEATURE_1_BTI property array.
|
||||
TEST(note_gnu_property, pt_gnu_property_ok_2) {
|
||||
#if defined(__aarch64__)
|
||||
GnuPropertySectionBuilder prop;
|
||||
ElfW(Word) pr_data[] = {static_cast<ElfW(Word)>(~GNU_PROPERTY_AARCH64_FEATURE_1_BTI)};
|
||||
ASSERT_TRUE(prop.push(GNU_PROPERTY_AARCH64_FEATURE_1_AND, sizeof(pr_data), &pr_data));
|
||||
PHDR_WITH_NOTE_GNU_PROPERTY(prop.data());
|
||||
ASSERT_NO_ERROR_MSG();
|
||||
test_bti_not_supported(note);
|
||||
#else
|
||||
GTEST_SKIP() << "BTI is not supported on this architecture.";
|
||||
#endif
|
||||
}
|
||||
|
||||
// Tests a .note.gnu.property section with more property arrays.
|
||||
TEST(note_gnu_property, pt_gnu_property_ok_3) {
|
||||
#if defined(__aarch64__)
|
||||
GnuPropertySectionBuilder prop;
|
||||
|
||||
ElfW(Word) pr_data_0[8] = {0xCD};
|
||||
ASSERT_TRUE(prop.push(1, 4, &pr_data_0));
|
||||
ASSERT_TRUE(prop.push(2, 3, &pr_data_0));
|
||||
ASSERT_TRUE(prop.push(3, 8, &pr_data_0));
|
||||
|
||||
ElfW(Word) pr_data[] = {GNU_PROPERTY_AARCH64_FEATURE_1_BTI};
|
||||
ASSERT_TRUE(prop.push(GNU_PROPERTY_AARCH64_FEATURE_1_AND, sizeof(pr_data), &pr_data));
|
||||
|
||||
ASSERT_TRUE(prop.push(4, 1, &pr_data_0));
|
||||
|
||||
PHDR_WITH_NOTE_GNU_PROPERTY(prop.data());
|
||||
ASSERT_NO_ERROR_MSG();
|
||||
test_bti_supported(note);
|
||||
#else
|
||||
GTEST_SKIP() << "BTI is not supported on this architecture.";
|
||||
#endif
|
||||
}
|
||||
|
||||
// Tests a .note.gnu.property but with bad property descriptor size.
|
||||
TEST(note_gnu_property, pt_gnu_property_bad_n_descsz) {
|
||||
#if defined(__aarch64__)
|
||||
GnuPropertySectionBuilder prop;
|
||||
ElfW(Word) pr_data[] = {GNU_PROPERTY_AARCH64_FEATURE_1_BTI};
|
||||
ASSERT_TRUE(prop.push(GNU_PROPERTY_AARCH64_FEATURE_1_AND, sizeof(pr_data), &pr_data));
|
||||
|
||||
ElfW(Word) n_descsz;
|
||||
if (sizeof(ElfW(Addr)) == 4) {
|
||||
n_descsz = 11;
|
||||
} else {
|
||||
n_descsz = 15;
|
||||
}
|
||||
|
||||
prop.corrupt_n_descsz(n_descsz);
|
||||
|
||||
PHDR_WITH_NOTE_GNU_PROPERTY(prop.data());
|
||||
if (sizeof(ElfW(Addr)) == 4) {
|
||||
ASSERT_ERROR_MSG_EQ(
|
||||
".note.gnu.property: property descriptor size is invalid. Expected at least 12 bytes, got "
|
||||
"11.");
|
||||
} else {
|
||||
ASSERT_ERROR_MSG_EQ(
|
||||
".note.gnu.property: property descriptor size is invalid. Expected at least 16 bytes, got "
|
||||
"15.");
|
||||
}
|
||||
test_bti_not_supported(note);
|
||||
#else
|
||||
GTEST_SKIP() << "BTI is not supported on this architecture.";
|
||||
#endif
|
||||
}
|
||||
|
||||
// Tests if platform support is missing.
|
||||
TEST(note_gnu_property, no_platform_support) {
|
||||
#if defined(__aarch64__)
|
||||
auto bti_supported_orig = g_platform_properties.bti_supported;
|
||||
g_platform_properties.bti_supported = false;
|
||||
|
||||
GnuPropertySectionBuilder prop;
|
||||
ElfW(Word) pr_data[] = {GNU_PROPERTY_AARCH64_FEATURE_1_BTI};
|
||||
ASSERT_TRUE(prop.push(GNU_PROPERTY_AARCH64_FEATURE_1_AND, sizeof(pr_data), &pr_data));
|
||||
PHDR_WITH_NOTE_GNU_PROPERTY(prop.data());
|
||||
ASSERT_NO_ERROR_MSG();
|
||||
test_bti_not_supported(note);
|
||||
|
||||
g_platform_properties.bti_supported = bti_supported_orig;
|
||||
#else
|
||||
GTEST_SKIP() << "BTI is not supported on this architecture.";
|
||||
#endif
|
||||
}
|
|
@ -169,8 +169,16 @@ bool ElfReader::Load(address_space_params* address_space) {
|
|||
if (did_load_) {
|
||||
return true;
|
||||
}
|
||||
if (ReserveAddressSpace(address_space) && LoadSegments() && FindPhdr()) {
|
||||
if (ReserveAddressSpace(address_space) && LoadSegments() && FindPhdr() &&
|
||||
FindGnuPropertySection()) {
|
||||
did_load_ = true;
|
||||
#if defined(__aarch64__)
|
||||
// For Armv8.5-A loaded executable segments may require PROT_BTI.
|
||||
if (note_gnu_property_.IsBTICompatible()) {
|
||||
did_load_ = (phdr_table_protect_segments(phdr_table_, phdr_num_, load_bias_,
|
||||
¬e_gnu_property_) == 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return did_load_;
|
||||
|
@ -748,15 +756,21 @@ static int _phdr_table_set_load_prot(const ElfW(Phdr)* phdr_table, size_t phdr_c
|
|||
ElfW(Addr) seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias;
|
||||
ElfW(Addr) seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias;
|
||||
|
||||
int prot = PFLAGS_TO_PROT(phdr->p_flags);
|
||||
if ((extra_prot_flags & PROT_WRITE) != 0) {
|
||||
int prot = PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags;
|
||||
if ((prot & PROT_WRITE) != 0) {
|
||||
// make sure we're never simultaneously writable / executable
|
||||
prot &= ~PROT_EXEC;
|
||||
}
|
||||
#if defined(__aarch64__)
|
||||
if ((prot & PROT_EXEC) == 0) {
|
||||
// Though it is not specified don't add PROT_BTI if segment is not
|
||||
// executable.
|
||||
prot &= ~PROT_BTI;
|
||||
}
|
||||
#endif
|
||||
|
||||
int ret = mprotect(reinterpret_cast<void*>(seg_page_start),
|
||||
seg_page_end - seg_page_start,
|
||||
prot | extra_prot_flags);
|
||||
int ret =
|
||||
mprotect(reinterpret_cast<void*>(seg_page_start), seg_page_end - seg_page_start, prot);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -768,16 +782,26 @@ static int _phdr_table_set_load_prot(const ElfW(Phdr)* phdr_table, size_t phdr_c
|
|||
* You should only call this after phdr_table_unprotect_segments and
|
||||
* applying all relocations.
|
||||
*
|
||||
* AArch64: also called from linker_main and ElfReader::Load to apply
|
||||
* PROT_BTI for loaded main so and other so-s.
|
||||
*
|
||||
* Input:
|
||||
* phdr_table -> program header table
|
||||
* phdr_count -> number of entries in tables
|
||||
* load_bias -> load bias
|
||||
* prop -> GnuPropertySection or nullptr
|
||||
* Return:
|
||||
* 0 on error, -1 on failure (error code in errno).
|
||||
*/
|
||||
int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table,
|
||||
size_t phdr_count, ElfW(Addr) load_bias) {
|
||||
return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, 0);
|
||||
int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count,
|
||||
ElfW(Addr) load_bias, const GnuPropertySection* prop __unused) {
|
||||
int prot = 0;
|
||||
#if defined(__aarch64__)
|
||||
if ((prop != nullptr) && prop->IsBTICompatible()) {
|
||||
prot |= PROT_BTI;
|
||||
}
|
||||
#endif
|
||||
return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, prot);
|
||||
}
|
||||
|
||||
/* Change the protection of all loaded segments in memory to writable.
|
||||
|
@ -1081,7 +1105,7 @@ void phdr_table_get_dynamic_section(const ElfW(Phdr)* phdr_table, size_t phdr_co
|
|||
* Return:
|
||||
* pointer to the program interpreter string.
|
||||
*/
|
||||
const char* phdr_table_get_interpreter_name(const ElfW(Phdr) * phdr_table, size_t phdr_count,
|
||||
const char* phdr_table_get_interpreter_name(const ElfW(Phdr)* phdr_table, size_t phdr_count,
|
||||
ElfW(Addr) load_bias) {
|
||||
for (size_t i = 0; i<phdr_count; ++i) {
|
||||
const ElfW(Phdr)& phdr = phdr_table[i];
|
||||
|
@ -1124,6 +1148,15 @@ bool ElfReader::FindPhdr() {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Tries to find .note.gnu.property section.
|
||||
// It is not considered an error if such section is missing.
|
||||
bool ElfReader::FindGnuPropertySection() {
|
||||
#if defined(__aarch64__)
|
||||
note_gnu_property_ = GnuPropertySection(phdr_table_, phdr_num_, load_start(), name_.c_str());
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ensures that our program header is actually within a loadable
|
||||
// segment. This should help catch badly-formed ELF files that
|
||||
// would cause the linker to crash later when trying to access it.
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
#include "linker.h"
|
||||
#include "linker_mapped_file_fragment.h"
|
||||
#include "linker_note_gnu_property.h"
|
||||
|
||||
class ElfReader {
|
||||
public:
|
||||
|
@ -67,6 +68,7 @@ class ElfReader {
|
|||
bool ReserveAddressSpace(address_space_params* address_space);
|
||||
bool LoadSegments();
|
||||
bool FindPhdr();
|
||||
bool FindGnuPropertySection();
|
||||
bool CheckPhdr(ElfW(Addr));
|
||||
bool CheckFileRange(ElfW(Addr) offset, size_t size, size_t alignment);
|
||||
|
||||
|
@ -110,13 +112,16 @@ class ElfReader {
|
|||
|
||||
// Is map owned by the caller
|
||||
bool mapped_by_caller_;
|
||||
|
||||
// Only used by AArch64 at the moment.
|
||||
GnuPropertySection note_gnu_property_ __unused;
|
||||
};
|
||||
|
||||
size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count,
|
||||
ElfW(Addr)* min_vaddr = nullptr, ElfW(Addr)* max_vaddr = nullptr);
|
||||
|
||||
int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table,
|
||||
size_t phdr_count, ElfW(Addr) load_bias);
|
||||
int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count,
|
||||
ElfW(Addr) load_bias, const GnuPropertySection* prop = nullptr);
|
||||
|
||||
int phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count,
|
||||
ElfW(Addr) load_bias);
|
||||
|
@ -139,5 +144,5 @@ void phdr_table_get_dynamic_section(const ElfW(Phdr)* phdr_table, size_t phdr_co
|
|||
ElfW(Addr) load_bias, ElfW(Dyn)** dynamic,
|
||||
ElfW(Word)* dynamic_flags);
|
||||
|
||||
const char* phdr_table_get_interpreter_name(const ElfW(Phdr) * phdr_table, size_t phdr_count,
|
||||
const char* phdr_table_get_interpreter_name(const ElfW(Phdr)* phdr_table, size_t phdr_count,
|
||||
ElfW(Addr) load_bias);
|
||||
|
|
Loading…
Reference in a new issue