mirror of
https://github.com/dynup/kpatch
synced 2024-12-21 12:50:01 +00:00
50476c94cc
There is no point inspecting through the symbols of the ELF files (original and patched) when the ELF headers do not meet requirements. Check ELF headers as soon as the files are mapped. Signed-off-by: Julien Thierry <jthierry@redhat.com>
3556 lines
98 KiB
C
3556 lines
98 KiB
C
/*
|
|
* create-diff-object.c
|
|
*
|
|
* Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
|
|
* Copyright (C) 2013-2014 Josh Poimboeuf <jpoimboe@redhat.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA,
|
|
* 02110-1301, USA.
|
|
*/
|
|
|
|
/*
|
|
* This file contains the heart of the ELF object differencing engine.
|
|
*
|
|
* The tool takes two ELF objects from two versions of the same source
|
|
* file; a "base" object and a "patched" object. These object need to have
|
|
* been compiled with the -ffunction-sections and -fdata-sections GCC options.
|
|
*
|
|
* The tool compares the objects at a section level to determine what
|
|
* sections have changed. Once a list of changed sections has been generated,
|
|
* various rules are applied to determine any object local sections that
|
|
* are dependencies of the changed section and also need to be included in
|
|
* the output object.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <error.h>
|
|
#include <gelf.h>
|
|
#include <argp.h>
|
|
#include <libgen.h>
|
|
#include <unistd.h>
|
|
|
|
#include "list.h"
|
|
#include "lookup.h"
|
|
#include "asm/insn.h"
|
|
#include "kpatch-patch.h"
|
|
#include "kpatch-elf.h"
|
|
#include "kpatch-intermediate.h"
|
|
#include "kpatch.h"
|
|
|
|
#define DIFF_FATAL(format, ...) \
|
|
({ \
|
|
fprintf(stderr, "ERROR: %s: " format "\n", childobj, ##__VA_ARGS__); \
|
|
error(EXIT_STATUS_DIFF_FATAL, 0, "unreconcilable difference"); \
|
|
})
|
|
|
|
#ifdef __powerpc64__
|
|
#define ABSOLUTE_RELA_TYPE R_PPC64_ADDR64
|
|
#else
|
|
#define ABSOLUTE_RELA_TYPE R_X86_64_64
|
|
#endif
|
|
|
|
char *childobj;
|
|
|
|
enum subsection {
|
|
SUBSECTION_NORMAL,
|
|
SUBSECTION_HOT,
|
|
SUBSECTION_UNLIKELY
|
|
};
|
|
|
|
enum loglevel loglevel = NORMAL;
|
|
|
|
/*******************
|
|
* Data structures
|
|
* ****************/
|
|
struct special_section {
|
|
char *name;
|
|
int (*group_size)(struct kpatch_elf *kelf, int offset);
|
|
int unsupported;
|
|
};
|
|
|
|
/*************
|
|
* Functions
|
|
* **********/
|
|
|
|
static int is_bundleable(struct symbol *sym)
|
|
{
|
|
if (sym->type == STT_FUNC &&
|
|
!strncmp(sym->sec->name, ".text.",6) &&
|
|
!strcmp(sym->sec->name + 6, sym->name))
|
|
return 1;
|
|
|
|
if (sym->type == STT_FUNC &&
|
|
!strncmp(sym->sec->name, ".text.unlikely.",15) &&
|
|
(!strcmp(sym->sec->name + 15, sym->name) ||
|
|
(strstr(sym->name, ".cold.") &&
|
|
!strncmp(sym->sec->name + 15, sym->name, strlen(sym->sec->name) - 15))))
|
|
return 1;
|
|
|
|
if (sym->type == STT_FUNC &&
|
|
!strncmp(sym->sec->name, ".text.hot.",10) &&
|
|
!strcmp(sym->sec->name + 10, sym->name))
|
|
return 1;
|
|
|
|
if (sym->type == STT_OBJECT &&
|
|
!strncmp(sym->sec->name, ".data.",6) &&
|
|
!strcmp(sym->sec->name + 6, sym->name))
|
|
return 1;
|
|
|
|
if (sym->type == STT_OBJECT &&
|
|
!strncmp(sym->sec->name, ".data.rel.", 10) &&
|
|
!strcmp(sym->sec->name + 10, sym->name))
|
|
return 1;
|
|
|
|
if (sym->type == STT_OBJECT &&
|
|
!strncmp(sym->sec->name, ".data.rel.ro.", 13) &&
|
|
!strcmp(sym->sec->name + 13, sym->name))
|
|
return 1;
|
|
|
|
if (sym->type == STT_OBJECT &&
|
|
!strncmp(sym->sec->name, ".rodata.",8) &&
|
|
!strcmp(sym->sec->name + 8, sym->name))
|
|
return 1;
|
|
|
|
if (sym->type == STT_OBJECT &&
|
|
!strncmp(sym->sec->name, ".bss.",5) &&
|
|
!strcmp(sym->sec->name + 5, sym->name))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef __powerpc64__
|
|
/* Symbol st_others value for powerpc */
|
|
#define STO_PPC64_LOCAL_BIT 5
|
|
#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT)
|
|
#define PPC64_LOCAL_ENTRY_OFFSET(other) \
|
|
(((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2)
|
|
|
|
/*
|
|
* On ppc64le, the function prologue generated by GCC 6+ has the sequence:
|
|
*
|
|
* .globl my_func
|
|
* .type my_func, @function
|
|
* .quad .TOC.-my_func
|
|
* my_func:
|
|
* .reloc ., R_PPC64_ENTRY ; optional
|
|
* ld r2,-8(r12)
|
|
* add r2,r2,r12
|
|
* .localentry my_func, .-my_func
|
|
*
|
|
* my_func is the global entry point, which, when called, sets up the TOC.
|
|
* .localentry is the local entry point, for calls to the function from within
|
|
* the object file. The local entry point is 8 bytes after the global entry
|
|
* point.
|
|
*/
|
|
static int is_gcc6_localentry_bundled_sym(struct symbol *sym)
|
|
{
|
|
return ((PPC64_LOCAL_ENTRY_OFFSET(sym->sym.st_other) != 0) &&
|
|
sym->sym.st_value == 8);
|
|
}
|
|
#else
|
|
static int is_gcc6_localentry_bundled_sym(struct symbol *sym)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static struct rela *toc_rela(const struct rela *rela)
|
|
{
|
|
if (rela->type != R_PPC64_TOC16_HA &&
|
|
rela->type != R_PPC64_TOC16_LO_DS)
|
|
return (struct rela *)rela;
|
|
|
|
/* Will return NULL for .toc constant entries */
|
|
return find_rela_by_offset(rela->sym->sec->rela, rela->addend);
|
|
}
|
|
|
|
/*
|
|
* When compiling with -ffunction-sections and -fdata-sections, almost every
|
|
* symbol gets its own dedicated section. We call such symbols "bundled"
|
|
* symbols. They're indicated by "sym->sec->sym == sym".
|
|
*/
|
|
static void kpatch_bundle_symbols(struct kpatch_elf *kelf)
|
|
{
|
|
struct symbol *sym;
|
|
|
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
if (is_bundleable(sym)) {
|
|
if (sym->sym.st_value != 0 &&
|
|
!is_gcc6_localentry_bundled_sym(sym)) {
|
|
ERROR("symbol %s at offset %lu within section %s, expected 0",
|
|
sym->name, sym->sym.st_value,
|
|
sym->sec->name);
|
|
}
|
|
|
|
sym->sec->sym = sym;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* During optimization gcc may move unlikely execution branches into *.cold
|
|
* subfunctions. kpatch_detect_child_functions detects such subfunctions and
|
|
* crossreferences them with their parent functions through parent/child
|
|
* pointers.
|
|
*/
|
|
static void kpatch_detect_child_functions(struct kpatch_elf *kelf)
|
|
{
|
|
struct symbol *sym;
|
|
|
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
char *coldstr;
|
|
|
|
coldstr = strstr(sym->name, ".cold.");
|
|
if (coldstr != NULL) {
|
|
char *pname;
|
|
|
|
pname = strndup(sym->name, coldstr - sym->name);
|
|
if (!pname)
|
|
ERROR("strndup");
|
|
|
|
sym->parent = find_symbol_by_name(&kelf->symbols, pname);
|
|
free(pname);
|
|
|
|
if (!sym->parent)
|
|
ERROR("failed to find parent function for %s", sym->name);
|
|
|
|
sym->parent->child = sym;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool is_dynamic_debug_symbol(struct symbol *sym)
|
|
{
|
|
if (sym->type == STT_OBJECT && !strcmp(sym->sec->name, "__verbose"))
|
|
return true;
|
|
if (sym->type == STT_SECTION && !strcmp(sym->name, "__verbose"))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* This function detects whether the given symbol is a "special" static local
|
|
* variable (for lack of a better term).
|
|
*
|
|
* Special static local variables should never be correlated and should always
|
|
* be included if they are referenced by an included function.
|
|
*/
|
|
static int is_special_static(struct symbol *sym)
|
|
{
|
|
static char *prefixes[] = {
|
|
"__key.",
|
|
"__warned.",
|
|
"__func__.",
|
|
"__FUNCTION__.",
|
|
"_rs.",
|
|
"CSWTCH.",
|
|
NULL,
|
|
};
|
|
char **prefix;
|
|
|
|
if (!sym)
|
|
return 0;
|
|
|
|
/* pr_debug() uses static local variables in the __verbose section */
|
|
if (is_dynamic_debug_symbol(sym))
|
|
return 1;
|
|
|
|
if (sym->type == STT_SECTION) {
|
|
/* make sure section is bundled */
|
|
if (!sym->sec->sym)
|
|
return 0;
|
|
|
|
/* use bundled object/function symbol for matching */
|
|
sym = sym->sec->sym;
|
|
}
|
|
|
|
if (sym->type != STT_OBJECT || sym->bind != STB_LOCAL)
|
|
return 0;
|
|
|
|
for (prefix = prefixes; *prefix; prefix++)
|
|
if (!strncmp(sym->name, *prefix, strlen(*prefix)))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This is like strcmp, but for gcc-mangled symbols. It skips the comparison
|
|
* of any substring which consists of '.' followed by any number of digits.
|
|
*/
|
|
static int kpatch_mangled_strcmp(char *s1, char *s2)
|
|
{
|
|
while (*s1 == *s2) {
|
|
if (!*s1)
|
|
return 0;
|
|
if (*s1 == '.' && isdigit(s1[1])) {
|
|
if (!isdigit(s2[1]))
|
|
return 1;
|
|
while (isdigit(*++s1))
|
|
;
|
|
while (isdigit(*++s2))
|
|
;
|
|
} else {
|
|
s1++;
|
|
s2++;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int rela_equal(struct rela *rela1, struct rela *rela2)
|
|
{
|
|
struct rela *rela_toc1, *rela_toc2;
|
|
unsigned long toc_data1 = 0, toc_data2 = 0; /* = 0 to prevent gcc warning */
|
|
|
|
if (rela1->type != rela2->type ||
|
|
rela1->offset != rela2->offset)
|
|
return 0;
|
|
|
|
/*
|
|
* With -mcmodel=large on ppc64le, GCC might generate entries in the .toc
|
|
* section for relocation symbol references. The .toc offsets may change
|
|
* between the original and patched .o, so comparing ".toc + offset" isn't
|
|
* right. Compare the .toc-based symbols by reading the corresponding relas
|
|
* from the .toc section.
|
|
*/
|
|
rela_toc1 = toc_rela(rela1);
|
|
if (!rela_toc1) {
|
|
/*
|
|
* .toc section entries are mostly place holder for relocation entries, specified
|
|
* in .rela.toc section. Sometimes, .toc section may have constants as entries.
|
|
* These constants are not reference to any symbols, but plain instructions mostly
|
|
* due to some arithmetics in the functions referring them.
|
|
*
|
|
* They are referred by the functions like normal .toc entries, these entries can
|
|
* not be resolved to any symbols.
|
|
*
|
|
* Disassembly of section .toc:
|
|
*
|
|
* 0000000000000000 <.toc>:
|
|
* ...
|
|
* 148: R_PPC64_ADDR64 .data.capacity_margin
|
|
* 150: 0b d7 a3 70 andi. r3,r5,55051
|
|
* 154: 3d 0a d7 a3 lhz r30,2621(r23)
|
|
* 158: R_PPC64_ADDR64 sched_max_numa_distance
|
|
*
|
|
* Relocation section '.rela.toc' at offset 0xadac0 contains 160 entries:
|
|
* Offset Info Type Symbol's Value Symbol's Name + Addend
|
|
* ...
|
|
* 0000000000000148 0000009100000026 R_PPC64_ADDR64 0000000000000000 .data.capacity_margin + 0
|
|
* 0000000000000158 000001a500000026 R_PPC64_ADDR64 0000000000000000 sched_max_numa_distance + 0
|
|
*
|
|
* Relocation section '.rela.text.select_task_rq_fair' at offset 0x90e98 contains 37 entries:
|
|
* Offset Info Type Symbol's Value Symbol's Name + Addend
|
|
* ...
|
|
* 00000000000004a0 0000008800000032 R_PPC64_TOC16_HA 0000000000000000 .toc + 148
|
|
* 00000000000004ac 0000008800000040 R_PPC64_TOC16_LO_DS 0000000000000000 .toc + 148
|
|
* 0000000000000514 0000008800000032 R_PPC64_TOC16_HA 0000000000000000 .toc + 150
|
|
* 000000000000051c 0000008800000040 R_PPC64_TOC16_LO_DS 0000000000000000 .toc + 150
|
|
*/
|
|
memcpy(&toc_data1, rela1->sym->sec->data->d_buf + rela1->addend, sizeof(toc_data1));
|
|
if (!toc_data1)
|
|
ERROR(".toc entry not found %s + %x", rela1->sym->name, rela1->addend);
|
|
}
|
|
|
|
rela_toc2 = toc_rela(rela2);
|
|
if (!rela_toc2) {
|
|
memcpy(&toc_data2, rela2->sym->sec->data->d_buf + rela2->addend, sizeof(toc_data2));
|
|
if (!toc_data2)
|
|
ERROR(".toc entry not found %s + %x", rela2->sym->name, rela2->addend);
|
|
}
|
|
|
|
if (!rela_toc1 && !rela_toc2)
|
|
return toc_data1 == toc_data2;
|
|
|
|
if (!rela_toc1 || !rela_toc2)
|
|
return 0;
|
|
|
|
if (rela_toc1->string)
|
|
return rela_toc2->string && !strcmp(rela_toc1->string, rela_toc2->string);
|
|
|
|
if (rela_toc1->addend != rela_toc2->addend)
|
|
return 0;
|
|
|
|
if (is_special_static(rela_toc1->sym))
|
|
return !kpatch_mangled_strcmp(rela_toc1->sym->name,
|
|
rela_toc2->sym->name);
|
|
|
|
return !strcmp(rela_toc1->sym->name, rela_toc2->sym->name);
|
|
}
|
|
|
|
static void kpatch_compare_correlated_rela_section(struct section *sec)
|
|
{
|
|
struct rela *rela1, *rela2 = NULL;
|
|
|
|
/*
|
|
* On ppc64le, don't compare the .rela.toc section. The .toc and
|
|
* .rela.toc sections are included as standard elements.
|
|
*/
|
|
if (!strcmp(sec->name, ".rela.toc")) {
|
|
sec->status = SAME;
|
|
return;
|
|
}
|
|
|
|
rela2 = list_entry(sec->twin->relas.next, struct rela, list);
|
|
list_for_each_entry(rela1, &sec->relas, list) {
|
|
if (rela_equal(rela1, rela2)) {
|
|
rela2 = list_entry(rela2->list.next, struct rela, list);
|
|
continue;
|
|
}
|
|
sec->status = CHANGED;
|
|
return;
|
|
}
|
|
|
|
sec->status = SAME;
|
|
}
|
|
|
|
static void kpatch_compare_correlated_nonrela_section(struct section *sec)
|
|
{
|
|
struct section *sec1 = sec, *sec2 = sec->twin;
|
|
|
|
if (sec1->sh.sh_type != SHT_NOBITS &&
|
|
memcmp(sec1->data->d_buf, sec2->data->d_buf, sec1->data->d_size))
|
|
sec->status = CHANGED;
|
|
else
|
|
sec->status = SAME;
|
|
}
|
|
|
|
static void kpatch_compare_correlated_section(struct section *sec)
|
|
{
|
|
struct section *sec1 = sec, *sec2 = sec->twin;
|
|
|
|
/* Compare section headers (must match or fatal) */
|
|
if (sec1->sh.sh_type != sec2->sh.sh_type ||
|
|
sec1->sh.sh_flags != sec2->sh.sh_flags ||
|
|
sec1->sh.sh_entsize != sec2->sh.sh_entsize ||
|
|
(sec1->sh.sh_addralign != sec2->sh.sh_addralign &&
|
|
!is_text_section(sec1)))
|
|
DIFF_FATAL("%s section header details differ", sec1->name);
|
|
|
|
/* Short circuit for mcount sections, we rebuild regardless */
|
|
if (!strcmp(sec->name, ".rela__mcount_loc") ||
|
|
!strcmp(sec->name, "__mcount_loc")) {
|
|
sec->status = SAME;
|
|
goto out;
|
|
}
|
|
|
|
if (sec1->sh.sh_size != sec2->sh.sh_size ||
|
|
sec1->data->d_size != sec2->data->d_size) {
|
|
sec->status = CHANGED;
|
|
goto out;
|
|
}
|
|
|
|
if (is_rela_section(sec))
|
|
kpatch_compare_correlated_rela_section(sec);
|
|
else
|
|
kpatch_compare_correlated_nonrela_section(sec);
|
|
out:
|
|
if (sec->status == CHANGED)
|
|
log_debug("section %s has changed\n", sec->name);
|
|
}
|
|
|
|
#ifdef __x86_64__
|
|
/*
|
|
* Determine if a section has changed only due to a WARN* or might_sleep
|
|
* macro call's embedding of the line number into an instruction operand.
|
|
*
|
|
* Warning: Hackery lies herein. It's hopefully justified by the fact that
|
|
* this issue is very common.
|
|
*
|
|
* Example WARN*:
|
|
*
|
|
* 938: be 70 00 00 00 mov $0x70,%esi
|
|
* 93d: 48 c7 c7 00 00 00 00 mov $0x0,%rdi
|
|
* 940: R_X86_64_32S .rodata.tcp_conn_request.str1.8+0x88
|
|
* 944: c6 05 00 00 00 00 01 movb $0x1,0x0(%rip) # 94b <tcp_conn_request+0x94b>
|
|
* 946: R_X86_64_PC32 .data.unlikely-0x1
|
|
* 94b: e8 00 00 00 00 callq 950 <tcp_conn_request+0x950>
|
|
* 94c: R_X86_64_PC32 warn_slowpath_null-0x4
|
|
*
|
|
* Example might_sleep:
|
|
*
|
|
* 50f: be f7 01 00 00 mov $0x1f7,%esi
|
|
* 514: 48 c7 c7 00 00 00 00 mov $0x0,%rdi
|
|
* 517: R_X86_64_32S .rodata.do_select.str1.8+0x98
|
|
* 51b: e8 00 00 00 00 callq 520 <do_select+0x520>
|
|
* 51c: R_X86_64_PC32 ___might_sleep-0x4
|
|
*
|
|
* The pattern which applies to all cases:
|
|
* 1) immediate move of the line number to %esi
|
|
* 2) (optional) string section rela
|
|
* 3) (optional) __warned.xxxxx static local rela
|
|
* 4) warn_slowpath_* or __might_sleep or some other similar rela
|
|
*/
|
|
static int kpatch_line_macro_change_only(struct section *sec)
|
|
{
|
|
struct insn insn1, insn2;
|
|
unsigned long start1, start2, size, offset, length;
|
|
struct rela *rela;
|
|
int lineonly = 0, found;
|
|
|
|
if (sec->status != CHANGED ||
|
|
is_rela_section(sec) ||
|
|
!is_text_section(sec) ||
|
|
sec->sh.sh_size != sec->twin->sh.sh_size ||
|
|
!sec->rela ||
|
|
sec->rela->status != SAME)
|
|
return 0;
|
|
|
|
start1 = (unsigned long)sec->twin->data->d_buf;
|
|
start2 = (unsigned long)sec->data->d_buf;
|
|
size = sec->sh.sh_size;
|
|
for (offset = 0; offset < size; offset += length) {
|
|
insn_init(&insn1, (void *)(start1 + offset), 1);
|
|
insn_init(&insn2, (void *)(start2 + offset), 1);
|
|
insn_get_length(&insn1);
|
|
insn_get_length(&insn2);
|
|
length = insn1.length;
|
|
|
|
if (!insn1.length || !insn2.length)
|
|
ERROR("can't decode instruction in section %s at offset 0x%lx",
|
|
sec->name, offset);
|
|
|
|
if (insn1.length != insn2.length)
|
|
return 0;
|
|
|
|
if (!memcmp((void *)start1 + offset, (void *)start2 + offset,
|
|
length))
|
|
continue;
|
|
|
|
/* verify it's a mov immediate to %edx or %esi */
|
|
insn_get_opcode(&insn1);
|
|
insn_get_opcode(&insn2);
|
|
if (!(insn1.opcode.value == 0xba && insn2.opcode.value == 0xba) &&
|
|
!(insn1.opcode.value == 0xbe && insn2.opcode.value == 0xbe))
|
|
return 0;
|
|
|
|
/*
|
|
* Verify zero or more string relas followed by a
|
|
* warn_slowpath_* or another similar rela.
|
|
*/
|
|
found = 0;
|
|
list_for_each_entry(rela, &sec->rela->relas, list) {
|
|
if (rela->offset < offset + length)
|
|
continue;
|
|
if (rela->string)
|
|
continue;
|
|
if (!strncmp(rela->sym->name, "__warned.", 9))
|
|
continue;
|
|
if (!strncmp(rela->sym->name, "warn_slowpath_", 14) ||
|
|
(!strcmp(rela->sym->name, "__warn_printk")) ||
|
|
(!strcmp(rela->sym->name, "__might_sleep")) ||
|
|
(!strcmp(rela->sym->name, "___might_sleep")) ||
|
|
(!strcmp(rela->sym->name, "__might_fault")) ||
|
|
(!strcmp(rela->sym->name, "printk")) ||
|
|
(!strcmp(rela->sym->name, "lockdep_rcu_suspicious"))) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
if (!found)
|
|
return 0;
|
|
|
|
lineonly = 1;
|
|
}
|
|
|
|
if (!lineonly)
|
|
ERROR("no instruction changes detected for changed section %s",
|
|
sec->name);
|
|
|
|
return 1;
|
|
}
|
|
#elif __powerpc64__
|
|
#define PPC_INSTR_LEN 4
|
|
#define PPC_RA_OFFSET 16
|
|
|
|
static int kpatch_line_macro_change_only(struct section *sec)
|
|
{
|
|
unsigned long start1, start2, size, offset;
|
|
unsigned int instr1, instr2;
|
|
struct rela *rela;
|
|
int lineonly = 0, found;
|
|
|
|
if (sec->status != CHANGED ||
|
|
is_rela_section(sec) ||
|
|
!is_text_section(sec) ||
|
|
sec->sh.sh_size != sec->twin->sh.sh_size ||
|
|
!sec->rela ||
|
|
sec->rela->status != SAME)
|
|
return 0;
|
|
|
|
start1 = (unsigned long)sec->twin->data->d_buf;
|
|
start2 = (unsigned long)sec->data->d_buf;
|
|
size = sec->sh.sh_size;
|
|
for (offset = 0; offset < size; offset += PPC_INSTR_LEN) {
|
|
if (!memcmp((void *)start1 + offset, (void *)start2 + offset,
|
|
PPC_INSTR_LEN))
|
|
continue;
|
|
|
|
instr1 = *(unsigned int *)(start1 + offset) >> PPC_RA_OFFSET;
|
|
instr2 = *(unsigned int *)(start2 + offset) >> PPC_RA_OFFSET;
|
|
|
|
/* verify it's a load immediate to r5 */
|
|
if (!(instr1 == 0x38a0 && instr2 == 0x38a0))
|
|
return 0;
|
|
|
|
found = 0;
|
|
list_for_each_entry(rela, &sec->rela->relas, list) {
|
|
if (rela->offset < offset + PPC_INSTR_LEN)
|
|
continue;
|
|
if (toc_rela(rela) && toc_rela(rela)->string)
|
|
continue;
|
|
if (!strncmp(rela->sym->name, "__warned.", 9))
|
|
continue;
|
|
if (!strncmp(rela->sym->name, "warn_slowpath_", 14) ||
|
|
(!strcmp(rela->sym->name, "__warn_printk")) ||
|
|
(!strcmp(rela->sym->name, "__might_sleep")) ||
|
|
(!strcmp(rela->sym->name, "___might_sleep")) ||
|
|
(!strcmp(rela->sym->name, "__might_fault")) ||
|
|
(!strcmp(rela->sym->name, "printk")) ||
|
|
(!strcmp(rela->sym->name, "lockdep_rcu_suspicious"))) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
if (!found)
|
|
return 0;
|
|
|
|
lineonly = 1;
|
|
}
|
|
|
|
if (!lineonly)
|
|
ERROR("no instruction changes detected for changed section %s",
|
|
sec->name);
|
|
|
|
return 1;
|
|
}
|
|
#else
|
|
static int kpatch_line_macro_change_only(struct section *sec)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static void kpatch_compare_sections(struct list_head *seclist)
|
|
{
|
|
struct section *sec;
|
|
|
|
/* compare all sections */
|
|
list_for_each_entry(sec, seclist, list) {
|
|
if (sec->twin)
|
|
kpatch_compare_correlated_section(sec);
|
|
else
|
|
sec->status = NEW;
|
|
}
|
|
|
|
/* exclude WARN-only, might_sleep changes */
|
|
list_for_each_entry(sec, seclist, list) {
|
|
if (kpatch_line_macro_change_only(sec)) {
|
|
log_debug("reverting macro / line number section %s status to SAME\n",
|
|
sec->name);
|
|
sec->status = SAME;
|
|
}
|
|
}
|
|
|
|
/* sync symbol status */
|
|
list_for_each_entry(sec, seclist, list) {
|
|
if (is_rela_section(sec)) {
|
|
if (sec->base->sym && sec->base->sym->status != CHANGED)
|
|
sec->base->sym->status = sec->status;
|
|
} else {
|
|
struct symbol *sym = sec->sym;
|
|
|
|
if (sym && sym->status != CHANGED)
|
|
sym->status = sec->status;
|
|
|
|
if (sym && sym->child && sym->status == SAME &&
|
|
sym->child->sec->status == CHANGED)
|
|
sym->status = CHANGED;
|
|
}
|
|
}
|
|
}
|
|
|
|
static enum subsection kpatch_subsection_type(struct section *sec)
|
|
{
|
|
if (!strncmp(sec->name, ".text.unlikely.", 15))
|
|
return SUBSECTION_UNLIKELY;
|
|
|
|
if (!strncmp(sec->name, ".text.hot.", 10))
|
|
return SUBSECTION_HOT;
|
|
|
|
return SUBSECTION_NORMAL;
|
|
}
|
|
|
|
static int kpatch_subsection_changed(struct section *sec1, struct section *sec2)
|
|
{
|
|
return kpatch_subsection_type(sec1) != kpatch_subsection_type(sec2);
|
|
}
|
|
|
|
static void kpatch_compare_correlated_symbol(struct symbol *sym)
|
|
{
|
|
struct symbol *sym1 = sym, *sym2 = sym->twin;
|
|
|
|
if (sym1->sym.st_info != sym2->sym.st_info ||
|
|
(sym1->sec && !sym2->sec) ||
|
|
(sym2->sec && !sym1->sec))
|
|
DIFF_FATAL("symbol info mismatch: %s", sym1->name);
|
|
|
|
/*
|
|
* If two symbols are correlated but their sections are not, then the
|
|
* symbol has changed sections. This is only allowed if the symbol is
|
|
* moving out of an ignored section, or moving between normal/hot/unlikely
|
|
* subsections.
|
|
*/
|
|
if (sym1->sec && sym2->sec && sym1->sec->twin != sym2->sec) {
|
|
if ((sym2->sec->twin && sym2->sec->twin->ignore) ||
|
|
kpatch_subsection_changed(sym1->sec, sym2->sec))
|
|
sym->status = CHANGED;
|
|
else
|
|
DIFF_FATAL("symbol changed sections: %s", sym1->name);
|
|
}
|
|
|
|
if (sym1->type == STT_OBJECT &&
|
|
sym1->sym.st_size != sym2->sym.st_size)
|
|
DIFF_FATAL("object size mismatch: %s", sym1->name);
|
|
|
|
if (sym1->sym.st_shndx == SHN_UNDEF ||
|
|
sym1->sym.st_shndx == SHN_ABS)
|
|
sym1->status = SAME;
|
|
|
|
/*
|
|
* The status of LOCAL symbols is dependent on the status of their
|
|
* matching section and is set during section comparison.
|
|
*/
|
|
}
|
|
|
|
static void kpatch_compare_symbols(struct list_head *symlist)
|
|
{
|
|
struct symbol *sym;
|
|
|
|
list_for_each_entry(sym, symlist, list) {
|
|
if (sym->twin)
|
|
kpatch_compare_correlated_symbol(sym);
|
|
else
|
|
sym->status = NEW;
|
|
|
|
log_debug("symbol %s is %s\n", sym->name, status_str(sym->status));
|
|
}
|
|
}
|
|
|
|
static void kpatch_correlate_sections(struct list_head *seclist1, struct list_head *seclist2)
|
|
{
|
|
struct section *sec1, *sec2;
|
|
|
|
list_for_each_entry(sec1, seclist1, list) {
|
|
list_for_each_entry(sec2, seclist2, list) {
|
|
if (strcmp(sec1->name, sec2->name))
|
|
continue;
|
|
|
|
if (is_special_static(is_rela_section(sec1) ?
|
|
sec1->base->secsym :
|
|
sec1->secsym))
|
|
continue;
|
|
|
|
/*
|
|
* Group sections must match exactly to be correlated.
|
|
* Changed group sections are currently not supported.
|
|
*/
|
|
if (sec1->sh.sh_type == SHT_GROUP) {
|
|
if (sec1->data->d_size != sec2->data->d_size)
|
|
continue;
|
|
if (memcmp(sec1->data->d_buf, sec2->data->d_buf,
|
|
sec1->data->d_size))
|
|
continue;
|
|
}
|
|
sec1->twin = sec2;
|
|
sec2->twin = sec1;
|
|
/* set initial status, might change */
|
|
sec1->status = sec2->status = SAME;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void kpatch_correlate_symbols(struct list_head *symlist1, struct list_head *symlist2)
|
|
{
|
|
struct symbol *sym1, *sym2;
|
|
|
|
list_for_each_entry(sym1, symlist1, list) {
|
|
list_for_each_entry(sym2, symlist2, list) {
|
|
if (strcmp(sym1->name, sym2->name) ||
|
|
sym1->type != sym2->type)
|
|
continue;
|
|
|
|
if (is_special_static(sym1))
|
|
continue;
|
|
|
|
/*
|
|
* The .LCx symbols point to strings, usually used for
|
|
* the bug table. Don't correlate and compare the
|
|
* symbols themselves, because the suffix number might
|
|
* change.
|
|
*
|
|
* If the symbol is used by the bug table (usual case),
|
|
* it may get pulled in by
|
|
* kpatch_regenerate_special_section().
|
|
*
|
|
* If the symbol is used outside of the bug table (not
|
|
* sure if this actually happens anywhere), any string
|
|
* changes will be detected elsewhere in rela_equal().
|
|
*/
|
|
if (sym1->type == STT_NOTYPE &&
|
|
!strncmp(sym1->name, ".LC", 3))
|
|
continue;
|
|
|
|
/* group section symbols must have correlated sections */
|
|
if (sym1->sec &&
|
|
sym1->sec->sh.sh_type == SHT_GROUP &&
|
|
sym1->sec->twin != sym2->sec)
|
|
continue;
|
|
|
|
sym1->twin = sym2;
|
|
sym2->twin = sym1;
|
|
/* set initial status, might change */
|
|
sym1->status = sym2->status = SAME;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void kpatch_compare_elf_headers(Elf *elf1, Elf *elf2)
|
|
{
|
|
GElf_Ehdr eh1, eh2;
|
|
|
|
if (!gelf_getehdr(elf1, &eh1))
|
|
ERROR("gelf_getehdr");
|
|
|
|
if (!gelf_getehdr(elf2, &eh2))
|
|
ERROR("gelf_getehdr");
|
|
|
|
if (memcmp(eh1.e_ident, eh2.e_ident, EI_NIDENT) ||
|
|
eh1.e_type != eh2.e_type ||
|
|
eh1.e_machine != eh2.e_machine ||
|
|
eh1.e_version != eh2.e_version ||
|
|
eh1.e_entry != eh2.e_entry ||
|
|
eh1.e_phoff != eh2.e_phoff ||
|
|
eh1.e_flags != eh2.e_flags ||
|
|
eh1.e_ehsize != eh2.e_ehsize ||
|
|
eh1.e_phentsize != eh2.e_phentsize ||
|
|
eh1.e_shentsize != eh2.e_shentsize)
|
|
DIFF_FATAL("ELF headers differ");
|
|
}
|
|
|
|
static void kpatch_check_program_headers(Elf *elf)
|
|
{
|
|
size_t ph_nr;
|
|
|
|
if (elf_getphdrnum(elf, &ph_nr))
|
|
ERROR("elf_getphdrnum");
|
|
|
|
if (ph_nr != 0)
|
|
DIFF_FATAL("ELF contains program header");
|
|
}
|
|
|
|
static void kpatch_mark_grouped_sections(struct kpatch_elf *kelf)
|
|
{
|
|
struct section *groupsec, *sec;
|
|
unsigned int *data, *end;
|
|
|
|
list_for_each_entry(groupsec, &kelf->sections, list) {
|
|
if (groupsec->sh.sh_type != SHT_GROUP)
|
|
continue;
|
|
data = groupsec->data->d_buf;
|
|
end = groupsec->data->d_buf + groupsec->data->d_size;
|
|
data++; /* skip first flag word (e.g. GRP_COMDAT) */
|
|
while (data < end) {
|
|
sec = find_section_by_index(&kelf->sections, *data);
|
|
if (!sec)
|
|
ERROR("group section not found");
|
|
sec->grouped = 1;
|
|
log_debug("marking section %s (%d) as grouped\n",
|
|
sec->name, sec->index);
|
|
data++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* When gcc makes compiler optimizations which affect a function's calling
|
|
* interface, it mangles the function's name. For example, sysctl_print_dir is
|
|
* renamed to sysctl_print_dir.isra.2. The problem is that the trailing number
|
|
* is chosen arbitrarily, and the patched version of the function may end up
|
|
* with a different trailing number. Rename any mangled patched functions to
|
|
* match their base counterparts.
|
|
*/
|
|
static void kpatch_rename_mangled_functions(struct kpatch_elf *base,
|
|
struct kpatch_elf *patched)
|
|
{
|
|
struct symbol *sym, *basesym;
|
|
char name[256], *origname;
|
|
struct section *sec, *basesec;
|
|
int found;
|
|
|
|
list_for_each_entry(sym, &patched->symbols, list) {
|
|
if (sym->type != STT_FUNC)
|
|
continue;
|
|
|
|
if (!strstr(sym->name, ".isra.") &&
|
|
!strstr(sym->name, ".constprop.") &&
|
|
!strstr(sym->name, ".cold.") &&
|
|
!strstr(sym->name, ".part."))
|
|
continue;
|
|
|
|
found = 0;
|
|
list_for_each_entry(basesym, &base->symbols, list) {
|
|
if (!kpatch_mangled_strcmp(basesym->name, sym->name)) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
continue;
|
|
|
|
if (!strcmp(sym->name, basesym->name))
|
|
continue;
|
|
|
|
log_debug("renaming %s to %s\n", sym->name, basesym->name);
|
|
origname = sym->name;
|
|
sym->name = strdup(basesym->name);
|
|
|
|
if (sym != sym->sec->sym)
|
|
continue;
|
|
|
|
sym->sec->name = strdup(basesym->sec->name);
|
|
if (sym->sec->rela)
|
|
sym->sec->rela->name = strdup(basesym->sec->rela->name);
|
|
|
|
/*
|
|
* When function foo.isra.1 has a switch statement, it might
|
|
* have a corresponding bundled .rodata.foo.isra.1 section (in
|
|
* addition to .text.foo.isra.1 which we renamed above).
|
|
*/
|
|
sprintf(name, ".rodata.%s", origname);
|
|
sec = find_section_by_name(&patched->sections, name);
|
|
if (!sec)
|
|
continue;
|
|
sprintf(name, ".rodata.%s", basesym->name);
|
|
basesec = find_section_by_name(&base->sections, name);
|
|
if (!basesec)
|
|
continue;
|
|
sec->name = strdup(basesec->name);
|
|
sec->secsym->name = sec->name;
|
|
if (sec->rela)
|
|
sec->rela->name = strdup(basesec->rela->name);
|
|
}
|
|
}
|
|
|
|
static char *kpatch_section_function_name(struct section *sec)
|
|
{
|
|
if (is_rela_section(sec))
|
|
sec = sec->base;
|
|
return sec->sym ? sec->sym->name : sec->name;
|
|
}
|
|
|
|
/*
|
|
* Given a static local variable symbol and a rela section which references it
|
|
* in the base object, find a corresponding usage of a similarly named symbol
|
|
* in the patched object.
|
|
*/
|
|
static struct symbol *kpatch_find_static_twin(struct section *sec,
|
|
struct symbol *sym)
|
|
{
|
|
struct rela *rela, *rela_toc;
|
|
|
|
if (!sec->twin)
|
|
return NULL;
|
|
|
|
/* find the patched object's corresponding variable */
|
|
list_for_each_entry(rela, &sec->twin->relas, list) {
|
|
|
|
rela_toc = toc_rela(rela);
|
|
if (!rela_toc)
|
|
continue; /* skip toc constants */
|
|
|
|
if (rela_toc->sym->twin)
|
|
continue;
|
|
|
|
if (kpatch_mangled_strcmp(rela_toc->sym->name, sym->name))
|
|
continue;
|
|
|
|
return rela_toc->sym;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int kpatch_is_normal_static_local(struct symbol *sym)
|
|
{
|
|
if (sym->type != STT_OBJECT || sym->bind != STB_LOCAL)
|
|
return 0;
|
|
|
|
if (!strchr(sym->name, '.'))
|
|
return 0;
|
|
|
|
if (is_special_static(sym))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* gcc renames static local variables by appending a period and a number. For
|
|
* example, __foo could be renamed to __foo.31452. Unfortunately this number
|
|
* can arbitrarily change. Correlate them by comparing which functions
|
|
* reference them, and rename the patched symbols to match the base symbol
|
|
* names.
|
|
*
|
|
* Some surprising facts about static local variable symbols:
|
|
*
|
|
* - It's possible for multiple functions to use the same
|
|
* static local variable if the variable is defined in an
|
|
* inlined function.
|
|
*
|
|
* - It's also possible for multiple static local variables
|
|
* with the same name to be used in the same function if they
|
|
* have different scopes. (We have to assume that in such
|
|
* cases, the order in which they're referenced remains the
|
|
* same between the base and patched objects, as there's no
|
|
* other way to distinguish them.)
|
|
*
|
|
* - Static locals are usually referenced by functions, but
|
|
* they can occasionally be referenced by data sections as
|
|
* well.
|
|
*/
|
|
static void kpatch_correlate_static_local_variables(struct kpatch_elf *base,
|
|
struct kpatch_elf *patched)
|
|
{
|
|
struct symbol *sym, *patched_sym;
|
|
struct section *sec;
|
|
struct rela *rela, *rela2;
|
|
int bundled, patched_bundled, found;
|
|
|
|
/*
|
|
* First undo the correlations for all static locals. Two static
|
|
* locals can have the same numbered suffix in the base and patched
|
|
* objects by coincidence.
|
|
*/
|
|
list_for_each_entry(sym, &base->symbols, list) {
|
|
|
|
if (!kpatch_is_normal_static_local(sym))
|
|
continue;
|
|
|
|
if (sym->twin) {
|
|
sym->twin->twin = NULL;
|
|
sym->twin = NULL;
|
|
}
|
|
|
|
bundled = sym == sym->sec->sym;
|
|
if (bundled && sym->sec->twin) {
|
|
sym->sec->twin->twin = NULL;
|
|
sym->sec->twin = NULL;
|
|
|
|
sym->sec->secsym->twin->twin = NULL;
|
|
sym->sec->secsym->twin = NULL;
|
|
|
|
if (sym->sec->rela) {
|
|
sym->sec->rela->twin->twin = NULL;
|
|
sym->sec->rela->twin = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Do the correlations: for each section reference to a static local,
|
|
* look for a corresponding reference in the section's twin.
|
|
*/
|
|
list_for_each_entry(sec, &base->sections, list) {
|
|
|
|
if (!is_rela_section(sec) ||
|
|
is_debug_section(sec) ||
|
|
!strcmp(sec->name, ".rela.toc"))
|
|
continue;
|
|
|
|
list_for_each_entry(rela, &sec->relas, list) {
|
|
|
|
if (!toc_rela(rela))
|
|
continue; /* skip toc constants */
|
|
sym = toc_rela(rela)->sym;
|
|
|
|
if (!kpatch_is_normal_static_local(sym))
|
|
continue;
|
|
|
|
if (sym->twin)
|
|
continue;
|
|
|
|
bundled = sym == sym->sec->sym;
|
|
if (bundled && sym->sec == sec->base) {
|
|
/*
|
|
* A rare case where a static local data
|
|
* structure references itself. There's no
|
|
* reliable way to correlate this. Hopefully
|
|
* there's another reference to the symbol
|
|
* somewhere that can be used.
|
|
*/
|
|
log_debug("can't correlate static local %s's reference to itself\n",
|
|
sym->name);
|
|
continue;
|
|
}
|
|
|
|
patched_sym = kpatch_find_static_twin(sec, sym);
|
|
if (!patched_sym)
|
|
DIFF_FATAL("reference to static local variable %s in %s was removed",
|
|
sym->name,
|
|
kpatch_section_function_name(sec));
|
|
|
|
patched_bundled = patched_sym == patched_sym->sec->sym;
|
|
if (bundled != patched_bundled)
|
|
ERROR("bundle mismatch for symbol %s", sym->name);
|
|
if (!bundled && sym->sec->twin != patched_sym->sec)
|
|
ERROR("sections %s and %s aren't correlated for symbol %s",
|
|
sym->sec->name, patched_sym->sec->name, sym->name);
|
|
|
|
log_debug("renaming and correlating static local %s to %s\n",
|
|
patched_sym->name, sym->name);
|
|
|
|
patched_sym->name = strdup(sym->name);
|
|
sym->twin = patched_sym;
|
|
patched_sym->twin = sym;
|
|
|
|
/* set initial status, might change */
|
|
sym->status = patched_sym->status = SAME;
|
|
|
|
if (bundled) {
|
|
sym->sec->twin = patched_sym->sec;
|
|
patched_sym->sec->twin = sym->sec;
|
|
|
|
sym->sec->secsym->twin = patched_sym->sec->secsym;
|
|
patched_sym->sec->secsym->twin = sym->sec->secsym;
|
|
|
|
if (sym->sec->rela && patched_sym->sec->rela) {
|
|
sym->sec->rela->twin = patched_sym->sec->rela;
|
|
patched_sym->sec->rela->twin = sym->sec->rela;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Make sure that:
|
|
*
|
|
* 1. all the base object's referenced static locals have been
|
|
* correlated; and
|
|
*
|
|
* 2. each reference to a static local in the base object has a
|
|
* corresponding reference in the patched object (because a static
|
|
* local can be referenced by more than one section).
|
|
*/
|
|
list_for_each_entry(sec, &base->sections, list) {
|
|
|
|
if (!is_rela_section(sec) ||
|
|
is_debug_section(sec))
|
|
continue;
|
|
|
|
list_for_each_entry(rela, &sec->relas, list) {
|
|
|
|
sym = rela->sym;
|
|
if (!kpatch_is_normal_static_local(sym))
|
|
continue;
|
|
|
|
if (!sym->twin || !sec->twin)
|
|
DIFF_FATAL("reference to static local variable %s in %s was removed",
|
|
sym->name,
|
|
kpatch_section_function_name(sec));
|
|
|
|
found = 0;
|
|
list_for_each_entry(rela2, &sec->twin->relas, list) {
|
|
if (rela2->sym == sym->twin) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
DIFF_FATAL("static local %s has been correlated with %s, but patched %s is missing a reference to it",
|
|
sym->name, sym->twin->name,
|
|
kpatch_section_function_name(sec->twin));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now go through the patched object and look for any uncorrelated
|
|
* static locals to see if we need to print any warnings about new
|
|
* variables.
|
|
*/
|
|
list_for_each_entry(sec, &patched->sections, list) {
|
|
|
|
if (!is_rela_section(sec) ||
|
|
is_debug_section(sec))
|
|
continue;
|
|
|
|
list_for_each_entry(rela, &sec->relas, list) {
|
|
|
|
sym = rela->sym;
|
|
if (!kpatch_is_normal_static_local(sym))
|
|
continue;
|
|
|
|
if (sym->twin)
|
|
continue;
|
|
|
|
log_normal("WARNING: unable to correlate static local variable %s used by %s, assuming variable is new\n",
|
|
sym->name,
|
|
kpatch_section_function_name(sec));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void kpatch_correlate_elfs(struct kpatch_elf *kelf1, struct kpatch_elf *kelf2)
|
|
{
|
|
kpatch_correlate_sections(&kelf1->sections, &kelf2->sections);
|
|
kpatch_correlate_symbols(&kelf1->symbols, &kelf2->symbols);
|
|
}
|
|
|
|
static void kpatch_compare_correlated_elements(struct kpatch_elf *kelf)
|
|
{
|
|
/* lists are already correlated at this point */
|
|
kpatch_compare_sections(&kelf->sections);
|
|
kpatch_compare_symbols(&kelf->symbols);
|
|
}
|
|
|
|
#ifdef __x86_64__
|
|
static void rela_insn(struct section *sec, struct rela *rela, struct insn *insn)
|
|
{
|
|
unsigned long insn_addr, start, end, rela_addr;
|
|
|
|
start = (unsigned long)sec->base->data->d_buf;
|
|
end = start + sec->base->sh.sh_size;
|
|
|
|
if (end <= start)
|
|
ERROR("bad section size");
|
|
|
|
rela_addr = start + rela->offset;
|
|
for (insn_addr = start; insn_addr < end; insn_addr += insn->length) {
|
|
insn_init(insn, (void *)insn_addr, 1);
|
|
insn_get_length(insn);
|
|
if (!insn->length)
|
|
ERROR("can't decode instruction in section %s at offset 0x%lx",
|
|
sec->base->name, insn_addr);
|
|
if (rela_addr >= insn_addr &&
|
|
rela_addr < insn_addr + insn->length)
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static bool is_callback_section(struct section *sec) {
|
|
|
|
static char *callback_sections[] = {
|
|
".kpatch.callbacks.pre_patch",
|
|
".kpatch.callbacks.post_patch",
|
|
".kpatch.callbacks.pre_unpatch",
|
|
".kpatch.callbacks.post_unpatch",
|
|
".rela.kpatch.callbacks.pre_patch",
|
|
".rela.kpatch.callbacks.post_patch",
|
|
".rela.kpatch.callbacks.pre_unpatch",
|
|
".rela.kpatch.callbacks.post_unpatch",
|
|
NULL,
|
|
};
|
|
char **callback_sec;
|
|
|
|
for (callback_sec = callback_sections; *callback_sec; callback_sec++)
|
|
if (!strcmp(sec->name, *callback_sec))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Mangle the relas a little. The compiler will sometimes use section symbols
|
|
* to reference local objects and functions rather than the object or function
|
|
* symbols themselves. We substitute the object/function symbols for the
|
|
* section symbol in this case so that the relas can be properly correlated and
|
|
* so that the existing object/function in vmlinux can be linked to.
|
|
*/
|
|
static void kpatch_replace_sections_syms(struct kpatch_elf *kelf)
|
|
{
|
|
struct section *sec;
|
|
struct rela *rela;
|
|
struct symbol *sym;
|
|
int add_off;
|
|
|
|
log_debug("\n");
|
|
|
|
list_for_each_entry(sec, &kelf->sections, list) {
|
|
if (!is_rela_section(sec) ||
|
|
is_debug_section(sec))
|
|
continue;
|
|
|
|
list_for_each_entry(rela, &sec->relas, list) {
|
|
|
|
if (rela->sym->type != STT_SECTION)
|
|
continue;
|
|
|
|
/*
|
|
* Replace references to bundled sections with their
|
|
* symbols.
|
|
*/
|
|
if (rela->sym->sec && rela->sym->sec->sym) {
|
|
rela->sym = rela->sym->sec->sym;
|
|
|
|
/*
|
|
* On ppc64le with GCC6+, even with
|
|
* -ffunction-sections, the function symbol
|
|
* starts 8 bytes past the beginning of the
|
|
* section, because the .TOC pointer is at the
|
|
* beginning, right before the code. So even
|
|
* though the symbol is bundled, we can't
|
|
* assume it's at offset 0 in the section.
|
|
*/
|
|
rela->addend -= rela->sym->sym.st_value;
|
|
|
|
continue;
|
|
}
|
|
|
|
#ifdef __powerpc64__
|
|
add_off = 0;
|
|
#else
|
|
if (rela->type == R_X86_64_PC32 ||
|
|
rela->type == R_X86_64_PLT32) {
|
|
struct insn insn;
|
|
rela_insn(sec, rela, &insn);
|
|
add_off = (long)insn.next_byte -
|
|
(long)sec->base->data->d_buf -
|
|
rela->offset;
|
|
} else if (rela->type == R_X86_64_64 ||
|
|
rela->type == R_X86_64_32S)
|
|
add_off = 0;
|
|
else
|
|
continue;
|
|
#endif
|
|
|
|
/*
|
|
* Attempt to replace references to unbundled sections
|
|
* with their symbols.
|
|
*/
|
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
int start, end;
|
|
|
|
if (sym->type == STT_SECTION ||
|
|
!sym->sec ||
|
|
sym->sec != rela->sym->sec)
|
|
continue;
|
|
|
|
start = sym->sym.st_value;
|
|
end = sym->sym.st_value + sym->sym.st_size;
|
|
|
|
if (!is_text_section(sym->sec) &&
|
|
rela->type == R_X86_64_32S &&
|
|
rela->addend == (int)sym->sec->sh.sh_size &&
|
|
end == (int)sym->sec->sh.sh_size) {
|
|
|
|
/*
|
|
* A special case where gcc needs a
|
|
* pointer to the address at the end of
|
|
* a data section.
|
|
*
|
|
* This is usually used with a compare
|
|
* instruction to determine when to end
|
|
* a loop. The code doesn't actually
|
|
* dereference the pointer so this is
|
|
* "normal" and we just replace the
|
|
* section reference with a reference
|
|
* to the last symbol in the section.
|
|
*
|
|
* Note that this only catches the
|
|
* issue when it happens at the end of
|
|
* a section. It can also happen in
|
|
* the middle of a section. In that
|
|
* case, the wrong symbol will be
|
|
* associated with the reference. But
|
|
* that's ok because:
|
|
*
|
|
* 1) This situation only occurs when
|
|
* gcc is trying to get the address
|
|
* of the symbol, not the contents
|
|
* of its data; and
|
|
*
|
|
* 2) Because kpatch doesn't allow data
|
|
* sections to change,
|
|
* &(var1+sizeof(var1)) will always
|
|
* be the same as &var2.
|
|
*/
|
|
|
|
} else if (rela->addend + add_off < start ||
|
|
rela->addend + add_off >= end)
|
|
continue;
|
|
|
|
log_debug("%s: replacing %s+%d reference with %s+%d\n",
|
|
sec->name,
|
|
rela->sym->name, rela->addend,
|
|
sym->name, rela->addend - start);
|
|
|
|
rela->sym = sym;
|
|
rela->addend -= start;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
log_debug("\n");
|
|
}
|
|
|
|
static void kpatch_check_func_profiling_calls(struct kpatch_elf *kelf)
|
|
{
|
|
struct symbol *sym;
|
|
int errs = 0;
|
|
|
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
if (sym->type != STT_FUNC || sym->status != CHANGED || sym->parent)
|
|
continue;
|
|
if (!sym->twin->has_func_profiling) {
|
|
log_normal("function %s has no fentry/mcount call, unable to patch\n",
|
|
sym->name);
|
|
errs++;
|
|
}
|
|
}
|
|
|
|
if (errs)
|
|
DIFF_FATAL("%d function(s) can not be patched", errs);
|
|
}
|
|
|
|
static void kpatch_verify_patchability(struct kpatch_elf *kelf)
|
|
{
|
|
struct section *sec;
|
|
int errs = 0;
|
|
|
|
list_for_each_entry(sec, &kelf->sections, list) {
|
|
if (sec->status == CHANGED && !sec->include) {
|
|
log_normal("changed section %s not selected for inclusion\n",
|
|
sec->name);
|
|
errs++;
|
|
}
|
|
|
|
if (sec->status != SAME && sec->grouped) {
|
|
log_normal("changed section %s is part of a section group\n",
|
|
sec->name);
|
|
errs++;
|
|
}
|
|
|
|
if (sec->sh.sh_type == SHT_GROUP && sec->status == NEW) {
|
|
log_normal("new/changed group sections are not supported\n");
|
|
errs++;
|
|
}
|
|
|
|
/*
|
|
* ensure we aren't including .data.* or .bss.*
|
|
* (.data.unlikely and .data.once is ok b/c it only has __warned vars)
|
|
*/
|
|
if (sec->include && sec->status != NEW &&
|
|
(!strncmp(sec->name, ".data", 5) || !strncmp(sec->name, ".bss", 4)) &&
|
|
(strcmp(sec->name, ".data.unlikely") && strcmp(sec->name, ".data.once"))) {
|
|
log_normal("data section %s selected for inclusion\n",
|
|
sec->name);
|
|
errs++;
|
|
}
|
|
}
|
|
|
|
if (errs)
|
|
DIFF_FATAL("%d unsupported section change(s)", errs);
|
|
}
|
|
|
|
static void kpatch_include_symbol(struct symbol *sym);
|
|
|
|
static void kpatch_include_section(struct section *sec)
|
|
{
|
|
struct rela *rela;
|
|
|
|
/* Include the section and its section symbol */
|
|
if (sec->include)
|
|
return;
|
|
sec->include = 1;
|
|
if (sec->secsym)
|
|
sec->secsym->include = 1;
|
|
|
|
/*
|
|
* Include the section's rela section and then recursively include the
|
|
* symbols needed by its relas.
|
|
*/
|
|
if (!sec->rela)
|
|
return;
|
|
sec->rela->include = 1;
|
|
list_for_each_entry(rela, &sec->rela->relas, list)
|
|
kpatch_include_symbol(rela->sym);
|
|
}
|
|
|
|
static void kpatch_include_symbol(struct symbol *sym)
|
|
{
|
|
/*
|
|
* This function is called recursively from kpatch_include_section().
|
|
* Make sure we don't get into an endless loop.
|
|
*/
|
|
if (sym->include)
|
|
return;
|
|
|
|
/*
|
|
* The symbol gets included even if its section isn't needed, as it
|
|
* might be needed: either permanently for a rela, or temporarily for
|
|
* the later creation of a dynrela.
|
|
*/
|
|
sym->include = 1;
|
|
|
|
/*
|
|
* For a function/object symbol, if it has a section, we only need to
|
|
* include the section if it has changed. Otherwise the symbol will be
|
|
* used by relas/dynrelas to link to the real symbol externally.
|
|
*
|
|
* For section symbols, we always include the section because
|
|
* references to them can't otherwise be resolved externally.
|
|
*/
|
|
if (sym->sec && (sym->type == STT_SECTION || sym->status != SAME))
|
|
kpatch_include_section(sym->sec);
|
|
}
|
|
|
|
static void kpatch_include_standard_elements(struct kpatch_elf *kelf)
|
|
{
|
|
struct section *sec;
|
|
|
|
list_for_each_entry(sec, &kelf->sections, list) {
|
|
/*
|
|
* Include the following sections even if they haven't changed.
|
|
*
|
|
* Notes about some of the more interesting sections:
|
|
*
|
|
* - With -fdata-sections, .rodata is only used for:
|
|
*
|
|
* switch jump tables;
|
|
* KASAN data (with KASAN enabled, which is rare); and
|
|
* an ugly hack in vmx_vcpu_run().
|
|
*
|
|
* Those data are all local to the functions which use them.
|
|
* So it's safe to include .rodata.
|
|
*
|
|
* - On ppc64le, the .toc section is used for all data
|
|
* accesses.
|
|
*
|
|
* Note that if any of these sections have rela sections, they
|
|
* will also be included in their entirety. That may result in
|
|
* some extra (unused) dynrelas getting created, which should
|
|
* be harmless.
|
|
*/
|
|
if (!strcmp(sec->name, ".shstrtab") ||
|
|
!strcmp(sec->name, ".strtab") ||
|
|
!strcmp(sec->name, ".symtab") ||
|
|
!strcmp(sec->name, ".toc") ||
|
|
!strcmp(sec->name, ".rodata") ||
|
|
(!strncmp(sec->name, ".rodata.", 8) &&
|
|
strstr(sec->name, ".str1."))) {
|
|
kpatch_include_section(sec);
|
|
}
|
|
}
|
|
|
|
/* include the NULL symbol */
|
|
list_entry(kelf->symbols.next, struct symbol, list)->include = 1;
|
|
}
|
|
|
|
static int kpatch_include_callback_elements(struct kpatch_elf *kelf)
|
|
{
|
|
struct section *sec;
|
|
struct symbol *sym;
|
|
struct rela *rela;
|
|
int found = 0;
|
|
|
|
/* include load/unload sections */
|
|
list_for_each_entry(sec, &kelf->sections, list) {
|
|
if (!is_callback_section(sec))
|
|
continue;
|
|
|
|
sec->include = 1;
|
|
found = 1;
|
|
if (is_rela_section(sec)) {
|
|
/* include callback dependencies */
|
|
rela = list_entry(sec->relas.next, struct rela, list);
|
|
sym = rela->sym;
|
|
log_normal("found callback: %s\n",sym->name);
|
|
kpatch_include_symbol(sym);
|
|
} else {
|
|
sec->secsym->include = 1;
|
|
}
|
|
}
|
|
|
|
/* Strip temporary global structures used by the callback macros. */
|
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
if (sym->sec && is_callback_section(sym->sec))
|
|
sym->include = 0;
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
static void kpatch_include_force_elements(struct kpatch_elf *kelf)
|
|
{
|
|
struct section *sec;
|
|
struct symbol *sym;
|
|
struct rela *rela;
|
|
|
|
/* include force sections */
|
|
list_for_each_entry(sec, &kelf->sections, list) {
|
|
if (!strcmp(sec->name, ".kpatch.force") ||
|
|
!strcmp(sec->name, ".rela.kpatch.force")) {
|
|
sec->include = 1;
|
|
if (!is_rela_section(sec)) {
|
|
/* .kpatch.force */
|
|
sec->secsym->include = 1;
|
|
continue;
|
|
}
|
|
/* .rela.kpatch.force */
|
|
list_for_each_entry(rela, &sec->relas, list)
|
|
log_normal("function '%s' marked with KPATCH_FORCE_UNSAFE!\n",
|
|
rela->sym->name);
|
|
}
|
|
}
|
|
|
|
/* strip temporary global kpatch_force_func_* symbols */
|
|
list_for_each_entry(sym, &kelf->symbols, list)
|
|
if (!strncmp(sym->name, "__kpatch_force_func_",
|
|
strlen("__kpatch_force_func_")))
|
|
sym->include = 0;
|
|
}
|
|
|
|
static int kpatch_include_new_globals(struct kpatch_elf *kelf)
|
|
{
|
|
struct symbol *sym;
|
|
int nr = 0;
|
|
|
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
if (sym->bind == STB_GLOBAL && sym->sec &&
|
|
sym->status == NEW) {
|
|
kpatch_include_symbol(sym);
|
|
nr++;
|
|
}
|
|
}
|
|
|
|
return nr;
|
|
}
|
|
|
|
static int kpatch_include_changed_functions(struct kpatch_elf *kelf)
|
|
{
|
|
struct symbol *sym;
|
|
int changed_nr = 0;
|
|
|
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
if (sym->status == CHANGED &&
|
|
sym->type == STT_FUNC) {
|
|
changed_nr++;
|
|
kpatch_include_symbol(sym);
|
|
}
|
|
|
|
if (sym->type == STT_FILE)
|
|
sym->include = 1;
|
|
}
|
|
|
|
return changed_nr;
|
|
}
|
|
|
|
static void kpatch_print_changes(struct kpatch_elf *kelf)
|
|
{
|
|
struct symbol *sym;
|
|
|
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
if (!sym->include || !sym->sec || sym->type != STT_FUNC || sym->parent)
|
|
continue;
|
|
if (sym->status == NEW)
|
|
log_normal("new function: %s\n", sym->name);
|
|
else if (sym->status == CHANGED)
|
|
log_normal("changed function: %s\n", sym->name);
|
|
}
|
|
}
|
|
|
|
static void kpatch_migrate_symbols(struct list_head *src,
|
|
struct list_head *dst,
|
|
int (*select)(struct symbol *))
|
|
{
|
|
struct symbol *sym, *safe;
|
|
|
|
list_for_each_entry_safe(sym, safe, src, list) {
|
|
if (select && !select(sym))
|
|
continue;
|
|
|
|
list_del(&sym->list);
|
|
list_add_tail(&sym->list, dst);
|
|
}
|
|
}
|
|
|
|
static void kpatch_migrate_included_elements(struct kpatch_elf *kelf, struct kpatch_elf **kelfout)
|
|
{
|
|
struct section *sec, *safesec;
|
|
struct symbol *sym, *safesym;
|
|
struct kpatch_elf *out;
|
|
|
|
/* allocate output kelf */
|
|
out = malloc(sizeof(*out));
|
|
if (!out)
|
|
ERROR("malloc");
|
|
memset(out, 0, sizeof(*out));
|
|
INIT_LIST_HEAD(&out->sections);
|
|
INIT_LIST_HEAD(&out->symbols);
|
|
INIT_LIST_HEAD(&out->strings);
|
|
|
|
/* migrate included sections from kelf to out */
|
|
list_for_each_entry_safe(sec, safesec, &kelf->sections, list) {
|
|
if (!sec->include)
|
|
continue;
|
|
list_del(&sec->list);
|
|
list_add_tail(&sec->list, &out->sections);
|
|
sec->index = 0;
|
|
if (!is_rela_section(sec) && sec->secsym && !sec->secsym->include)
|
|
/* break link to non-included section symbol */
|
|
sec->secsym = NULL;
|
|
}
|
|
|
|
/* migrate included symbols from kelf to out */
|
|
list_for_each_entry_safe(sym, safesym, &kelf->symbols, list) {
|
|
if (!sym->include)
|
|
continue;
|
|
list_del(&sym->list);
|
|
list_add_tail(&sym->list, &out->symbols);
|
|
sym->index = 0;
|
|
sym->strip = 0;
|
|
if (sym->sec && !sym->sec->include)
|
|
/* break link to non-included section */
|
|
sym->sec = NULL;
|
|
|
|
}
|
|
|
|
*kelfout = out;
|
|
}
|
|
|
|
static void kpatch_reorder_symbols(struct kpatch_elf *kelf)
|
|
{
|
|
LIST_HEAD(symbols);
|
|
|
|
/* migrate NULL sym */
|
|
kpatch_migrate_symbols(&kelf->symbols, &symbols, is_null_sym);
|
|
/* migrate LOCAL FILE sym */
|
|
kpatch_migrate_symbols(&kelf->symbols, &symbols, is_file_sym);
|
|
/* migrate LOCAL FUNC syms */
|
|
kpatch_migrate_symbols(&kelf->symbols, &symbols, is_local_func_sym);
|
|
/* migrate all other LOCAL syms */
|
|
kpatch_migrate_symbols(&kelf->symbols, &symbols, is_local_sym);
|
|
/* migrate all other (GLOBAL) syms */
|
|
kpatch_migrate_symbols(&kelf->symbols, &symbols, NULL);
|
|
|
|
list_replace(&symbols, &kelf->symbols);
|
|
}
|
|
|
|
static int bug_table_group_size(struct kpatch_elf *kelf, int offset)
|
|
{
|
|
static int size = 0;
|
|
char *str;
|
|
|
|
if (!size) {
|
|
str = getenv("BUG_STRUCT_SIZE");
|
|
if (!str)
|
|
ERROR("BUG_STRUCT_SIZE not set");
|
|
size = atoi(str);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static int ex_table_group_size(struct kpatch_elf *kelf, int offset)
|
|
{
|
|
static int size = 0;
|
|
char *str;
|
|
|
|
if (!size) {
|
|
str = getenv("EX_STRUCT_SIZE");
|
|
if (!str)
|
|
ERROR("EX_STRUCT_SIZE not set");
|
|
size = atoi(str);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static int jump_table_group_size(struct kpatch_elf *kelf, int offset)
|
|
{
|
|
static int size = 0;
|
|
char *str;
|
|
|
|
if (!size) {
|
|
str = getenv("JUMP_STRUCT_SIZE");
|
|
if (!str)
|
|
ERROR("JUMP_STRUCT_SIZE not set");
|
|
size = atoi(str);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
#ifdef __x86_64__
|
|
static int parainstructions_group_size(struct kpatch_elf *kelf, int offset)
|
|
{
|
|
static int size = 0;
|
|
char *str;
|
|
|
|
if (!size) {
|
|
str = getenv("PARA_STRUCT_SIZE");
|
|
if (!str)
|
|
ERROR("PARA_STRUCT_SIZE not set");
|
|
size = atoi(str);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static int altinstructions_group_size(struct kpatch_elf *kelf, int offset)
|
|
{
|
|
static int size = 0;
|
|
char *str;
|
|
|
|
if (!size) {
|
|
str = getenv("ALT_STRUCT_SIZE");
|
|
if (!str)
|
|
ERROR("ALT_STRUCT_SIZE not set");
|
|
size = atoi(str);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static int smp_locks_group_size(struct kpatch_elf *kelf, int offset)
|
|
{
|
|
return 4;
|
|
}
|
|
#endif
|
|
#ifdef __powerpc64__
|
|
static int fixup_entry_group_size(struct kpatch_elf *kelf, int offset)
|
|
{
|
|
static int size = 0;
|
|
char *str;
|
|
|
|
if (!size) {
|
|
str = getenv("FIXUP_STRUCT_SIZE");
|
|
if (!str)
|
|
ERROR("FIXUP_STRUCT_SIZE not set");
|
|
size = atoi(str);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static int fixup_lwsync_group_size(struct kpatch_elf *kelf, int offset)
|
|
{
|
|
return 8;
|
|
}
|
|
|
|
static int fixup_barrier_nospec_group_size(struct kpatch_elf *kelf, int offset)
|
|
{
|
|
return 8;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* The rela groups in the .fixup section vary in size. The beginning of each
|
|
* .fixup rela group is referenced by the __ex_table section. To find the size
|
|
* of a .fixup rela group, we have to traverse the __ex_table relas.
|
|
*/
|
|
static int fixup_group_size(struct kpatch_elf *kelf, int offset)
|
|
{
|
|
struct section *sec;
|
|
struct rela *rela;
|
|
int found;
|
|
|
|
sec = find_section_by_name(&kelf->sections, ".rela__ex_table");
|
|
if (!sec)
|
|
ERROR("missing .rela__ex_table section");
|
|
|
|
/* find beginning of this group */
|
|
found = 0;
|
|
list_for_each_entry(rela, &sec->relas, list) {
|
|
if (!strcmp(rela->sym->name, ".fixup") &&
|
|
rela->addend == offset) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
ERROR("can't find .fixup rela group at offset %d\n", offset);
|
|
|
|
/* find beginning of next group */
|
|
found = 0;
|
|
list_for_each_entry_continue(rela, &sec->relas, list) {
|
|
if (!strcmp(rela->sym->name, ".fixup") &&
|
|
rela->addend > offset) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
/* last group */
|
|
struct section *fixupsec;
|
|
fixupsec = find_section_by_name(&kelf->sections, ".fixup");
|
|
if (!fixupsec)
|
|
ERROR("missing .fixup section");
|
|
return fixupsec->sh.sh_size - offset;
|
|
}
|
|
|
|
return rela->addend - offset;
|
|
}
|
|
|
|
static struct special_section special_sections[] = {
|
|
{
|
|
.name = "__bug_table",
|
|
.group_size = bug_table_group_size,
|
|
},
|
|
{
|
|
.name = ".fixup",
|
|
.group_size = fixup_group_size,
|
|
},
|
|
{
|
|
.name = "__ex_table", /* must come after .fixup */
|
|
.group_size = ex_table_group_size,
|
|
},
|
|
{
|
|
.name = "__jump_table",
|
|
.group_size = jump_table_group_size,
|
|
},
|
|
#ifdef __x86_64__
|
|
{
|
|
.name = ".smp_locks",
|
|
.group_size = smp_locks_group_size,
|
|
},
|
|
{
|
|
.name = ".parainstructions",
|
|
.group_size = parainstructions_group_size,
|
|
},
|
|
{
|
|
.name = ".altinstructions",
|
|
.group_size = altinstructions_group_size,
|
|
},
|
|
#endif
|
|
#ifdef __powerpc64__
|
|
{
|
|
.name = "__ftr_fixup",
|
|
.group_size = fixup_entry_group_size,
|
|
.unsupported = 1,
|
|
},
|
|
{
|
|
.name = "__mmu_ftr_fixup",
|
|
.group_size = fixup_entry_group_size,
|
|
.unsupported = 1,
|
|
},
|
|
{
|
|
.name = "__fw_ftr_fixup",
|
|
.group_size = fixup_entry_group_size,
|
|
.unsupported = 1,
|
|
},
|
|
{
|
|
.name = "__lwsync_fixup",
|
|
.group_size = fixup_lwsync_group_size,
|
|
.unsupported = 1,
|
|
},
|
|
{
|
|
.name = "__barrier_nospec_fixup",
|
|
.group_size = fixup_barrier_nospec_group_size,
|
|
.unsupported = 1,
|
|
},
|
|
#endif
|
|
{},
|
|
};
|
|
|
|
static int should_keep_rela_group(struct section *sec, unsigned int start,
|
|
unsigned int size)
|
|
{
|
|
struct rela *rela;
|
|
int found = 0;
|
|
|
|
/* check if any relas in the group reference any changed functions */
|
|
list_for_each_entry(rela, &sec->relas, list) {
|
|
if (rela->offset >= start &&
|
|
rela->offset < start + size &&
|
|
rela->sym->type == STT_FUNC &&
|
|
rela->sym->sec->include) {
|
|
found = 1;
|
|
log_debug("new/changed symbol %s found in special section %s\n",
|
|
rela->sym->name, sec->name);
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
/*
|
|
* When updating .fixup, the corresponding addends in .ex_table need to be
|
|
* updated too. Stash the result in rela.r_addend so that the calculation in
|
|
* fixup_group_size() is not affected.
|
|
*/
|
|
static void kpatch_update_ex_table_addend(struct kpatch_elf *kelf,
|
|
struct special_section *special,
|
|
int src_offset, int dest_offset,
|
|
int group_size)
|
|
{
|
|
struct rela *rela;
|
|
struct section *sec;
|
|
|
|
sec = find_section_by_name(&kelf->sections, ".rela__ex_table");
|
|
if (!sec)
|
|
ERROR("missing .rela__ex_table section");
|
|
|
|
list_for_each_entry(rela, &sec->relas, list) {
|
|
if (!strcmp(rela->sym->name, ".fixup") &&
|
|
rela->addend >= src_offset &&
|
|
rela->addend < src_offset + group_size)
|
|
rela->rela.r_addend = rela->addend - (src_offset - dest_offset);
|
|
}
|
|
}
|
|
|
|
static void kpatch_regenerate_special_section(struct kpatch_elf *kelf,
|
|
struct special_section *special,
|
|
struct section *sec)
|
|
{
|
|
struct rela *rela, *safe;
|
|
char *src, *dest;
|
|
unsigned int group_size, src_offset, dest_offset, include;
|
|
int jump_table = !strcmp(special->name, "__jump_table");
|
|
|
|
LIST_HEAD(newrelas);
|
|
|
|
src = sec->base->data->d_buf;
|
|
/* alloc buffer for new base section */
|
|
dest = malloc(sec->base->sh.sh_size);
|
|
if (!dest)
|
|
ERROR("malloc");
|
|
|
|
/* Restore the stashed r_addend from kpatch_update_ex_table_addend. */
|
|
if (!strcmp(special->name, "__ex_table")) {
|
|
list_for_each_entry(rela, &sec->relas, list) {
|
|
if (!strcmp(rela->sym->name, ".fixup"))
|
|
rela->addend = rela->rela.r_addend;
|
|
}
|
|
}
|
|
|
|
src_offset = 0;
|
|
dest_offset = 0;
|
|
for ( ; src_offset < sec->base->sh.sh_size; src_offset += group_size) {
|
|
|
|
group_size = special->group_size(kelf, src_offset);
|
|
|
|
/*
|
|
* In some cases the struct has padding at the end to ensure
|
|
* that all structs after it are properly aligned. But the
|
|
* last struct in the section may not be padded. In that case,
|
|
* shrink the group_size such that it still (hopefully)
|
|
* contains the data but doesn't go past the end of the
|
|
* section.
|
|
*/
|
|
if (src_offset + group_size > sec->base->sh.sh_size)
|
|
group_size = sec->base->sh.sh_size - src_offset;
|
|
|
|
include = should_keep_rela_group(sec, src_offset, group_size);
|
|
|
|
if (!include)
|
|
continue;
|
|
|
|
if (special->unsupported)
|
|
DIFF_FATAL("unsupported reference to special section %s", sec->base->name);
|
|
|
|
/*
|
|
* Jump labels (aka static keys or static branches) aren't
|
|
* actually supported for the time being. Warn on all
|
|
* non-tracepoint jump labels when they occur in a replacement
|
|
* function. An inert tracepoint is harmless enough, but a
|
|
* broken static key can cause unexpected behavior.
|
|
*
|
|
* Here we hard-code knowledge about the contents of the
|
|
* jump_label struct. It has three fields: code, target, and
|
|
* key.
|
|
*/
|
|
if (jump_table) {
|
|
struct rela *code, *key;
|
|
int i = 0;
|
|
|
|
list_for_each_entry(rela, &sec->relas, list) {
|
|
if (rela->offset >= src_offset &&
|
|
rela->offset < src_offset + group_size) {
|
|
if (i == 0)
|
|
code = rela;
|
|
else if (i == 2)
|
|
key = rela;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if (i != 3)
|
|
ERROR("BUG: __jump_table has an unexpected format");
|
|
|
|
/* inert tracepoints are harmless */
|
|
if (!strncmp(key->sym->name, "__tracepoint_", 13))
|
|
continue;
|
|
|
|
/* inert dynamic debug printks are harmless */
|
|
if (is_dynamic_debug_symbol(key->sym))
|
|
continue;
|
|
|
|
ERROR("Found a jump label at %s()+0x%x, using key %s. Jump labels aren't currently supported. Use static_key_enabled() instead.",
|
|
code->sym->name, code->addend, key->sym->name);
|
|
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Copy all relas in the group. It's possible that the relas
|
|
* aren't sorted (e.g. .rela.fixup), so go through the entire
|
|
* rela list each time.
|
|
*/
|
|
list_for_each_entry_safe(rela, safe, &sec->relas, list) {
|
|
if (rela->offset >= src_offset &&
|
|
rela->offset < src_offset + group_size) {
|
|
/* copy rela entry */
|
|
list_del(&rela->list);
|
|
list_add_tail(&rela->list, &newrelas);
|
|
|
|
rela->offset -= src_offset - dest_offset;
|
|
rela->rela.r_offset = rela->offset;
|
|
|
|
rela->sym->include = 1;
|
|
|
|
|
|
if (!strcmp(special->name, ".fixup"))
|
|
kpatch_update_ex_table_addend(kelf, special,
|
|
src_offset,
|
|
dest_offset,
|
|
group_size);
|
|
}
|
|
}
|
|
|
|
/* copy base section group */
|
|
memcpy(dest + dest_offset, src + src_offset, group_size);
|
|
dest_offset += group_size;
|
|
}
|
|
|
|
if (!dest_offset) {
|
|
/* no changed or global functions referenced */
|
|
sec->status = sec->base->status = SAME;
|
|
sec->include = sec->base->include = 0;
|
|
free(dest);
|
|
return;
|
|
}
|
|
|
|
/* overwrite with new relas list */
|
|
list_replace(&newrelas, &sec->relas);
|
|
|
|
/* include both rela and base sections */
|
|
sec->include = 1;
|
|
sec->base->include = 1;
|
|
/* include secsym so .kpatch.arch relas can point to section symbols */
|
|
sec->base->secsym->include = 1;
|
|
|
|
/*
|
|
* Update text section data buf and size.
|
|
*
|
|
* The rela section's data buf and size will be regenerated in
|
|
* kpatch_rebuild_rela_section_data().
|
|
*/
|
|
sec->base->data->d_buf = dest;
|
|
sec->base->data->d_size = dest_offset;
|
|
}
|
|
|
|
#define ORC_IP_PTR_SIZE 4
|
|
|
|
/*
|
|
* This function is similar to kpatch_regenerate_special_section(), but
|
|
* customized for the ORC-related sections. ORC is more special than the other
|
|
* special sections because each ORC entry is split into .orc_unwind (struct
|
|
* orc_entry) and .orc_unwind_ip.
|
|
*/
|
|
static void kpatch_regenerate_orc_sections(struct kpatch_elf *kelf)
|
|
{
|
|
struct rela *rela, *safe;
|
|
char *src, *dest, *str;
|
|
unsigned int src_idx = 0, dest_idx = 0, orc_entry_size;
|
|
struct section *orc_sec, *ip_sec;
|
|
|
|
|
|
str = getenv("ORC_STRUCT_SIZE");
|
|
if (!str)
|
|
return;
|
|
orc_entry_size = atoi(str);
|
|
|
|
if (!orc_entry_size)
|
|
ERROR("bad ORC_STRUCT_SIZE");
|
|
|
|
LIST_HEAD(newrelas);
|
|
|
|
orc_sec = find_section_by_name(&kelf->sections, ".orc_unwind");
|
|
ip_sec = find_section_by_name(&kelf->sections, ".orc_unwind_ip");
|
|
|
|
if (!orc_sec || !ip_sec)
|
|
return;
|
|
|
|
if (orc_sec->sh.sh_size % orc_entry_size != 0)
|
|
ERROR("bad .orc_unwind size");
|
|
|
|
if (ip_sec->sh.sh_size !=
|
|
(orc_sec->sh.sh_size / orc_entry_size) * ORC_IP_PTR_SIZE)
|
|
ERROR(".orc_unwind/.orc_unwind_ip size mismatch");
|
|
|
|
src = orc_sec->data->d_buf;
|
|
dest = malloc(orc_sec->sh.sh_size);
|
|
if (!dest)
|
|
ERROR("malloc");
|
|
|
|
list_for_each_entry_safe(rela, safe, &ip_sec->rela->relas, list) {
|
|
|
|
if (rela->sym->type != STT_FUNC || !rela->sym->sec->include)
|
|
goto next;
|
|
|
|
/* copy orc entry */
|
|
memcpy(dest + (dest_idx * orc_entry_size),
|
|
src + (src_idx * orc_entry_size),
|
|
orc_entry_size);
|
|
|
|
/* move ip rela */
|
|
list_del(&rela->list);
|
|
list_add_tail(&rela->list, &newrelas);
|
|
rela->offset = dest_idx * ORC_IP_PTR_SIZE;
|
|
rela->sym->include = 1;
|
|
|
|
dest_idx++;
|
|
next:
|
|
src_idx++;
|
|
}
|
|
|
|
if (!dest_idx) {
|
|
/* no changed or global functions referenced */
|
|
orc_sec->status = ip_sec->status = ip_sec->rela->status = SAME;
|
|
orc_sec->include = ip_sec->include = ip_sec->rela->include = 0;
|
|
free(dest);
|
|
return;
|
|
}
|
|
|
|
/* overwrite with new relas list */
|
|
list_replace(&newrelas, &ip_sec->rela->relas);
|
|
|
|
/* include the sections */
|
|
orc_sec->include = ip_sec->include = ip_sec->rela->include = 1;
|
|
|
|
/*
|
|
* Update data buf/size.
|
|
*
|
|
* The ip section can keep its old (zeroed data), though its size has
|
|
* possibly decreased. The ip rela section's data buf and size will be
|
|
* regenerated in kpatch_rebuild_rela_section_data().
|
|
*/
|
|
orc_sec->data->d_buf = dest;
|
|
orc_sec->data->d_size = dest_idx * orc_entry_size;
|
|
ip_sec->data->d_size = dest_idx * ORC_IP_PTR_SIZE;
|
|
}
|
|
|
|
static void kpatch_check_relocations(struct kpatch_elf *kelf)
|
|
{
|
|
struct rela *rela;
|
|
struct section *sec;
|
|
Elf_Data *sdata;
|
|
|
|
list_for_each_entry(sec, &kelf->sections, list) {
|
|
if (!is_rela_section(sec))
|
|
continue;
|
|
list_for_each_entry(rela, &sec->relas, list) {
|
|
if (rela->sym->sec) {
|
|
sdata = rela->sym->sec->data;
|
|
if (rela->addend > (int)sdata->d_size) {
|
|
ERROR("out-of-range relocation %s+%x in %s", rela->sym->sec->name,
|
|
rela->addend, sec->name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void kpatch_include_debug_sections(struct kpatch_elf *kelf)
|
|
{
|
|
struct section *sec;
|
|
struct rela *rela, *saferela;
|
|
|
|
/* include all .debug_* sections */
|
|
list_for_each_entry(sec, &kelf->sections, list) {
|
|
if (is_debug_section(sec)) {
|
|
sec->include = 1;
|
|
if (!is_rela_section(sec))
|
|
sec->secsym->include = 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Go through the .rela.debug_ sections and strip entries
|
|
* referencing unchanged symbols
|
|
*/
|
|
list_for_each_entry(sec, &kelf->sections, list) {
|
|
if (!is_rela_section(sec) || !is_debug_section(sec))
|
|
continue;
|
|
list_for_each_entry_safe(rela, saferela, &sec->relas, list)
|
|
if (!rela->sym->sec->include)
|
|
list_del(&rela->list);
|
|
}
|
|
}
|
|
|
|
static void kpatch_mark_ignored_sections(struct kpatch_elf *kelf)
|
|
{
|
|
struct section *sec, *strsec, *ignoresec;
|
|
struct rela *rela;
|
|
char *name;
|
|
|
|
/* Ignore any discarded sections */
|
|
list_for_each_entry(sec, &kelf->sections, list) {
|
|
if (!strncmp(sec->name, ".discard", 8) ||
|
|
!strncmp(sec->name, ".rela.discard", 13))
|
|
sec->ignore = 1;
|
|
}
|
|
|
|
sec = find_section_by_name(&kelf->sections, ".kpatch.ignore.sections");
|
|
if (!sec)
|
|
return;
|
|
|
|
list_for_each_entry(rela, &sec->rela->relas, list) {
|
|
strsec = rela->sym->sec;
|
|
strsec->status = CHANGED;
|
|
/*
|
|
* Include the string section here. This is because the
|
|
* KPATCH_IGNORE_SECTION() macro is passed a literal string
|
|
* by the patch author, resulting in a change to the string
|
|
* section. If we don't include it, then we will potentially
|
|
* get a "changed section not included" error in
|
|
* kpatch_verify_patchability() if no other function based change
|
|
* also changes the string section. We could try to exclude each
|
|
* literal string added to the section by KPATCH_IGNORE_SECTION()
|
|
* from the section data comparison, but this is a simpler way.
|
|
*/
|
|
strsec->include = 1;
|
|
strsec->secsym->include = 1;
|
|
name = strsec->data->d_buf + rela->addend;
|
|
ignoresec = find_section_by_name(&kelf->sections, name);
|
|
if (!ignoresec)
|
|
ERROR("KPATCH_IGNORE_SECTION: can't find %s", name);
|
|
log_normal("ignoring section: %s\n", name);
|
|
if (is_rela_section(ignoresec))
|
|
ignoresec = ignoresec->base;
|
|
ignoresec->ignore = 1;
|
|
if (ignoresec->twin)
|
|
ignoresec->twin->ignore = 1;
|
|
}
|
|
}
|
|
|
|
static void kpatch_mark_ignored_sections_same(struct kpatch_elf *kelf)
|
|
{
|
|
struct section *sec;
|
|
struct symbol *sym;
|
|
|
|
list_for_each_entry(sec, &kelf->sections, list) {
|
|
if (!sec->ignore)
|
|
continue;
|
|
sec->status = SAME;
|
|
if (!is_rela_section(sec)) {
|
|
if (sec->secsym)
|
|
sec->secsym->status = SAME;
|
|
if (sec->rela)
|
|
sec->rela->status = SAME;
|
|
}
|
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
if (sym->sec != sec)
|
|
continue;
|
|
sym->status = SAME;
|
|
}
|
|
}
|
|
|
|
/* strip temporary global __UNIQUE_ID_kpatch_ignore_section_* symbols */
|
|
list_for_each_entry(sym, &kelf->symbols, list)
|
|
if (!strncmp(sym->name, "__UNIQUE_ID_kpatch_ignore_section_",
|
|
strlen("__UNIQUE_ID_kpatch_ignore_section_")))
|
|
sym->status = SAME;
|
|
}
|
|
|
|
static void kpatch_mark_ignored_functions_same(struct kpatch_elf *kelf)
|
|
{
|
|
struct section *sec;
|
|
struct symbol *sym;
|
|
struct rela *rela;
|
|
|
|
sec = find_section_by_name(&kelf->sections, ".kpatch.ignore.functions");
|
|
if (!sec)
|
|
return;
|
|
|
|
list_for_each_entry(rela, &sec->rela->relas, list) {
|
|
if (!rela->sym->sec)
|
|
ERROR("expected bundled symbol");
|
|
if (rela->sym->type != STT_FUNC)
|
|
ERROR("expected function symbol");
|
|
log_normal("ignoring function: %s\n", rela->sym->name);
|
|
if (rela->sym->status != CHANGED)
|
|
log_normal("NOTICE: no change detected in function %s, unnecessary KPATCH_IGNORE_FUNCTION()?\n", rela->sym->name);
|
|
rela->sym->status = SAME;
|
|
rela->sym->sec->status = SAME;
|
|
|
|
if (rela->sym->child)
|
|
rela->sym->child->status = SAME;
|
|
|
|
if (rela->sym->sec->secsym)
|
|
rela->sym->sec->secsym->status = SAME;
|
|
if (rela->sym->sec->rela)
|
|
rela->sym->sec->rela->status = SAME;
|
|
}
|
|
|
|
/* strip temporary global kpatch_ignore_func_* symbols */
|
|
list_for_each_entry(sym, &kelf->symbols, list)
|
|
if (!strncmp(sym->name, "__kpatch_ignore_func_",
|
|
strlen("__kpatch_ignore_func_")))
|
|
sym->status = SAME;
|
|
}
|
|
|
|
static void kpatch_create_kpatch_arch_section(struct kpatch_elf *kelf, char *objname)
|
|
{
|
|
struct special_section *special;
|
|
struct symbol *strsym;
|
|
struct section *sec, *karch_sec;
|
|
struct rela *rela;
|
|
int nr, index = 0;
|
|
|
|
nr = sizeof(special_sections) / sizeof(special_sections[0]);
|
|
karch_sec = create_section_pair(kelf, ".kpatch.arch", sizeof(struct kpatch_arch), nr);
|
|
|
|
/* lookup strings symbol */
|
|
strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings");
|
|
if (!strsym)
|
|
ERROR("can't find .kpatch.strings symbol");
|
|
|
|
for (special = special_sections; special->name; special++) {
|
|
if (strcmp(special->name, ".parainstructions") &&
|
|
strcmp(special->name, ".altinstructions"))
|
|
continue;
|
|
|
|
sec = find_section_by_name(&kelf->sections, special->name);
|
|
if (!sec)
|
|
continue;
|
|
|
|
/* entries[index].sec */
|
|
ALLOC_LINK(rela, &karch_sec->rela->relas);
|
|
rela->sym = sec->secsym;
|
|
rela->type = ABSOLUTE_RELA_TYPE;
|
|
rela->addend = 0;
|
|
rela->offset = index * sizeof(struct kpatch_arch) + \
|
|
offsetof(struct kpatch_arch, sec);
|
|
|
|
/* entries[index].objname */
|
|
ALLOC_LINK(rela, &karch_sec->rela->relas);
|
|
rela->sym = strsym;
|
|
rela->type = ABSOLUTE_RELA_TYPE;
|
|
rela->addend = offset_of_string(&kelf->strings, objname);
|
|
rela->offset = index * sizeof(struct kpatch_arch) + \
|
|
offsetof(struct kpatch_arch, objname);
|
|
|
|
index++;
|
|
}
|
|
|
|
karch_sec->data->d_size = index * sizeof(struct kpatch_arch);
|
|
karch_sec->sh.sh_size = karch_sec->data->d_size;
|
|
}
|
|
|
|
static void kpatch_process_special_sections(struct kpatch_elf *kelf)
|
|
{
|
|
struct special_section *special;
|
|
struct section *sec;
|
|
struct symbol *sym;
|
|
struct rela *rela;
|
|
int altinstr = 0;
|
|
|
|
for (special = special_sections; special->name; special++) {
|
|
sec = find_section_by_name(&kelf->sections, special->name);
|
|
if (!sec)
|
|
continue;
|
|
|
|
sec = sec->rela;
|
|
if (!sec)
|
|
continue;
|
|
|
|
kpatch_regenerate_special_section(kelf, special, sec);
|
|
|
|
if (!strcmp(special->name, ".altinstructions") && sec->base->include)
|
|
altinstr = 1;
|
|
}
|
|
|
|
/*
|
|
* The following special sections don't have relas which reference
|
|
* non-included symbols, so their entire rela section can be included.
|
|
*/
|
|
list_for_each_entry(sec, &kelf->sections, list) {
|
|
if (strcmp(sec->name, ".altinstr_replacement"))
|
|
continue;
|
|
/*
|
|
* Only include .altinstr_replacement if .altinstructions
|
|
* is also included.
|
|
*/
|
|
if (!altinstr)
|
|
break;
|
|
|
|
/* include base section */
|
|
sec->include = 1;
|
|
|
|
/* include all symbols in the section */
|
|
list_for_each_entry(sym, &kelf->symbols, list)
|
|
if (sym->sec == sec)
|
|
sym->include = 1;
|
|
|
|
/* include rela section */
|
|
if (sec->rela) {
|
|
sec->rela->include = 1;
|
|
/* include all symbols referenced by relas */
|
|
list_for_each_entry(rela, &sec->rela->relas, list)
|
|
kpatch_include_symbol(rela->sym);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The following special sections aren't supported, so make sure we
|
|
* don't ever try to include them. Otherwise the kernel will see the
|
|
* jump table during module loading and get confused. Generally it
|
|
* should be safe to exclude them, it just means that you can't modify
|
|
* jump labels and enable tracepoints in a patched function.
|
|
*/
|
|
list_for_each_entry(sec, &kelf->sections, list) {
|
|
if (strcmp(sec->name, "__jump_table") &&
|
|
strcmp(sec->name, "__tracepoints") &&
|
|
strcmp(sec->name, "__tracepoints_ptrs") &&
|
|
strcmp(sec->name, "__tracepoints_strings"))
|
|
continue;
|
|
|
|
sec->status = SAME;
|
|
sec->include = 0;
|
|
if (sec->rela) {
|
|
sec->rela->status = SAME;
|
|
sec->rela->include = 0;
|
|
}
|
|
}
|
|
|
|
kpatch_regenerate_orc_sections(kelf);
|
|
}
|
|
|
|
static struct sym_compare_type *kpatch_elf_locals(struct kpatch_elf *kelf)
|
|
{
|
|
struct symbol *sym;
|
|
int i = 0, sym_num = 0;
|
|
struct sym_compare_type *sym_array;
|
|
|
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
if (sym->bind != STB_LOCAL)
|
|
continue;
|
|
if (sym->type != STT_FUNC && sym->type != STT_OBJECT)
|
|
continue;
|
|
|
|
sym_num++;
|
|
}
|
|
|
|
if (!sym_num)
|
|
return NULL;
|
|
|
|
sym_array = malloc((sym_num + 1) * sizeof(struct sym_compare_type));
|
|
if (!sym_array)
|
|
ERROR("malloc");
|
|
|
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
if (sym->bind != STB_LOCAL)
|
|
continue;
|
|
if (sym->type != STT_FUNC && sym->type != STT_OBJECT)
|
|
continue;
|
|
|
|
sym_array[i].type = sym->type;
|
|
sym_array[i++].name = strdup(sym->name);
|
|
}
|
|
sym_array[i].type = 0;
|
|
sym_array[i].name = NULL;
|
|
|
|
return sym_array;
|
|
}
|
|
|
|
static void kpatch_create_patches_sections(struct kpatch_elf *kelf,
|
|
struct lookup_table *table,
|
|
char *objname)
|
|
{
|
|
int nr, index, objname_offset;
|
|
struct section *sec, *relasec;
|
|
struct symbol *sym, *strsym;
|
|
struct rela *rela;
|
|
struct lookup_result result;
|
|
struct kpatch_patch_func *funcs;
|
|
|
|
/* count patched functions */
|
|
nr = 0;
|
|
list_for_each_entry(sym, &kelf->symbols, list)
|
|
if (sym->type == STT_FUNC && sym->status == CHANGED && !sym->parent)
|
|
nr++;
|
|
|
|
/* create text/rela section pair */
|
|
sec = create_section_pair(kelf, ".kpatch.funcs", sizeof(*funcs), nr);
|
|
relasec = sec->rela;
|
|
funcs = sec->data->d_buf;
|
|
|
|
/* lookup strings symbol */
|
|
strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings");
|
|
if (!strsym)
|
|
ERROR("can't find .kpatch.strings symbol");
|
|
|
|
/* add objname to strings */
|
|
objname_offset = offset_of_string(&kelf->strings, objname);
|
|
|
|
/* populate sections */
|
|
index = 0;
|
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
if (sym->type == STT_FUNC && sym->status == CHANGED && !sym->parent) {
|
|
if (sym->bind == STB_LOCAL) {
|
|
if (lookup_local_symbol(table, sym->name,
|
|
&result))
|
|
ERROR("lookup_local_symbol %s",
|
|
sym->name);
|
|
} else {
|
|
if(lookup_global_symbol(table, sym->name,
|
|
&result))
|
|
ERROR("lookup_global_symbol %s",
|
|
sym->name);
|
|
}
|
|
log_debug("lookup for %s @ 0x%016lx len %lu\n",
|
|
sym->name, result.value, result.size);
|
|
|
|
/*
|
|
* Convert global symbols to local so other objects in
|
|
* the patch module (like the patch callback object's init
|
|
* code) won't link to this function and call it before
|
|
* its relocations have been applied.
|
|
*/
|
|
sym->bind = STB_LOCAL;
|
|
sym->sym.st_info = GELF_ST_INFO(sym->bind, sym->type);
|
|
|
|
/* add entry in text section */
|
|
funcs[index].old_addr = result.value;
|
|
funcs[index].old_size = result.size;
|
|
funcs[index].new_size = sym->sym.st_size;
|
|
funcs[index].sympos = result.pos;
|
|
|
|
/*
|
|
* Add a relocation that will populate
|
|
* the funcs[index].new_addr field at
|
|
* module load time.
|
|
*/
|
|
ALLOC_LINK(rela, &relasec->relas);
|
|
rela->sym = sym;
|
|
rela->type = ABSOLUTE_RELA_TYPE;
|
|
rela->addend = 0;
|
|
rela->offset = index * sizeof(*funcs);
|
|
|
|
/*
|
|
* Add a relocation that will populate
|
|
* the funcs[index].name field.
|
|
*/
|
|
ALLOC_LINK(rela, &relasec->relas);
|
|
rela->sym = strsym;
|
|
rela->type = ABSOLUTE_RELA_TYPE;
|
|
rela->addend = offset_of_string(&kelf->strings, sym->name);
|
|
rela->offset = index * sizeof(*funcs) +
|
|
offsetof(struct kpatch_patch_func, name);
|
|
|
|
/*
|
|
* Add a relocation that will populate
|
|
* the funcs[index].objname field.
|
|
*/
|
|
ALLOC_LINK(rela, &relasec->relas);
|
|
rela->sym = strsym;
|
|
rela->type = ABSOLUTE_RELA_TYPE;
|
|
rela->addend = objname_offset;
|
|
rela->offset = index * sizeof(*funcs) +
|
|
offsetof(struct kpatch_patch_func,objname);
|
|
|
|
index++;
|
|
}
|
|
}
|
|
|
|
/* sanity check, index should equal nr */
|
|
if (index != nr)
|
|
ERROR("size mismatch in funcs sections");
|
|
|
|
}
|
|
|
|
static int kpatch_is_core_module_symbol(char *name)
|
|
{
|
|
return (!strcmp(name, "kpatch_shadow_alloc") ||
|
|
!strcmp(name, "kpatch_shadow_free") ||
|
|
!strcmp(name, "kpatch_shadow_get"));
|
|
}
|
|
|
|
/*
|
|
* If the patched code refers to a symbol, for example, calls a function
|
|
* or stores a pointer to a function somewhere, the address of that symbol
|
|
* must be resolved somehow before the patch is applied. The symbol may be
|
|
* present in the original code too, so the patch may refer either to that
|
|
* version of the symbol (dynrela is used for that) or to its patched
|
|
* version directly (with a normal relocation).
|
|
*
|
|
* Dynrelas may be needed for the symbols not present in this object file
|
|
* (rela->sym->sec is NULL), because it is unknown if the patched versions
|
|
* of these symbols exist and where they are.
|
|
*
|
|
* The patched code can usually refer to a symbol from this object file
|
|
* directly. If it is a function, this may also improve performance because
|
|
* it will not be needed to call the original function first, find the
|
|
* patched one and then use Ftrace to pass control to it.
|
|
*
|
|
* There is an exception though, at least on x86. It is safer to use
|
|
* a dynrela if the patched code stores a pointer to a function somewhere
|
|
* (relocation of type R_X86_64_32S). The function could be used as
|
|
* a callback and some kinds of callbacks are called asynchronously. If
|
|
* the patch module sets such callback and is unloaded shortly after,
|
|
* the kernel could try to call the function via an invalid pointer and
|
|
* would crash. With dynrela, the kernel would call the original function
|
|
* in that case.
|
|
*/
|
|
static int function_ptr_rela(const struct rela *rela)
|
|
{
|
|
const struct rela *rela_toc = toc_rela(rela);
|
|
|
|
return (rela_toc && rela_toc->sym->type == STT_FUNC &&
|
|
!rela_toc->sym->parent &&
|
|
/* skip switch table on PowerPC */
|
|
rela_toc->addend == (int)rela_toc->sym->sym.st_value &&
|
|
(rela->type == R_X86_64_32S ||
|
|
rela->type == R_PPC64_TOC16_HA ||
|
|
rela->type == R_PPC64_TOC16_LO_DS));
|
|
}
|
|
|
|
static int may_need_dynrela(const struct rela *rela)
|
|
{
|
|
/*
|
|
* References to .TOC. are treated specially by the module loader and
|
|
* should never be converted to dynrelas.
|
|
*/
|
|
if (rela->type == R_PPC64_REL16_HA || rela->type == R_PPC64_REL16_LO ||
|
|
rela->type == R_PPC64_REL64)
|
|
return 0;
|
|
|
|
if (!rela->sym->sec)
|
|
return 1;
|
|
|
|
/*
|
|
* Nested functions used as callbacks are a special case.
|
|
* They are not supposed to be visible outside of the
|
|
* function that defines them. Their names may differ in
|
|
* the original and the patched kernels which makes it
|
|
* difficult to use dynrelas. Fortunately, nested functions
|
|
* are rare and are unlikely to be used as asynchronous
|
|
* callbacks, so the patched code can refer to them directly.
|
|
* It seems, one can only distinguish such functions by their
|
|
* names containing a dot. Other kinds of functions with
|
|
* such names (e.g. optimized copies of functions) are
|
|
* unlikely to be used as callbacks.
|
|
*/
|
|
return (function_ptr_rela(rela) &&
|
|
toc_rela(rela)->sym->status != NEW &&
|
|
!strchr(toc_rela(rela)->sym->name, '.'));
|
|
}
|
|
|
|
static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|
struct lookup_table *table,
|
|
char *objname,
|
|
char *pmod_name)
|
|
{
|
|
int nr, index;
|
|
struct section *sec, *ksym_sec, *krela_sec;
|
|
struct rela *rela, *rela2, *safe;
|
|
struct symbol *strsym, *ksym_sec_sym;
|
|
struct kpatch_symbol *ksyms;
|
|
struct kpatch_relocation *krelas;
|
|
struct lookup_result result;
|
|
char *sym_objname;
|
|
int ret, vmlinux, external;
|
|
|
|
vmlinux = !strcmp(objname, "vmlinux");
|
|
|
|
/* count rela entries that need to be dynamic */
|
|
nr = 0;
|
|
list_for_each_entry(sec, &kelf->sections, list) {
|
|
if (!is_rela_section(sec))
|
|
continue;
|
|
if (!strcmp(sec->name, ".rela.kpatch.funcs"))
|
|
continue;
|
|
list_for_each_entry(rela, &sec->relas, list) {
|
|
nr++; /* upper bound on number of kpatch relas and symbols */
|
|
/*
|
|
* Relocation section '.rela.toc' at offset 0xcc6b0 contains 46 entries:
|
|
* ...
|
|
* 0000000000000138 0000002a00000026 R_PPC64_ADDR64 0000000000000000 .text.deferred_put_nlk_sk + 8
|
|
*
|
|
* Relocation section '.rela.text.netlink_release' at offset 0xcadf0 contains 44 entries:
|
|
* ...
|
|
* 0000000000000398 0000007300000032 R_PPC64_TOC16_HA 0000000000000000 .toc + 138
|
|
* 00000000000003a0 0000007300000040 R_PPC64_TOC16_LO_DS 0000000000000000 .toc + 138
|
|
*
|
|
* On PowerPC, may_need_dynrela() should be using rela's reference in .rela.toc for
|
|
* the rela like in the example, where the sym name is .toc + offset. In such case,
|
|
* the checks are performed on both rela and its reference in .rela.toc. Where the
|
|
* rela is checked for rela->type and its corresponding rela in .rela.toc for function
|
|
* pointer/switch label. If rela->need_dynrela needs to be set, it's referenced rela
|
|
* in (.rela.toc)->need_dynrela is set, as they represent the function sym.
|
|
*/
|
|
if (may_need_dynrela(rela))
|
|
toc_rela(rela)->need_dynrela = 1;
|
|
}
|
|
}
|
|
|
|
/* create .kpatch.relocations text/rela section pair */
|
|
krela_sec = create_section_pair(kelf, ".kpatch.relocations", sizeof(*krelas), nr);
|
|
krelas = krela_sec->data->d_buf;
|
|
|
|
/* create .kpatch.symbols text/rela section pair */
|
|
ksym_sec = create_section_pair(kelf, ".kpatch.symbols", sizeof(*ksyms), nr);
|
|
ksyms = ksym_sec->data->d_buf;
|
|
|
|
/* create .kpatch.symbols section symbol (to set rela->sym later) */
|
|
ALLOC_LINK(ksym_sec_sym, &kelf->symbols);
|
|
ksym_sec_sym->sec = ksym_sec;
|
|
ksym_sec_sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION);
|
|
ksym_sec_sym->type = STT_SECTION;
|
|
ksym_sec_sym->bind = STB_LOCAL;
|
|
ksym_sec_sym->name = ".kpatch.symbols";
|
|
|
|
/* lookup strings symbol */
|
|
strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings");
|
|
if (!strsym)
|
|
ERROR("can't find .kpatch.strings symbol");
|
|
|
|
/* populate sections */
|
|
index = 0;
|
|
list_for_each_entry(sec, &kelf->sections, list) {
|
|
if (!is_rela_section(sec))
|
|
continue;
|
|
if (!strcmp(sec->name, ".rela.kpatch.funcs") ||
|
|
!strcmp(sec->name, ".rela.kpatch.relocations") ||
|
|
!strcmp(sec->name, ".rela.kpatch.symbols"))
|
|
continue;
|
|
list_for_each_entry_safe(rela, safe, &sec->relas, list) {
|
|
if (!rela->need_dynrela)
|
|
continue;
|
|
|
|
/*
|
|
* Allow references to core module symbols to remain as
|
|
* normal relas, since the core module may not be
|
|
* compiled into the kernel, and they should be
|
|
* exported anyway.
|
|
*/
|
|
if (kpatch_is_core_module_symbol(rela->sym->name))
|
|
continue;
|
|
|
|
external = 0;
|
|
/*
|
|
* sym_objname is the name of the object to which
|
|
* rela->sym belongs. We'll need this to build
|
|
* ".klp.sym." symbol names later on.
|
|
*
|
|
* By default sym_objname is the name of the
|
|
* component being patched (vmlinux or module).
|
|
* If it's an external symbol, sym_objname
|
|
* will get reassigned appropriately.
|
|
*/
|
|
sym_objname = objname;
|
|
|
|
/*
|
|
* On ppc64le, the function prologue generated by GCC 6
|
|
* has the sequence:
|
|
*
|
|
* .globl my_func
|
|
* .type my_func, @function
|
|
* .quad .TOC.-my_func
|
|
* my_func:
|
|
* .reloc ., R_PPC64_ENTRY ; optional
|
|
* ld r2,-8(r12)
|
|
* add r2,r2,r12
|
|
* .localentry my_func, .-my_func
|
|
*
|
|
* The R_PPC64_ENTRY is optional and its symbol might
|
|
* have an empty name. Leave it as a normal rela.
|
|
*/
|
|
if (rela->type == R_PPC64_ENTRY)
|
|
continue;
|
|
|
|
if (rela->sym->bind == STB_LOCAL) {
|
|
/* An unchanged local symbol */
|
|
ret = lookup_local_symbol(table,
|
|
rela->sym->name, &result);
|
|
if (ret)
|
|
ERROR("lookup_local_symbol %s needed for %s",
|
|
rela->sym->name, sec->base->name);
|
|
|
|
}
|
|
else if (vmlinux) {
|
|
/*
|
|
* We have a patch to vmlinux which references
|
|
* a global symbol. Use a normal rela for
|
|
* exported symbols and a dynrela otherwise.
|
|
*/
|
|
#ifdef __powerpc64__
|
|
/*
|
|
* An exported symbol might be local to an
|
|
* object file and any access to the function
|
|
* might be through localentry (toc+offset)
|
|
* instead of global offset.
|
|
*
|
|
* fs/proc/proc_sysctl::sysctl_head_grab:
|
|
* 166: 0000000000000000 256 FUNC GLOBAL DEFAULT [<localentry>: 8] 42 unregister_sysctl_table
|
|
* 167: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND .TOC.
|
|
*
|
|
* These type of symbols have a type of
|
|
* STT_FUNC. Treat them like local symbols.
|
|
* They will be handled by the livepatch
|
|
* relocation code.
|
|
*/
|
|
if (lookup_is_exported_symbol(table, rela->sym->name)) {
|
|
if (rela->sym->type != STT_FUNC)
|
|
continue;
|
|
}
|
|
#else
|
|
if (lookup_is_exported_symbol(table, rela->sym->name))
|
|
continue;
|
|
#endif
|
|
/*
|
|
* If lookup_global_symbol() fails, assume the
|
|
* symbol is defined in another object in the
|
|
* patch module.
|
|
*/
|
|
if (lookup_global_symbol(table, rela->sym->name,
|
|
&result))
|
|
continue;
|
|
} else {
|
|
/*
|
|
* We have a patch to a module which references
|
|
* a global symbol. Try to find the symbol in
|
|
* the module being patched.
|
|
*/
|
|
if (lookup_global_symbol(table, rela->sym->name,
|
|
&result)) {
|
|
/*
|
|
* Not there, see if the symbol is
|
|
* exported, and set sym_objname to the
|
|
* object the exported symbol belongs
|
|
* to. If it's not exported, assume sym
|
|
* is provided by another .o in the
|
|
* patch module.
|
|
*/
|
|
sym_objname = lookup_exported_symbol_objname(table, rela->sym->name);
|
|
if (!sym_objname)
|
|
sym_objname = pmod_name;
|
|
|
|
/*
|
|
* For a symbol exported by vmlinux, use
|
|
* the original rela.
|
|
*
|
|
* For a symbol exported by a module,
|
|
* convert to a dynrela because the
|
|
* module might not be loaded yet.
|
|
*/
|
|
if (!strcmp(sym_objname, "vmlinux"))
|
|
continue;
|
|
|
|
external = 1;
|
|
}
|
|
}
|
|
log_debug("lookup for %s @ 0x%016lx len %lu\n",
|
|
rela->sym->name, result.value, result.size);
|
|
|
|
/* Fill in ksyms[index] */
|
|
if (vmlinux)
|
|
ksyms[index].src = result.value;
|
|
else
|
|
/* for modules, src is discovered at runtime */
|
|
ksyms[index].src = 0;
|
|
ksyms[index].pos = result.pos;
|
|
ksyms[index].type = rela->sym->type;
|
|
ksyms[index].bind = rela->sym->bind;
|
|
|
|
/* add rela to fill in ksyms[index].name field */
|
|
ALLOC_LINK(rela2, &ksym_sec->rela->relas);
|
|
rela2->sym = strsym;
|
|
rela2->type = ABSOLUTE_RELA_TYPE;
|
|
rela2->addend = offset_of_string(&kelf->strings, rela->sym->name);
|
|
rela2->offset = index * sizeof(*ksyms) + \
|
|
offsetof(struct kpatch_symbol, name);
|
|
|
|
/* add rela to fill in ksyms[index].objname field */
|
|
ALLOC_LINK(rela2, &ksym_sec->rela->relas);
|
|
rela2->sym = strsym;
|
|
rela2->type = ABSOLUTE_RELA_TYPE;
|
|
rela2->addend = offset_of_string(&kelf->strings, sym_objname);
|
|
rela2->offset = index * sizeof(*ksyms) + \
|
|
offsetof(struct kpatch_symbol, objname);
|
|
|
|
/* Fill in krelas[index] */
|
|
if (is_gcc6_localentry_bundled_sym(rela->sym) &&
|
|
rela->addend == (int)rela->sym->sym.st_value)
|
|
rela->addend -= rela->sym->sym.st_value;
|
|
krelas[index].addend = rela->addend;
|
|
krelas[index].type = rela->type;
|
|
krelas[index].external = external;
|
|
|
|
/* add rela to fill in krelas[index].dest field */
|
|
ALLOC_LINK(rela2, &krela_sec->rela->relas);
|
|
if (sec->base->secsym)
|
|
rela2->sym = sec->base->secsym;
|
|
else
|
|
ERROR("can't create dynrela for section %s (symbol %s): no bundled or section symbol",
|
|
sec->name, rela->sym->name);
|
|
|
|
rela2->type = ABSOLUTE_RELA_TYPE;
|
|
rela2->addend = rela->offset;
|
|
rela2->offset = index * sizeof(*krelas) + \
|
|
offsetof(struct kpatch_relocation, dest);
|
|
|
|
/* add rela to fill in krelas[index].objname field */
|
|
ALLOC_LINK(rela2, &krela_sec->rela->relas);
|
|
rela2->sym = strsym;
|
|
rela2->type = ABSOLUTE_RELA_TYPE;
|
|
rela2->addend = offset_of_string(&kelf->strings, objname);
|
|
rela2->offset = index * sizeof(*krelas) + \
|
|
offsetof(struct kpatch_relocation, objname);
|
|
|
|
/* add rela to fill in krelas[index].ksym field */
|
|
ALLOC_LINK(rela2, &krela_sec->rela->relas);
|
|
rela2->sym = ksym_sec_sym;
|
|
rela2->type = ABSOLUTE_RELA_TYPE;
|
|
rela2->addend = index * sizeof(*ksyms);
|
|
rela2->offset = index * sizeof(*krelas) + \
|
|
offsetof(struct kpatch_relocation, ksym);
|
|
|
|
/*
|
|
* Mark the referred to symbol for removal but
|
|
* only if it is not from this object file.
|
|
* The symbols from this object file may be needed
|
|
* later (for example, they may have relocations
|
|
* of their own which should be processed).
|
|
*/
|
|
if (!rela->sym->sec)
|
|
rela->sym->strip = 1;
|
|
list_del(&rela->list);
|
|
free(rela);
|
|
|
|
index++;
|
|
}
|
|
}
|
|
|
|
/* set size to actual number of ksyms/krelas */
|
|
ksym_sec->data->d_size = index * sizeof(struct kpatch_symbol);
|
|
ksym_sec->sh.sh_size = ksym_sec->data->d_size;
|
|
|
|
krela_sec->data->d_size = index * sizeof(struct kpatch_relocation);
|
|
krela_sec->sh.sh_size = krela_sec->data->d_size;
|
|
}
|
|
|
|
static void kpatch_create_callbacks_objname_rela(struct kpatch_elf *kelf, char *objname)
|
|
{
|
|
struct section *sec;
|
|
struct rela *rela;
|
|
struct symbol *strsym;
|
|
int objname_offset;
|
|
|
|
struct callback { char *name; int offset; };
|
|
static struct callback callbacks[] = {
|
|
{ .name = ".rela.kpatch.callbacks.pre_patch",
|
|
.offset = offsetof(struct kpatch_pre_patch_callback, objname) },
|
|
{ .name = ".rela.kpatch.callbacks.post_patch",
|
|
.offset = offsetof(struct kpatch_post_patch_callback, objname) },
|
|
{ .name = ".rela.kpatch.callbacks.pre_unpatch",
|
|
.offset = offsetof(struct kpatch_pre_unpatch_callback, objname) },
|
|
{ .name = ".rela.kpatch.callbacks.post_unpatch",
|
|
.offset = offsetof(struct kpatch_post_patch_callback, objname) },
|
|
{ .name = NULL, .offset = 0 },
|
|
};
|
|
struct callback *callbackp;
|
|
|
|
/* lookup strings symbol */
|
|
strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings");
|
|
if (!strsym)
|
|
ERROR("can't find .kpatch.strings symbol");
|
|
|
|
/* add objname to strings */
|
|
objname_offset = offset_of_string(&kelf->strings, objname);
|
|
|
|
list_for_each_entry(sec, &kelf->sections, list) {
|
|
for (callbackp = callbacks; callbackp->name; callbackp++) {
|
|
if (!strcmp(callbackp->name, sec->name)) {
|
|
ALLOC_LINK(rela, &sec->relas);
|
|
rela->sym = strsym;
|
|
rela->type = ABSOLUTE_RELA_TYPE;
|
|
rela->addend = objname_offset;
|
|
rela->offset = callbackp->offset;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef __powerpc64__
|
|
void kpatch_create_mcount_sections(struct kpatch_elf *kelf) { }
|
|
#else
|
|
/*
|
|
* This function basically reimplements the functionality of the Linux
|
|
* recordmcount script, so that patched functions can be recognized by ftrace.
|
|
*
|
|
* TODO: Eventually we can modify recordmount so that it recognizes our bundled
|
|
* sections as valid and does this work for us.
|
|
*/
|
|
static void kpatch_create_mcount_sections(struct kpatch_elf *kelf)
|
|
{
|
|
int nr, index;
|
|
struct section *sec, *relasec;
|
|
struct symbol *sym;
|
|
struct rela *rela;
|
|
void *newdata;
|
|
unsigned char *insn;
|
|
|
|
nr = 0;
|
|
list_for_each_entry(sym, &kelf->symbols, list)
|
|
if (sym->type == STT_FUNC && sym->status != SAME &&
|
|
sym->has_func_profiling)
|
|
nr++;
|
|
|
|
/* create text/rela section pair */
|
|
sec = create_section_pair(kelf, "__mcount_loc", sizeof(void*), nr);
|
|
relasec = sec->rela;
|
|
|
|
/* populate sections */
|
|
index = 0;
|
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
if (sym->type != STT_FUNC || sym->status == SAME)
|
|
continue;
|
|
|
|
if (!sym->has_func_profiling) {
|
|
log_debug("function %s has no fentry/mcount call, no mcount record is needed\n",
|
|
sym->name);
|
|
continue;
|
|
}
|
|
|
|
/* add rela in .rela__mcount_loc to fill in function pointer */
|
|
ALLOC_LINK(rela, &relasec->relas);
|
|
rela->sym = sym;
|
|
rela->type = R_X86_64_64;
|
|
rela->addend = 0;
|
|
rela->offset = index * sizeof(void*);
|
|
|
|
/*
|
|
* Modify the first instruction of the function to "callq
|
|
* __fentry__" so that ftrace will be happy.
|
|
*/
|
|
newdata = malloc(sym->sec->data->d_size);
|
|
if (!newdata)
|
|
ERROR("malloc");
|
|
|
|
memcpy(newdata, sym->sec->data->d_buf, sym->sec->data->d_size);
|
|
sym->sec->data->d_buf = newdata;
|
|
insn = newdata;
|
|
|
|
rela = list_first_entry(&sym->sec->rela->relas, struct rela,
|
|
list);
|
|
|
|
/*
|
|
* R_X86_64_NONE is only generated by older versions of kernel/gcc
|
|
* which use the mcount script.
|
|
*/
|
|
if (rela->type == R_X86_64_NONE) {
|
|
if (insn[0] != 0xf)
|
|
ERROR("%s: unexpected instruction at the start of the function",
|
|
sym->name);
|
|
insn[0] = 0xe8;
|
|
insn[1] = 0;
|
|
insn[2] = 0;
|
|
insn[3] = 0;
|
|
insn[4] = 0;
|
|
|
|
rela->type = R_X86_64_PC32;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
|
|
/* sanity check, index should equal nr */
|
|
if (index != nr)
|
|
ERROR("size mismatch in funcs sections");
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* This function strips out symbols that were referenced by changed rela
|
|
* sections, but the rela entries that referenced them were converted to
|
|
* dynrelas and are no longer needed.
|
|
*/
|
|
static void kpatch_strip_unneeded_syms(struct kpatch_elf *kelf,
|
|
struct lookup_table *table)
|
|
{
|
|
struct symbol *sym, *safe;
|
|
|
|
list_for_each_entry_safe(sym, safe, &kelf->symbols, list) {
|
|
if (sym->strip) {
|
|
list_del(&sym->list);
|
|
free(sym);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void kpatch_create_strings_elements(struct kpatch_elf *kelf)
|
|
{
|
|
struct section *sec;
|
|
struct symbol *sym;
|
|
|
|
/* create .kpatch.strings */
|
|
|
|
/* allocate section resources */
|
|
ALLOC_LINK(sec, &kelf->sections);
|
|
sec->name = ".kpatch.strings";
|
|
|
|
/* set data */
|
|
sec->data = malloc(sizeof(*sec->data));
|
|
if (!sec->data)
|
|
ERROR("malloc");
|
|
sec->data->d_type = ELF_T_BYTE;
|
|
|
|
/* set section header */
|
|
sec->sh.sh_type = SHT_PROGBITS;
|
|
sec->sh.sh_entsize = 1;
|
|
sec->sh.sh_addralign = 1;
|
|
sec->sh.sh_flags = SHF_ALLOC;
|
|
|
|
/* create .kpatch.strings section symbol (reuse sym variable) */
|
|
|
|
ALLOC_LINK(sym, &kelf->symbols);
|
|
sym->sec = sec;
|
|
sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION);
|
|
sym->type = STT_SECTION;
|
|
sym->bind = STB_LOCAL;
|
|
sym->name = ".kpatch.strings";
|
|
}
|
|
|
|
static void kpatch_build_strings_section_data(struct kpatch_elf *kelf)
|
|
{
|
|
struct string *string;
|
|
struct section *sec;
|
|
int size;
|
|
char *strtab;
|
|
|
|
sec = find_section_by_name(&kelf->sections, ".kpatch.strings");
|
|
if (!sec)
|
|
ERROR("can't find .kpatch.strings");
|
|
|
|
/* determine size */
|
|
size = 0;
|
|
list_for_each_entry(string, &kelf->strings, list)
|
|
size += strlen(string->name) + 1;
|
|
|
|
/* allocate section resources */
|
|
strtab = malloc(size);
|
|
if (!strtab)
|
|
ERROR("malloc");
|
|
sec->data->d_buf = strtab;
|
|
sec->data->d_size = size;
|
|
|
|
/* populate strings section data */
|
|
list_for_each_entry(string, &kelf->strings, list) {
|
|
strcpy(strtab, string->name);
|
|
strtab += strlen(string->name) + 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Don't allow sibling calls from patched functions on ppc64le. Before doing a
|
|
* sibling call, the patched function restores the stack to its caller's stack.
|
|
* The kernel-generated stub then writes the patch module's r2 (toc) value to
|
|
* the caller's stack, corrupting it, eventually causing a panic after it
|
|
* returns to the caller and the caller tries to use the livepatch module's toc
|
|
* value.
|
|
*
|
|
* In theory we could instead a) generate a custom stub, or b) modify the
|
|
* kernel livepatch_handler code to save/restore the stack r2 value, but this
|
|
* is easier for now.
|
|
*/
|
|
static void kpatch_no_sibling_calls_ppc64le(struct kpatch_elf *kelf)
|
|
{
|
|
#ifdef __powerpc64__
|
|
struct symbol *sym;
|
|
unsigned int insn;
|
|
unsigned long offset;
|
|
|
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
if (sym->type != STT_FUNC || sym->status != CHANGED)
|
|
continue;
|
|
|
|
for (offset = 0; offset < sym->sec->data->d_size; offset += 4) {
|
|
|
|
insn = *(unsigned int *)(sym->sec->data->d_buf + offset);
|
|
|
|
/*
|
|
* The instruction 0x48000000 can be assumed to be a
|
|
* sibling call:
|
|
*
|
|
* Bits 0-5 (opcode) == 0x9: unconditional branch
|
|
* Bit 30 (absolute) == 0: relative address
|
|
* Bit 31 (link) == 0: doesn't set LR (not a call)
|
|
*
|
|
* Bits 6-29 (branch address) == zero, which means
|
|
* it's either a branch to self (infinite loop), or
|
|
* there's a REL24 relocation for the address which
|
|
* will be written by the linker or the kernel.
|
|
*/
|
|
if (insn != 0x48000000)
|
|
continue;
|
|
|
|
/* Make sure it's not a branch-to-self: */
|
|
if (!find_rela_by_offset(sym->sec->rela, offset))
|
|
continue;
|
|
|
|
ERROR("Found an unsupported sibling call at %s()+0x%lx. Add __attribute__((optimize(\"-fno-optimize-sibling-calls\"))) to %s() definition.",
|
|
sym->name, sym->sym.st_value + offset, sym->name);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
struct arguments {
|
|
char *args[7];
|
|
int debug;
|
|
};
|
|
|
|
static char args_doc[] = "original.o patched.o parent-name parent-symtab Module.symvers patch-module-name output.o";
|
|
|
|
static struct argp_option options[] = {
|
|
{"debug", 'd', NULL, 0, "Show debug output" },
|
|
{ NULL }
|
|
};
|
|
|
|
static error_t parse_opt (int key, char *arg, struct argp_state *state)
|
|
{
|
|
/* Get the input argument from argp_parse, which we
|
|
know is a pointer to our arguments structure. */
|
|
struct arguments *arguments = state->input;
|
|
|
|
switch (key)
|
|
{
|
|
case 'd':
|
|
arguments->debug = 1;
|
|
break;
|
|
case ARGP_KEY_ARG:
|
|
if (state->arg_num >= 7)
|
|
/* Too many arguments. */
|
|
argp_usage (state);
|
|
arguments->args[state->arg_num] = arg;
|
|
break;
|
|
case ARGP_KEY_END:
|
|
if (state->arg_num < 7)
|
|
/* Not enough arguments. */
|
|
argp_usage (state);
|
|
break;
|
|
default:
|
|
return ARGP_ERR_UNKNOWN;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct argp argp = { options, parse_opt, args_doc, NULL };
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct kpatch_elf *kelf_base, *kelf_patched, *kelf_out;
|
|
struct arguments arguments;
|
|
int num_changed, callbacks_exist, new_globals_exist;
|
|
struct lookup_table *lookup;
|
|
struct section *sec, *symtab;
|
|
struct symbol *sym;
|
|
char *hint = NULL, *orig_obj, *patched_obj, *parent_name;
|
|
char *parent_symtab, *mod_symvers, *patch_name, *output_obj;
|
|
struct sym_compare_type *base_locals, *sym_comp;
|
|
|
|
arguments.debug = 0;
|
|
argp_parse (&argp, argc, argv, 0, NULL, &arguments);
|
|
if (arguments.debug)
|
|
loglevel = DEBUG;
|
|
|
|
elf_version(EV_CURRENT);
|
|
|
|
orig_obj = arguments.args[0];
|
|
patched_obj = arguments.args[1];
|
|
parent_name = arguments.args[2];
|
|
parent_symtab = arguments.args[3];
|
|
mod_symvers = arguments.args[4];
|
|
patch_name = arguments.args[5];
|
|
output_obj = arguments.args[6];
|
|
|
|
childobj = basename(orig_obj);
|
|
|
|
kelf_base = kpatch_elf_open(orig_obj);
|
|
kelf_patched = kpatch_elf_open(patched_obj);
|
|
|
|
kpatch_compare_elf_headers(kelf_base->elf, kelf_patched->elf);
|
|
kpatch_check_program_headers(kelf_base->elf);
|
|
kpatch_check_program_headers(kelf_patched->elf);
|
|
|
|
kpatch_bundle_symbols(kelf_base);
|
|
kpatch_bundle_symbols(kelf_patched);
|
|
|
|
kpatch_detect_child_functions(kelf_base);
|
|
kpatch_detect_child_functions(kelf_patched);
|
|
|
|
list_for_each_entry(sym, &kelf_base->symbols, list) {
|
|
if (sym->type == STT_FILE) {
|
|
hint = strdup(sym->name);
|
|
break;
|
|
}
|
|
}
|
|
if (!hint) {
|
|
log_normal("WARNING: FILE symbol not found in base. Stripped object file or assembly source?\n");
|
|
return EXIT_STATUS_NO_CHANGE;
|
|
}
|
|
|
|
base_locals = kpatch_elf_locals(kelf_base);
|
|
|
|
kpatch_mark_grouped_sections(kelf_patched);
|
|
kpatch_replace_sections_syms(kelf_base);
|
|
kpatch_replace_sections_syms(kelf_patched);
|
|
kpatch_rename_mangled_functions(kelf_base, kelf_patched);
|
|
|
|
kpatch_correlate_elfs(kelf_base, kelf_patched);
|
|
kpatch_correlate_static_local_variables(kelf_base, kelf_patched);
|
|
|
|
/*
|
|
* After this point, we don't care about kelf_base anymore.
|
|
* We access its sections via the twin pointers in the
|
|
* section, symbol, and rela lists of kelf_patched.
|
|
*/
|
|
kpatch_mark_ignored_sections(kelf_patched);
|
|
kpatch_compare_correlated_elements(kelf_patched);
|
|
kpatch_check_func_profiling_calls(kelf_patched);
|
|
kpatch_elf_teardown(kelf_base);
|
|
kpatch_elf_free(kelf_base);
|
|
|
|
kpatch_mark_ignored_functions_same(kelf_patched);
|
|
kpatch_mark_ignored_sections_same(kelf_patched);
|
|
|
|
kpatch_include_standard_elements(kelf_patched);
|
|
num_changed = kpatch_include_changed_functions(kelf_patched);
|
|
kpatch_include_debug_sections(kelf_patched);
|
|
callbacks_exist = kpatch_include_callback_elements(kelf_patched);
|
|
kpatch_include_force_elements(kelf_patched);
|
|
new_globals_exist = kpatch_include_new_globals(kelf_patched);
|
|
|
|
kpatch_process_special_sections(kelf_patched);
|
|
|
|
kpatch_print_changes(kelf_patched);
|
|
kpatch_dump_kelf(kelf_patched);
|
|
|
|
kpatch_verify_patchability(kelf_patched);
|
|
|
|
if (!num_changed && !new_globals_exist) {
|
|
if (callbacks_exist)
|
|
log_debug("no changed functions were found, but callbacks exist\n");
|
|
else {
|
|
log_debug("no changed functions were found\n");
|
|
free(hint);
|
|
return EXIT_STATUS_NO_CHANGE;
|
|
}
|
|
}
|
|
|
|
/* this is destructive to kelf_patched */
|
|
kpatch_migrate_included_elements(kelf_patched, &kelf_out);
|
|
|
|
/*
|
|
* Teardown kelf_patched since we shouldn't access sections or symbols
|
|
* through it anymore. Don't free however, since our section and symbol
|
|
* name fields still point to strings in the Elf object owned by
|
|
* kpatch_patched.
|
|
*/
|
|
kpatch_elf_teardown(kelf_patched);
|
|
|
|
/* create symbol lookup table */
|
|
lookup = lookup_open(parent_symtab, mod_symvers, hint, base_locals);
|
|
for (sym_comp = base_locals; sym_comp && sym_comp->name; sym_comp++) {
|
|
free(sym_comp->name);
|
|
}
|
|
free(base_locals);
|
|
free(hint);
|
|
|
|
kpatch_no_sibling_calls_ppc64le(kelf_out);
|
|
|
|
/* create strings, patches, and dynrelas sections */
|
|
kpatch_create_strings_elements(kelf_out);
|
|
kpatch_create_patches_sections(kelf_out, lookup, parent_name);
|
|
kpatch_create_intermediate_sections(kelf_out, lookup, parent_name, patch_name);
|
|
kpatch_create_kpatch_arch_section(kelf_out, parent_name);
|
|
kpatch_create_callbacks_objname_rela(kelf_out, parent_name);
|
|
kpatch_build_strings_section_data(kelf_out);
|
|
|
|
kpatch_create_mcount_sections(kelf_out);
|
|
|
|
/*
|
|
* At this point, the set of output sections and symbols is
|
|
* finalized. Reorder the symbols into linker-compliant
|
|
* order and index all the symbols and sections. After the
|
|
* indexes have been established, update index data
|
|
* throughout the structure.
|
|
*/
|
|
kpatch_reorder_symbols(kelf_out);
|
|
kpatch_strip_unneeded_syms(kelf_out, lookup);
|
|
kpatch_reindex_elements(kelf_out);
|
|
|
|
/*
|
|
* Update rela section headers and rebuild the rela section data
|
|
* buffers from the relas lists.
|
|
*/
|
|
symtab = find_section_by_name(&kelf_out->sections, ".symtab");
|
|
if (!symtab)
|
|
ERROR("missing .symtab section");
|
|
|
|
list_for_each_entry(sec, &kelf_out->sections, list) {
|
|
if (!is_rela_section(sec))
|
|
continue;
|
|
sec->sh.sh_link = symtab->index;
|
|
sec->sh.sh_info = sec->base->index;
|
|
kpatch_rebuild_rela_section_data(sec);
|
|
}
|
|
kpatch_check_relocations(kelf_out);
|
|
|
|
kpatch_create_shstrtab(kelf_out);
|
|
kpatch_create_strtab(kelf_out);
|
|
kpatch_create_symtab(kelf_out);
|
|
kpatch_dump_kelf(kelf_out);
|
|
kpatch_write_output_elf(kelf_out, kelf_patched->elf, output_obj);
|
|
|
|
lookup_close(lookup);
|
|
kpatch_elf_free(kelf_patched);
|
|
kpatch_elf_teardown(kelf_out);
|
|
kpatch_elf_free(kelf_out);
|
|
|
|
return EXIT_STATUS_SUCCESS;
|
|
}
|