Revert "Remove text-relocation support for lp32"
This reverts commit cb00add1b3
.
Bug: 20020312
Bug: 20013628
Change-Id: I8baa3d4b6c7fef50c9e2531257d5b96762099eb3
This commit is contained in:
parent
cb00add1b3
commit
56be6ed9e4
4 changed files with 108 additions and 4 deletions
|
@ -2297,8 +2297,13 @@ bool soinfo::prelink_image() {
|
|||
break;
|
||||
|
||||
case DT_TEXTREL:
|
||||
DL_ERR("text relocations (DT_TEXTREL) found in the ELF file \"%s\"", name);
|
||||
#if defined(__LP64__)
|
||||
DL_ERR("text relocations (DT_TEXTREL) found in 64-bit ELF file \"%s\"", name);
|
||||
return false;
|
||||
#else
|
||||
has_text_relocations = true;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case DT_SYMBOLIC:
|
||||
has_DT_SYMBOLIC = true;
|
||||
|
@ -2310,8 +2315,12 @@ bool soinfo::prelink_image() {
|
|||
|
||||
case DT_FLAGS:
|
||||
if (d->d_un.d_val & DF_TEXTREL) {
|
||||
DL_ERR("text relocations (DF_TEXTREL) found in the ELF file \"%s\"", name);
|
||||
#if defined(__LP64__)
|
||||
DL_ERR("text relocations (DF_TEXTREL) found in 64-bit ELF file \"%s\"", name);
|
||||
return false;
|
||||
#else
|
||||
has_text_relocations = true;
|
||||
#endif
|
||||
}
|
||||
if (d->d_un.d_val & DF_SYMBOLIC) {
|
||||
has_DT_SYMBOLIC = true;
|
||||
|
@ -2420,6 +2429,20 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t&
|
|||
local_group_root_ = this;
|
||||
}
|
||||
|
||||
#if !defined(__LP64__)
|
||||
if (has_text_relocations) {
|
||||
// Make segments writable to allow text relocations to work properly. We will later call
|
||||
// phdr_table_protect_segments() after all of them are applied and all constructors are run.
|
||||
DL_WARN("%s has text relocations. This is wasting memory and prevents "
|
||||
"security hardening. Please fix.", name);
|
||||
if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) {
|
||||
DL_ERR("can't unprotect loadable segments for \"%s\": %s",
|
||||
name, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (android_relocs_ != nullptr) {
|
||||
// check signature
|
||||
if (android_relocs_size_ > 3 &&
|
||||
|
@ -2490,6 +2513,17 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t&
|
|||
|
||||
DEBUG("[ finished linking %s ]", name);
|
||||
|
||||
#if !defined(__LP64__)
|
||||
if (has_text_relocations) {
|
||||
// All relocations are done, we can protect our segments back to read-only.
|
||||
if (phdr_table_protect_segments(phdr, phnum, load_bias) < 0) {
|
||||
DL_ERR("can't protect segments for \"%s\": %s",
|
||||
name, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* We can also turn on GNU RELRO protection */
|
||||
if (phdr_table_protect_gnu_relro(phdr, phnum, load_bias) < 0) {
|
||||
DL_ERR("can't enable GNU RELRO protection for \"%s\": %s",
|
||||
|
|
|
@ -226,8 +226,8 @@ struct soinfo {
|
|||
// value to get the corresponding address in the process' address space.
|
||||
ElfW(Addr) load_bias;
|
||||
|
||||
#if defined(__arm__)
|
||||
bool unused4; // DO NOT USE, maintained for compatibility
|
||||
#if !defined(__LP64__)
|
||||
bool has_text_relocations;
|
||||
#endif
|
||||
bool has_DT_SYMBOLIC;
|
||||
|
||||
|
|
|
@ -412,6 +412,70 @@ bool ElfReader::LoadSegments() {
|
|||
return true;
|
||||
}
|
||||
|
||||
/* Used internally. Used to set the protection bits of all loaded segments
|
||||
* with optional extra flags (i.e. really PROT_WRITE). Used by
|
||||
* phdr_table_protect_segments and phdr_table_unprotect_segments.
|
||||
*/
|
||||
static int _phdr_table_set_load_prot(const ElfW(Phdr)* phdr_table, size_t phdr_count,
|
||||
ElfW(Addr) load_bias, int extra_prot_flags) {
|
||||
const ElfW(Phdr)* phdr = phdr_table;
|
||||
const ElfW(Phdr)* phdr_limit = phdr + phdr_count;
|
||||
|
||||
for (; phdr < phdr_limit; phdr++) {
|
||||
if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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 ret = mprotect(reinterpret_cast<void*>(seg_page_start),
|
||||
seg_page_end - seg_page_start,
|
||||
PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Restore the original protection modes for all loadable segments.
|
||||
* You should only call this after phdr_table_unprotect_segments and
|
||||
* applying all relocations.
|
||||
*
|
||||
* Input:
|
||||
* phdr_table -> program header table
|
||||
* phdr_count -> number of entries in tables
|
||||
* load_bias -> load bias
|
||||
* 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);
|
||||
}
|
||||
|
||||
/* Change the protection of all loaded segments in memory to writable.
|
||||
* This is useful before performing relocations. Once completed, you
|
||||
* will have to call phdr_table_protect_segments to restore the original
|
||||
* protection flags on all segments.
|
||||
*
|
||||
* Note that some writable segments can also have their content turned
|
||||
* to read-only by calling phdr_table_protect_gnu_relro. This is no
|
||||
* performed here.
|
||||
*
|
||||
* Input:
|
||||
* phdr_table -> program header table
|
||||
* phdr_count -> number of entries in tables
|
||||
* load_bias -> load bias
|
||||
* Return:
|
||||
* 0 on error, -1 on failure (error code in errno).
|
||||
*/
|
||||
int phdr_table_unprotect_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, PROT_WRITE);
|
||||
}
|
||||
|
||||
/* Used internally by phdr_table_protect_gnu_relro and
|
||||
* phdr_table_unprotect_gnu_relro.
|
||||
*/
|
||||
|
|
|
@ -84,6 +84,12 @@ class ElfReader {
|
|||
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_unprotect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count,
|
||||
ElfW(Addr) load_bias);
|
||||
|
||||
int phdr_table_protect_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count,
|
||||
ElfW(Addr) load_bias);
|
||||
|
||||
|
|
Loading…
Reference in a new issue