|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* create-diff-object.c
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
|
|
Packit Service |
ac8aad |
* Copyright (C) 2013-2014 Josh Poimboeuf <jpoimboe@redhat.com>
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* This program is free software; you can redistribute it and/or
|
|
Packit Service |
ac8aad |
* modify it under the terms of the GNU General Public License
|
|
Packit Service |
ac8aad |
* as published by the Free Software Foundation; either version 2
|
|
Packit Service |
ac8aad |
* of the License, or (at your option) any later version.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* This program is distributed in the hope that it will be useful,
|
|
Packit Service |
ac8aad |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
ac8aad |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
ac8aad |
* GNU General Public License for more details.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* You should have received a copy of the GNU General Public License
|
|
Packit Service |
ac8aad |
* along with this program; if not, write to the Free Software
|
|
Packit Service |
ac8aad |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA,
|
|
Packit Service |
ac8aad |
* 02110-1301, USA.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* This file contains the heart of the ELF object differencing engine.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* The tool takes two ELF objects from two versions of the same source
|
|
Packit Service |
ac8aad |
* file; a "base" object and a "patched" object. These object need to have
|
|
Packit Service |
ac8aad |
* been compiled with the -ffunction-sections and -fdata-sections GCC options.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* The tool compares the objects at a section level to determine what
|
|
Packit Service |
ac8aad |
* sections have changed. Once a list of changed sections has been generated,
|
|
Packit Service |
ac8aad |
* various rules are applied to determine any object local sections that
|
|
Packit Service |
ac8aad |
* are dependencies of the changed section and also need to be included in
|
|
Packit Service |
ac8aad |
* the output object.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
#include <sys/types.h>
|
|
Packit Service |
ac8aad |
#include <sys/stat.h>
|
|
Packit Service |
ac8aad |
#include <fcntl.h>
|
|
Packit Service |
ac8aad |
#include <stdlib.h>
|
|
Packit Service |
ac8aad |
#include <stdio.h>
|
|
Packit Service |
ac8aad |
#include <string.h>
|
|
Packit Service |
ac8aad |
#include <error.h>
|
|
Packit Service |
ac8aad |
#include <gelf.h>
|
|
Packit Service |
ac8aad |
#include <argp.h>
|
|
Packit Service |
ac8aad |
#include <libgen.h>
|
|
Packit Service |
ac8aad |
#include <unistd.h>
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
#include "list.h"
|
|
Packit Service |
ac8aad |
#include "lookup.h"
|
|
Packit Service |
ac8aad |
#include "asm/insn.h"
|
|
Packit Service |
ac8aad |
#include "kpatch-patch.h"
|
|
Packit Service |
ac8aad |
#include "kpatch-elf.h"
|
|
Packit Service |
ac8aad |
#include "kpatch-intermediate.h"
|
|
Packit Service |
ac8aad |
#include "kpatch.h"
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
#define DIFF_FATAL(format, ...) \
|
|
Packit Service |
ac8aad |
({ \
|
|
Packit Service |
ac8aad |
fprintf(stderr, "ERROR: %s: " format "\n", childobj, ##__VA_ARGS__); \
|
|
Packit Service |
ac8aad |
error(EXIT_STATUS_DIFF_FATAL, 0, "unreconcilable difference"); \
|
|
Packit Service |
ac8aad |
})
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
#ifdef __powerpc64__
|
|
Packit Service |
ac8aad |
#define ABSOLUTE_RELA_TYPE R_PPC64_ADDR64
|
|
Packit Service |
ac8aad |
#else
|
|
Packit Service |
ac8aad |
#define ABSOLUTE_RELA_TYPE R_X86_64_64
|
|
Packit Service |
ac8aad |
#endif
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
char *childobj;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
enum subsection {
|
|
Packit Service |
da4517 |
SUBSECTION_NORMAL,
|
|
Packit Service |
da4517 |
SUBSECTION_HOT,
|
|
Packit Service |
da4517 |
SUBSECTION_UNLIKELY
|
|
Packit Service |
da4517 |
};
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
enum loglevel loglevel = NORMAL;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
bool KLP_ARCH;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
/*******************
|
|
Packit Service |
ac8aad |
* Data structures
|
|
Packit Service |
ac8aad |
* ****************/
|
|
Packit Service |
ac8aad |
struct special_section {
|
|
Packit Service |
ac8aad |
char *name;
|
|
Packit Service |
ac8aad |
int (*group_size)(struct kpatch_elf *kelf, int offset);
|
|
Packit Service |
ac8aad |
};
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*************
|
|
Packit Service |
ac8aad |
* Functions
|
|
Packit Service |
ac8aad |
* **********/
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static int is_bundleable(struct symbol *sym)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
if (sym->type == STT_FUNC &&
|
|
Packit Service |
ac8aad |
!strncmp(sym->sec->name, ".text.",6) &&
|
|
Packit Service |
ac8aad |
!strcmp(sym->sec->name + 6, sym->name))
|
|
Packit Service |
ac8aad |
return 1;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (sym->type == STT_FUNC &&
|
|
Packit Service |
ac8aad |
!strncmp(sym->sec->name, ".text.unlikely.",15) &&
|
|
Packit Service |
ac8aad |
(!strcmp(sym->sec->name + 15, sym->name) ||
|
|
Packit Service |
ac8aad |
(strstr(sym->name, ".cold.") &&
|
|
Packit Service |
ac8aad |
!strncmp(sym->sec->name + 15, sym->name, strlen(sym->sec->name) - 15))))
|
|
Packit Service |
ac8aad |
return 1;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (sym->type == STT_FUNC &&
|
|
Packit Service |
da4517 |
!strncmp(sym->sec->name, ".text.hot.",10) &&
|
|
Packit Service |
da4517 |
!strcmp(sym->sec->name + 10, sym->name))
|
|
Packit Service |
da4517 |
return 1;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
if (sym->type == STT_OBJECT &&
|
|
Packit Service |
ac8aad |
!strncmp(sym->sec->name, ".data.",6) &&
|
|
Packit Service |
ac8aad |
!strcmp(sym->sec->name + 6, sym->name))
|
|
Packit Service |
ac8aad |
return 1;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (sym->type == STT_OBJECT &&
|
|
Packit Service |
da4517 |
!strncmp(sym->sec->name, ".data.rel.", 10) &&
|
|
Packit Service |
da4517 |
!strcmp(sym->sec->name + 10, sym->name))
|
|
Packit Service |
da4517 |
return 1;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (sym->type == STT_OBJECT &&
|
|
Packit Service |
da4517 |
!strncmp(sym->sec->name, ".data.rel.ro.", 13) &&
|
|
Packit Service |
da4517 |
!strcmp(sym->sec->name + 13, sym->name))
|
|
Packit Service |
da4517 |
return 1;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (sym->type == STT_OBJECT &&
|
|
Packit Service |
ac8aad |
!strncmp(sym->sec->name, ".rodata.",8) &&
|
|
Packit Service |
ac8aad |
!strcmp(sym->sec->name + 8, sym->name))
|
|
Packit Service |
ac8aad |
return 1;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (sym->type == STT_OBJECT &&
|
|
Packit Service |
ac8aad |
!strncmp(sym->sec->name, ".bss.",5) &&
|
|
Packit Service |
ac8aad |
!strcmp(sym->sec->name + 5, sym->name))
|
|
Packit Service |
ac8aad |
return 1;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
#ifdef __powerpc64__
|
|
Packit Service |
ac8aad |
/* Symbol st_others value for powerpc */
|
|
Packit Service |
ac8aad |
#define STO_PPC64_LOCAL_BIT 5
|
|
Packit Service |
ac8aad |
#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT)
|
|
Packit Service |
ac8aad |
#define PPC64_LOCAL_ENTRY_OFFSET(other) \
|
|
Packit Service |
ac8aad |
(((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2)
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* On ppc64le, the function prologue generated by GCC 6+ has the sequence:
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* .globl my_func
|
|
Packit Service |
ac8aad |
* .type my_func, @function
|
|
Packit Service |
ac8aad |
* .quad .TOC.-my_func
|
|
Packit Service |
ac8aad |
* my_func:
|
|
Packit Service |
ac8aad |
* .reloc ., R_PPC64_ENTRY ; optional
|
|
Packit Service |
ac8aad |
* ld r2,-8(r12)
|
|
Packit Service |
ac8aad |
* add r2,r2,r12
|
|
Packit Service |
ac8aad |
* .localentry my_func, .-my_func
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* my_func is the global entry point, which, when called, sets up the TOC.
|
|
Packit Service |
ac8aad |
* .localentry is the local entry point, for calls to the function from within
|
|
Packit Service |
ac8aad |
* the object file. The local entry point is 8 bytes after the global entry
|
|
Packit Service |
ac8aad |
* point.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
static int is_gcc6_localentry_bundled_sym(struct symbol *sym)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
return ((PPC64_LOCAL_ENTRY_OFFSET(sym->sym.st_other) != 0) &&
|
|
Packit Service |
ac8aad |
sym->sym.st_value == 8);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
#else
|
|
Packit Service |
ac8aad |
static int is_gcc6_localentry_bundled_sym(struct symbol *sym)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
#endif
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* On ppc64le, when a function references data, it does so indirectly, via the
|
|
Packit Service |
da4517 |
* .toc section. So there are *two* levels of relas:
|
|
Packit Service |
da4517 |
*
|
|
Packit Service |
da4517 |
* 1) the original function rela, referring to the .toc section; and
|
|
Packit Service |
da4517 |
*
|
|
Packit Service |
da4517 |
* 2) the .toc section rela, referring to the data needed by the function.
|
|
Packit Service |
da4517 |
*
|
|
Packit Service |
da4517 |
* For example:
|
|
Packit Service |
da4517 |
*
|
|
Packit Service |
da4517 |
* Relocation section '.rela.text.netlink_release' at offset 0xcadf0 contains 44 entries:
|
|
Packit Service |
da4517 |
* ...
|
|
Packit Service |
da4517 |
* 0000000000000398 0000007300000032 R_PPC64_TOC16_HA 0000000000000000 .toc + 138
|
|
Packit Service |
da4517 |
* 00000000000003a0 0000007300000040 R_PPC64_TOC16_LO_DS 0000000000000000 .toc + 138
|
|
Packit Service |
da4517 |
*
|
|
Packit Service |
da4517 |
* Relocation section '.rela.toc' at offset 0xcc6b0 contains 46 entries:
|
|
Packit Service |
da4517 |
* ...
|
|
Packit Service |
da4517 |
* 0000000000000138 0000002a00000026 R_PPC64_ADDR64 0000000000000000 .text.deferred_put_nlk_sk + 8
|
|
Packit Service |
da4517 |
*
|
|
Packit Service |
da4517 |
* The below function takes the "first level" rela as input, and, if it refers
|
|
Packit Service |
da4517 |
* to .toc, returns the "second level" rela, which is the one that refers to
|
|
Packit Service |
da4517 |
* the actual data symbol.
|
|
Packit Service |
da4517 |
*
|
|
Packit Service |
da4517 |
* In some rare cases, a .toc entry has constant data, and thus has no
|
|
Packit Service |
da4517 |
* corresponding rela. In that case, NULL is returned.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
ac8aad |
static struct rela *toc_rela(const struct rela *rela)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
if (rela->type != R_PPC64_TOC16_HA &&
|
|
Packit Service |
ac8aad |
rela->type != R_PPC64_TOC16_LO_DS)
|
|
Packit Service |
ac8aad |
return (struct rela *)rela;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
/* Only constants in toc */
|
|
Packit Service |
da4517 |
if (!rela->sym->sec->rela)
|
|
Packit Service |
da4517 |
return NULL;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
/* Will return NULL for .toc constant entries */
|
|
Packit Service |
da4517 |
return find_rela_by_offset(rela->sym->sec->rela,
|
|
Packit Service |
da4517 |
(unsigned int)rela->addend);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* When compiling with -ffunction-sections and -fdata-sections, almost every
|
|
Packit Service |
ac8aad |
* symbol gets its own dedicated section. We call such symbols "bundled"
|
|
Packit Service |
ac8aad |
* symbols. They're indicated by "sym->sec->sym == sym".
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
static void kpatch_bundle_symbols(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct symbol *sym;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
Packit Service |
ac8aad |
if (is_bundleable(sym)) {
|
|
Packit Service |
ac8aad |
if (sym->sym.st_value != 0 &&
|
|
Packit Service |
ac8aad |
!is_gcc6_localentry_bundled_sym(sym)) {
|
|
Packit Service |
ac8aad |
ERROR("symbol %s at offset %lu within section %s, expected 0",
|
|
Packit Service |
ac8aad |
sym->name, sym->sym.st_value,
|
|
Packit Service |
ac8aad |
sym->sec->name);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
sym->sec->sym = sym;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
static struct symbol *kpatch_lookup_parent(struct kpatch_elf *kelf,
|
|
Packit Service |
da4517 |
const char *symname,
|
|
Packit Service |
da4517 |
const char *child_suffix)
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
struct symbol *parent;
|
|
Packit Service |
da4517 |
char *pname;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
pname = strndup(symname, child_suffix - symname);
|
|
Packit Service |
da4517 |
if (!pname)
|
|
Packit Service |
da4517 |
ERROR("strndup");
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
parent = find_symbol_by_name(&kelf->symbols, pname);
|
|
Packit Service |
da4517 |
free(pname);
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
return parent;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* During optimization gcc may move unlikely execution branches into *.cold
|
|
Packit Service |
da4517 |
* subfunctions. Some functions can also be split into multiple *.part
|
|
Packit Service |
da4517 |
* functions.
|
|
Packit Service |
da4517 |
* kpatch_detect_child_functions detects such subfunctions and
|
|
Packit Service |
da4517 |
* crossreferences them with their parent functions through parent/child
|
|
Packit Service |
da4517 |
* pointers.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
static void kpatch_detect_child_functions(struct kpatch_elf *kelf)
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
struct symbol *sym;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
Packit Service |
da4517 |
char *childstr;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (sym->type != STT_FUNC)
|
|
Packit Service |
da4517 |
continue;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
childstr = strstr(sym->name, ".cold.");
|
|
Packit Service |
da4517 |
if (childstr) {
|
|
Packit Service |
da4517 |
sym->parent = kpatch_lookup_parent(kelf, sym->name,
|
|
Packit Service |
da4517 |
childstr);
|
|
Packit Service |
da4517 |
if (!sym->parent)
|
|
Packit Service |
da4517 |
ERROR("failed to find parent function for %s",
|
|
Packit Service |
da4517 |
sym->name);
|
|
Packit Service |
da4517 |
} else {
|
|
Packit Service |
da4517 |
childstr = strstr(sym->name, ".part.");
|
|
Packit Service |
da4517 |
if (!childstr)
|
|
Packit Service |
da4517 |
continue;
|
|
Packit Service |
da4517 |
sym->parent = kpatch_lookup_parent(kelf, sym->name,
|
|
Packit Service |
da4517 |
childstr);
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (sym->parent)
|
|
Packit Service |
da4517 |
list_add_tail(&sym->subfunction_node, &sym->parent->children);
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
static bool is_dynamic_debug_symbol(struct symbol *sym)
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
if (sym->type == STT_OBJECT && !strcmp(sym->sec->name, "__verbose"))
|
|
Packit Service |
da4517 |
return true;
|
|
Packit Service |
da4517 |
if (sym->type == STT_SECTION && !strcmp(sym->name, "__verbose"))
|
|
Packit Service |
da4517 |
return true;
|
|
Packit Service |
da4517 |
return false;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* This function detects whether the given symbol is a "special" static local
|
|
Packit Service |
ac8aad |
* variable (for lack of a better term).
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* Special static local variables should never be correlated and should always
|
|
Packit Service |
ac8aad |
* be included if they are referenced by an included function.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
static int is_special_static(struct symbol *sym)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
static char *prefixes[] = {
|
|
Packit Service |
ac8aad |
"__key.",
|
|
Packit Service |
ac8aad |
"__warned.",
|
|
Packit Service |
ac8aad |
"__func__.",
|
|
Packit Service |
da4517 |
"__FUNCTION__.",
|
|
Packit Service |
ac8aad |
"_rs.",
|
|
Packit Service |
ac8aad |
"CSWTCH.",
|
|
Packit Service |
ac8aad |
NULL,
|
|
Packit Service |
ac8aad |
};
|
|
Packit Service |
ac8aad |
char **prefix;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!sym)
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
/* pr_debug() uses static local variables in the __verbose section */
|
|
Packit Service |
da4517 |
if (is_dynamic_debug_symbol(sym))
|
|
Packit Service |
da4517 |
return 1;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (sym->type == STT_SECTION) {
|
|
Packit Service |
da4517 |
/* make sure section is bundled */
|
|
Packit Service |
ac8aad |
if (!sym->sec->sym)
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* use bundled object/function symbol for matching */
|
|
Packit Service |
ac8aad |
sym = sym->sec->sym;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (sym->type != STT_OBJECT || sym->bind != STB_LOCAL)
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
for (prefix = prefixes; *prefix; prefix++)
|
|
Packit Service |
ac8aad |
if (!strncmp(sym->name, *prefix, strlen(*prefix)))
|
|
Packit Service |
ac8aad |
return 1;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* This is like strcmp, but for gcc-mangled symbols. It skips the comparison
|
|
Packit Service |
ac8aad |
* of any substring which consists of '.' followed by any number of digits.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
static int kpatch_mangled_strcmp(char *s1, char *s2)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* ELF string sections aren't mangled, though they look that way. Just
|
|
Packit Service |
da4517 |
* compare them normally.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
if (strstr(s1, ".str1."))
|
|
Packit Service |
da4517 |
return strcmp(s1, s2);
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
while (*s1 == *s2) {
|
|
Packit Service |
ac8aad |
if (!*s1)
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
if (*s1 == '.' && isdigit(s1[1])) {
|
|
Packit Service |
ac8aad |
if (!isdigit(s2[1]))
|
|
Packit Service |
ac8aad |
return 1;
|
|
Packit Service |
ac8aad |
while (isdigit(*++s1))
|
|
Packit Service |
ac8aad |
;
|
|
Packit Service |
ac8aad |
while (isdigit(*++s2))
|
|
Packit Service |
ac8aad |
;
|
|
Packit Service |
ac8aad |
} else {
|
|
Packit Service |
ac8aad |
s1++;
|
|
Packit Service |
ac8aad |
s2++;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
return 1;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
static bool rela_equal(struct rela *rela1, struct rela *rela2)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct rela *rela_toc1, *rela_toc2;
|
|
Packit Service |
ac8aad |
unsigned long toc_data1 = 0, toc_data2 = 0; /* = 0 to prevent gcc warning */
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (rela1->type != rela2->type ||
|
|
Packit Service |
ac8aad |
rela1->offset != rela2->offset)
|
|
Packit Service |
da4517 |
return false;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* On x86, .altinstr_aux is used to store temporary code which allows
|
|
Packit Service |
da4517 |
* static_cpu_has() to work before apply_alternatives() has run. This
|
|
Packit Service |
da4517 |
* code is completely inert for modules, because apply_alternatives()
|
|
Packit Service |
da4517 |
* runs during module init, before the module is fully formed. Any
|
|
Packit Service |
da4517 |
* changed references to it (i.e. changed addend) can be ignored. As
|
|
Packit Service |
da4517 |
* long as they're both references to .altinstr_aux, they can be
|
|
Packit Service |
da4517 |
* considered equal, even if the addends differ.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
if (!strcmp(rela1->sym->name, ".altinstr_aux") &&
|
|
Packit Service |
da4517 |
!strcmp(rela2->sym->name, ".altinstr_aux"))
|
|
Packit Service |
da4517 |
return true;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* With -mcmodel=large on ppc64le, GCC might generate entries in the .toc
|
|
Packit Service |
ac8aad |
* section for relocation symbol references. The .toc offsets may change
|
|
Packit Service |
ac8aad |
* between the original and patched .o, so comparing ".toc + offset" isn't
|
|
Packit Service |
ac8aad |
* right. Compare the .toc-based symbols by reading the corresponding relas
|
|
Packit Service |
ac8aad |
* from the .toc section.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
rela_toc1 = toc_rela(rela1);
|
|
Packit Service |
ac8aad |
if (!rela_toc1) {
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* .toc section entries are mostly place holder for relocation entries, specified
|
|
Packit Service |
ac8aad |
* in .rela.toc section. Sometimes, .toc section may have constants as entries.
|
|
Packit Service |
ac8aad |
* These constants are not reference to any symbols, but plain instructions mostly
|
|
Packit Service |
ac8aad |
* due to some arithmetics in the functions referring them.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* They are referred by the functions like normal .toc entries, these entries can
|
|
Packit Service |
ac8aad |
* not be resolved to any symbols.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* Disassembly of section .toc:
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* 0000000000000000 <.toc>:
|
|
Packit Service |
ac8aad |
* ...
|
|
Packit Service |
ac8aad |
* 148: R_PPC64_ADDR64 .data.capacity_margin
|
|
Packit Service |
ac8aad |
* 150: 0b d7 a3 70 andi. r3,r5,55051
|
|
Packit Service |
ac8aad |
* 154: 3d 0a d7 a3 lhz r30,2621(r23)
|
|
Packit Service |
ac8aad |
* 158: R_PPC64_ADDR64 sched_max_numa_distance
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* Relocation section '.rela.toc' at offset 0xadac0 contains 160 entries:
|
|
Packit Service |
ac8aad |
* Offset Info Type Symbol's Value Symbol's Name + Addend
|
|
Packit Service |
ac8aad |
* ...
|
|
Packit Service |
ac8aad |
* 0000000000000148 0000009100000026 R_PPC64_ADDR64 0000000000000000 .data.capacity_margin + 0
|
|
Packit Service |
ac8aad |
* 0000000000000158 000001a500000026 R_PPC64_ADDR64 0000000000000000 sched_max_numa_distance + 0
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* Relocation section '.rela.text.select_task_rq_fair' at offset 0x90e98 contains 37 entries:
|
|
Packit Service |
ac8aad |
* Offset Info Type Symbol's Value Symbol's Name + Addend
|
|
Packit Service |
ac8aad |
* ...
|
|
Packit Service |
ac8aad |
* 00000000000004a0 0000008800000032 R_PPC64_TOC16_HA 0000000000000000 .toc + 148
|
|
Packit Service |
ac8aad |
* 00000000000004ac 0000008800000040 R_PPC64_TOC16_LO_DS 0000000000000000 .toc + 148
|
|
Packit Service |
ac8aad |
* 0000000000000514 0000008800000032 R_PPC64_TOC16_HA 0000000000000000 .toc + 150
|
|
Packit Service |
ac8aad |
* 000000000000051c 0000008800000040 R_PPC64_TOC16_LO_DS 0000000000000000 .toc + 150
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
memcpy(&toc_data1, rela1->sym->sec->data->d_buf + rela1->addend, sizeof(toc_data1));
|
|
Packit Service |
ac8aad |
if (!toc_data1)
|
|
Packit Service |
da4517 |
ERROR(".toc entry not found %s + %lx", rela1->sym->name, rela1->addend);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
rela_toc2 = toc_rela(rela2);
|
|
Packit Service |
ac8aad |
if (!rela_toc2) {
|
|
Packit Service |
ac8aad |
memcpy(&toc_data2, rela2->sym->sec->data->d_buf + rela2->addend, sizeof(toc_data2));
|
|
Packit Service |
ac8aad |
if (!toc_data2)
|
|
Packit Service |
da4517 |
ERROR(".toc entry not found %s + %lx", rela2->sym->name, rela2->addend);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!rela_toc1 && !rela_toc2)
|
|
Packit Service |
ac8aad |
return toc_data1 == toc_data2;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (!rela_toc1 || !rela_toc2)
|
|
Packit Service |
da4517 |
return false;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
if (rela_toc1->string)
|
|
Packit Service |
ac8aad |
return rela_toc2->string && !strcmp(rela_toc1->string, rela_toc2->string);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (rela_toc1->addend != rela_toc2->addend)
|
|
Packit Service |
da4517 |
return false;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
return !kpatch_mangled_strcmp(rela_toc1->sym->name, rela_toc2->sym->name);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_compare_correlated_rela_section(struct section *sec)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct rela *rela1, *rela2 = NULL;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* On ppc64le, don't compare the .rela.toc section. The .toc and
|
|
Packit Service |
ac8aad |
* .rela.toc sections are included as standard elements.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
if (!strcmp(sec->name, ".rela.toc")) {
|
|
Packit Service |
ac8aad |
sec->status = SAME;
|
|
Packit Service |
ac8aad |
return;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
rela2 = list_entry(sec->twin->relas.next, struct rela, list);
|
|
Packit Service |
ac8aad |
list_for_each_entry(rela1, &sec->relas, list) {
|
|
Packit Service |
ac8aad |
if (rela_equal(rela1, rela2)) {
|
|
Packit Service |
ac8aad |
rela2 = list_entry(rela2->list.next, struct rela, list);
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
sec->status = CHANGED;
|
|
Packit Service |
ac8aad |
return;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
sec->status = SAME;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_compare_correlated_nonrela_section(struct section *sec)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct section *sec1 = sec, *sec2 = sec->twin;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (sec1->sh.sh_type != SHT_NOBITS &&
|
|
Packit Service |
ac8aad |
memcmp(sec1->data->d_buf, sec2->data->d_buf, sec1->data->d_size))
|
|
Packit Service |
ac8aad |
sec->status = CHANGED;
|
|
Packit Service |
ac8aad |
else
|
|
Packit Service |
ac8aad |
sec->status = SAME;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_compare_correlated_section(struct section *sec)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct section *sec1 = sec, *sec2 = sec->twin;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* Compare section headers (must match or fatal) */
|
|
Packit Service |
ac8aad |
if (sec1->sh.sh_type != sec2->sh.sh_type ||
|
|
Packit Service |
ac8aad |
sec1->sh.sh_flags != sec2->sh.sh_flags ||
|
|
Packit Service |
da4517 |
sec1->sh.sh_entsize != sec2->sh.sh_entsize ||
|
|
Packit Service |
da4517 |
(sec1->sh.sh_addralign != sec2->sh.sh_addralign &&
|
|
Packit Service |
da4517 |
!is_text_section(sec1)))
|
|
Packit Service |
da4517 |
DIFF_FATAL("%s section header details differ from %s", sec1->name, sec2->name);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* Short circuit for mcount sections, we rebuild regardless */
|
|
Packit Service |
ac8aad |
if (!strcmp(sec->name, ".rela__mcount_loc") ||
|
|
Packit Service |
ac8aad |
!strcmp(sec->name, "__mcount_loc")) {
|
|
Packit Service |
ac8aad |
sec->status = SAME;
|
|
Packit Service |
ac8aad |
goto out;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (sec1->sh.sh_size != sec2->sh.sh_size ||
|
|
Packit Service |
ac8aad |
sec1->data->d_size != sec2->data->d_size) {
|
|
Packit Service |
ac8aad |
sec->status = CHANGED;
|
|
Packit Service |
ac8aad |
goto out;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (is_rela_section(sec))
|
|
Packit Service |
ac8aad |
kpatch_compare_correlated_rela_section(sec);
|
|
Packit Service |
ac8aad |
else
|
|
Packit Service |
ac8aad |
kpatch_compare_correlated_nonrela_section(sec);
|
|
Packit Service |
ac8aad |
out:
|
|
Packit Service |
ac8aad |
if (sec->status == CHANGED)
|
|
Packit Service |
ac8aad |
log_debug("section %s has changed\n", sec->name);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
#ifdef __x86_64__
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Determine if a section has changed only due to a WARN* or might_sleep
|
|
Packit Service |
ac8aad |
* macro call's embedding of the line number into an instruction operand.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* Warning: Hackery lies herein. It's hopefully justified by the fact that
|
|
Packit Service |
ac8aad |
* this issue is very common.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* Example WARN*:
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* 938: be 70 00 00 00 mov $0x70,%esi
|
|
Packit Service |
ac8aad |
* 93d: 48 c7 c7 00 00 00 00 mov $0x0,%rdi
|
|
Packit Service |
ac8aad |
* 940: R_X86_64_32S .rodata.tcp_conn_request.str1.8+0x88
|
|
Packit Service |
ac8aad |
* 944: c6 05 00 00 00 00 01 movb $0x1,0x0(%rip) # 94b <tcp_conn_request+0x94b>
|
|
Packit Service |
ac8aad |
* 946: R_X86_64_PC32 .data.unlikely-0x1
|
|
Packit Service |
ac8aad |
* 94b: e8 00 00 00 00 callq 950 <tcp_conn_request+0x950>
|
|
Packit Service |
ac8aad |
* 94c: R_X86_64_PC32 warn_slowpath_null-0x4
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* Example might_sleep:
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* 50f: be f7 01 00 00 mov $0x1f7,%esi
|
|
Packit Service |
ac8aad |
* 514: 48 c7 c7 00 00 00 00 mov $0x0,%rdi
|
|
Packit Service |
ac8aad |
* 517: R_X86_64_32S .rodata.do_select.str1.8+0x98
|
|
Packit Service |
ac8aad |
* 51b: e8 00 00 00 00 callq 520 <do_select+0x520>
|
|
Packit Service |
ac8aad |
* 51c: R_X86_64_PC32 ___might_sleep-0x4
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* The pattern which applies to all cases:
|
|
Packit Service |
ac8aad |
* 1) immediate move of the line number to %esi
|
|
Packit Service |
ac8aad |
* 2) (optional) string section rela
|
|
Packit Service |
ac8aad |
* 3) (optional) __warned.xxxxx static local rela
|
|
Packit Service |
ac8aad |
* 4) warn_slowpath_* or __might_sleep or some other similar rela
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
static int kpatch_line_macro_change_only(struct section *sec)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct insn insn1, insn2;
|
|
Packit Service |
ac8aad |
unsigned long start1, start2, size, offset, length;
|
|
Packit Service |
ac8aad |
struct rela *rela;
|
|
Packit Service |
ac8aad |
int lineonly = 0, found;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (sec->status != CHANGED ||
|
|
Packit Service |
ac8aad |
is_rela_section(sec) ||
|
|
Packit Service |
ac8aad |
!is_text_section(sec) ||
|
|
Packit Service |
ac8aad |
sec->sh.sh_size != sec->twin->sh.sh_size ||
|
|
Packit Service |
ac8aad |
!sec->rela ||
|
|
Packit Service |
ac8aad |
sec->rela->status != SAME)
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
start1 = (unsigned long)sec->twin->data->d_buf;
|
|
Packit Service |
ac8aad |
start2 = (unsigned long)sec->data->d_buf;
|
|
Packit Service |
ac8aad |
size = sec->sh.sh_size;
|
|
Packit Service |
ac8aad |
for (offset = 0; offset < size; offset += length) {
|
|
Packit Service |
ac8aad |
insn_init(&insn1, (void *)(start1 + offset), 1);
|
|
Packit Service |
ac8aad |
insn_init(&insn2, (void *)(start2 + offset), 1);
|
|
Packit Service |
ac8aad |
insn_get_length(&insn1);
|
|
Packit Service |
ac8aad |
insn_get_length(&insn2);
|
|
Packit Service |
ac8aad |
length = insn1.length;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!insn1.length || !insn2.length)
|
|
Packit Service |
ac8aad |
ERROR("can't decode instruction in section %s at offset 0x%lx",
|
|
Packit Service |
ac8aad |
sec->name, offset);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (insn1.length != insn2.length)
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!memcmp((void *)start1 + offset, (void *)start2 + offset,
|
|
Packit Service |
ac8aad |
length))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* verify it's a mov immediate to %edx or %esi */
|
|
Packit Service |
ac8aad |
insn_get_opcode(&insn1);
|
|
Packit Service |
ac8aad |
insn_get_opcode(&insn2);
|
|
Packit Service |
ac8aad |
if (!(insn1.opcode.value == 0xba && insn2.opcode.value == 0xba) &&
|
|
Packit Service |
ac8aad |
!(insn1.opcode.value == 0xbe && insn2.opcode.value == 0xbe))
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Verify zero or more string relas followed by a
|
|
Packit Service |
ac8aad |
* warn_slowpath_* or another similar rela.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
found = 0;
|
|
Packit Service |
ac8aad |
list_for_each_entry(rela, &sec->rela->relas, list) {
|
|
Packit Service |
ac8aad |
if (rela->offset < offset + length)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
if (rela->string)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
if (!strncmp(rela->sym->name, "__warned.", 9))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
if (!strncmp(rela->sym->name, "warn_slowpath_", 14) ||
|
|
Packit Service |
ac8aad |
(!strcmp(rela->sym->name, "__warn_printk")) ||
|
|
Packit Service |
ac8aad |
(!strcmp(rela->sym->name, "__might_sleep")) ||
|
|
Packit Service |
ac8aad |
(!strcmp(rela->sym->name, "___might_sleep")) ||
|
|
Packit Service |
ac8aad |
(!strcmp(rela->sym->name, "__might_fault")) ||
|
|
Packit Service |
ac8aad |
(!strcmp(rela->sym->name, "printk")) ||
|
|
Packit Service |
ac8aad |
(!strcmp(rela->sym->name, "lockdep_rcu_suspicious"))) {
|
|
Packit Service |
ac8aad |
found = 1;
|
|
Packit Service |
ac8aad |
break;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
if (!found)
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
lineonly = 1;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!lineonly)
|
|
Packit Service |
ac8aad |
ERROR("no instruction changes detected for changed section %s",
|
|
Packit Service |
ac8aad |
sec->name);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
return 1;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
#elif __powerpc64__
|
|
Packit Service |
ac8aad |
#define PPC_INSTR_LEN 4
|
|
Packit Service |
ac8aad |
#define PPC_RA_OFFSET 16
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static int kpatch_line_macro_change_only(struct section *sec)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
unsigned long start1, start2, size, offset;
|
|
Packit Service |
ac8aad |
unsigned int instr1, instr2;
|
|
Packit Service |
ac8aad |
struct rela *rela;
|
|
Packit Service |
ac8aad |
int lineonly = 0, found;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (sec->status != CHANGED ||
|
|
Packit Service |
ac8aad |
is_rela_section(sec) ||
|
|
Packit Service |
ac8aad |
!is_text_section(sec) ||
|
|
Packit Service |
ac8aad |
sec->sh.sh_size != sec->twin->sh.sh_size ||
|
|
Packit Service |
ac8aad |
!sec->rela ||
|
|
Packit Service |
ac8aad |
sec->rela->status != SAME)
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
start1 = (unsigned long)sec->twin->data->d_buf;
|
|
Packit Service |
ac8aad |
start2 = (unsigned long)sec->data->d_buf;
|
|
Packit Service |
ac8aad |
size = sec->sh.sh_size;
|
|
Packit Service |
ac8aad |
for (offset = 0; offset < size; offset += PPC_INSTR_LEN) {
|
|
Packit Service |
ac8aad |
if (!memcmp((void *)start1 + offset, (void *)start2 + offset,
|
|
Packit Service |
ac8aad |
PPC_INSTR_LEN))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
instr1 = *(unsigned int *)(start1 + offset) >> PPC_RA_OFFSET;
|
|
Packit Service |
ac8aad |
instr2 = *(unsigned int *)(start2 + offset) >> PPC_RA_OFFSET;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* verify it's a load immediate to r5 */
|
|
Packit Service |
ac8aad |
if (!(instr1 == 0x38a0 && instr2 == 0x38a0))
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
found = 0;
|
|
Packit Service |
ac8aad |
list_for_each_entry(rela, &sec->rela->relas, list) {
|
|
Packit Service |
ac8aad |
if (rela->offset < offset + PPC_INSTR_LEN)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
if (toc_rela(rela) && toc_rela(rela)->string)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
if (!strncmp(rela->sym->name, "__warned.", 9))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
if (!strncmp(rela->sym->name, "warn_slowpath_", 14) ||
|
|
Packit Service |
ac8aad |
(!strcmp(rela->sym->name, "__warn_printk")) ||
|
|
Packit Service |
ac8aad |
(!strcmp(rela->sym->name, "__might_sleep")) ||
|
|
Packit Service |
ac8aad |
(!strcmp(rela->sym->name, "___might_sleep")) ||
|
|
Packit Service |
ac8aad |
(!strcmp(rela->sym->name, "__might_fault")) ||
|
|
Packit Service |
ac8aad |
(!strcmp(rela->sym->name, "printk")) ||
|
|
Packit Service |
ac8aad |
(!strcmp(rela->sym->name, "lockdep_rcu_suspicious"))) {
|
|
Packit Service |
ac8aad |
found = 1;
|
|
Packit Service |
ac8aad |
break;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
if (!found)
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
lineonly = 1;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!lineonly)
|
|
Packit Service |
ac8aad |
ERROR("no instruction changes detected for changed section %s",
|
|
Packit Service |
ac8aad |
sec->name);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
return 1;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
#else
|
|
Packit Service |
ac8aad |
static int kpatch_line_macro_change_only(struct section *sec)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
#endif
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* Child functions with "*.cold" names don't have _fentry_ calls, but "*.part",
|
|
Packit Service |
da4517 |
* often do. In the later case, it is not necessary to include the parent
|
|
Packit Service |
da4517 |
* in the output object when the child function has changed.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
static bool kpatch_changed_child_needs_parent_profiling(struct symbol *sym)
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
struct symbol *child;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
list_for_each_entry(child, &sym->children, subfunction_node) {
|
|
Packit Service |
da4517 |
if (child->has_func_profiling)
|
|
Packit Service |
da4517 |
continue;
|
|
Packit Service |
da4517 |
if (child->sec->status == CHANGED ||
|
|
Packit Service |
da4517 |
kpatch_changed_child_needs_parent_profiling(child))
|
|
Packit Service |
da4517 |
return true;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
return false;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
static void kpatch_compare_sections(struct list_head *seclist)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct section *sec;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* compare all sections */
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, seclist, list) {
|
|
Packit Service |
ac8aad |
if (sec->twin)
|
|
Packit Service |
ac8aad |
kpatch_compare_correlated_section(sec);
|
|
Packit Service |
ac8aad |
else
|
|
Packit Service |
ac8aad |
sec->status = NEW;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* exclude WARN-only, might_sleep changes */
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, seclist, list) {
|
|
Packit Service |
ac8aad |
if (kpatch_line_macro_change_only(sec)) {
|
|
Packit Service |
ac8aad |
log_debug("reverting macro / line number section %s status to SAME\n",
|
|
Packit Service |
ac8aad |
sec->name);
|
|
Packit Service |
ac8aad |
sec->status = SAME;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* sync symbol status */
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, seclist, list) {
|
|
Packit Service |
ac8aad |
if (is_rela_section(sec)) {
|
|
Packit Service |
ac8aad |
if (sec->base->sym && sec->base->sym->status != CHANGED)
|
|
Packit Service |
ac8aad |
sec->base->sym->status = sec->status;
|
|
Packit Service |
ac8aad |
} else {
|
|
Packit Service |
da4517 |
struct symbol *sym = sec->sym;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (sym && sym->status != CHANGED)
|
|
Packit Service |
da4517 |
sym->status = sec->status;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (sym && sym->status == SAME &&
|
|
Packit Service |
da4517 |
kpatch_changed_child_needs_parent_profiling(sym))
|
|
Packit Service |
da4517 |
sym->status = CHANGED;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
static enum subsection kpatch_subsection_type(struct section *sec)
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
if (!strncmp(sec->name, ".text.unlikely.", 15))
|
|
Packit Service |
da4517 |
return SUBSECTION_UNLIKELY;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (!strncmp(sec->name, ".text.hot.", 10))
|
|
Packit Service |
da4517 |
return SUBSECTION_HOT;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
return SUBSECTION_NORMAL;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
static int kpatch_subsection_changed(struct section *sec1, struct section *sec2)
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
return kpatch_subsection_type(sec1) != kpatch_subsection_type(sec2);
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
static struct symbol *kpatch_get_correlated_parent(struct symbol *sym)
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
while (sym->parent && !sym->parent->twin)
|
|
Packit Service |
da4517 |
sym = sym->parent;
|
|
Packit Service |
da4517 |
return sym->parent;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
static void kpatch_compare_correlated_symbol(struct symbol *sym)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct symbol *sym1 = sym, *sym2 = sym->twin;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (sym1->sym.st_info != sym2->sym.st_info ||
|
|
Packit Service |
ac8aad |
(sym1->sec && !sym2->sec) ||
|
|
Packit Service |
ac8aad |
(sym2->sec && !sym1->sec))
|
|
Packit Service |
ac8aad |
DIFF_FATAL("symbol info mismatch: %s", sym1->name);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* If two symbols are correlated but their sections are not, then the
|
|
Packit Service |
ac8aad |
* symbol has changed sections. This is only allowed if the symbol is
|
|
Packit Service |
da4517 |
* moving out of an ignored section, or moving between normal/hot/unlikely
|
|
Packit Service |
da4517 |
* subsections.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
if (sym1->sec && sym2->sec && sym1->sec->twin != sym2->sec) {
|
|
Packit Service |
da4517 |
if ((sym2->sec->twin && sym2->sec->twin->ignore) ||
|
|
Packit Service |
da4517 |
kpatch_subsection_changed(sym1->sec, sym2->sec))
|
|
Packit Service |
ac8aad |
sym->status = CHANGED;
|
|
Packit Service |
ac8aad |
else
|
|
Packit Service |
ac8aad |
DIFF_FATAL("symbol changed sections: %s", sym1->name);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (sym1->type == STT_OBJECT &&
|
|
Packit Service |
ac8aad |
sym1->sym.st_size != sym2->sym.st_size)
|
|
Packit Service |
ac8aad |
DIFF_FATAL("object size mismatch: %s", sym1->name);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (sym1->sym.st_shndx == SHN_UNDEF ||
|
|
Packit Service |
ac8aad |
sym1->sym.st_shndx == SHN_ABS)
|
|
Packit Service |
ac8aad |
sym1->status = SAME;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* The status of LOCAL symbols is dependent on the status of their
|
|
Packit Service |
ac8aad |
* matching section and is set during section comparison.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_compare_symbols(struct list_head *symlist)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct symbol *sym;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym, symlist, list) {
|
|
Packit Service |
ac8aad |
if (sym->twin)
|
|
Packit Service |
ac8aad |
kpatch_compare_correlated_symbol(sym);
|
|
Packit Service |
ac8aad |
else
|
|
Packit Service |
ac8aad |
sym->status = NEW;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
log_debug("symbol %s is %s\n", sym->name, status_str(sym->status));
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
#define CORRELATE_ELEMENT(_e1_, _e2_, kindstr) \
|
|
Packit Service |
da4517 |
do { \
|
|
Packit Service |
da4517 |
typeof(_e1_) e1 = (_e1_); \
|
|
Packit Service |
da4517 |
typeof(_e2_) e2 = (_e2_); \
|
|
Packit Service |
da4517 |
e1->twin = e2; \
|
|
Packit Service |
da4517 |
e2->twin = e1; \
|
|
Packit Service |
da4517 |
/* set initial status, might change */ \
|
|
Packit Service |
da4517 |
e1->status = e2->status = SAME; \
|
|
Packit Service |
da4517 |
if (strcmp(e1->name, e2->name)) { \
|
|
Packit Service |
da4517 |
/* Rename mangled element */ \
|
|
Packit Service |
da4517 |
log_debug("renaming %s %s to %s\n", \
|
|
Packit Service |
da4517 |
kindstr, e2->name, e1->name); \
|
|
Packit Service |
da4517 |
e2->name = strdup(e1->name); \
|
|
Packit Service |
da4517 |
} \
|
|
Packit Service |
da4517 |
} while (0)
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
#define UNCORRELATE_ELEMENT(_elem_) \
|
|
Packit Service |
da4517 |
do { \
|
|
Packit Service |
da4517 |
typeof(_elem_) elem = (_elem_); \
|
|
Packit Service |
da4517 |
elem->twin->twin = NULL; \
|
|
Packit Service |
da4517 |
elem->twin = NULL; \
|
|
Packit Service |
da4517 |
} while (0)
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
static void __kpatch_correlate_section(struct section *sec1, struct section *sec2)
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
CORRELATE_ELEMENT(sec1, sec2, "section");
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
static void kpatch_correlate_symbol(struct symbol *sym1, struct symbol *sym2)
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
CORRELATE_ELEMENT(sym1, sym2, "symbol");
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
static void kpatch_correlate_static_local(struct symbol *sym1, struct symbol *sym2)
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
CORRELATE_ELEMENT(sym1, sym2, "static local");
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
static void kpatch_correlate_section(struct section *sec1, struct section *sec2)
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
__kpatch_correlate_section(sec1, sec2);
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (is_rela_section(sec1)) {
|
|
Packit Service |
da4517 |
__kpatch_correlate_section(sec1->base, sec2->base);
|
|
Packit Service |
da4517 |
sec1 = sec1->base;
|
|
Packit Service |
da4517 |
sec2 = sec2->base;
|
|
Packit Service |
da4517 |
} else if (sec1->rela) {
|
|
Packit Service |
da4517 |
__kpatch_correlate_section(sec1->rela, sec2->rela);
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (sec1->secsym)
|
|
Packit Service |
da4517 |
kpatch_correlate_symbol(sec1->secsym, sec2->secsym);
|
|
Packit Service |
da4517 |
if (sec1->sym)
|
|
Packit Service |
da4517 |
kpatch_correlate_symbol(sec1->sym, sec2->sym);
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
static void kpatch_correlate_sections(struct list_head *seclist1, struct list_head *seclist2)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct section *sec1, *sec2;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec1, seclist1, list) {
|
|
Packit Service |
da4517 |
if (sec1->twin)
|
|
Packit Service |
da4517 |
continue;
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec2, seclist2, list) {
|
|
Packit Service |
da4517 |
if (kpatch_mangled_strcmp(sec1->name, sec2->name) ||
|
|
Packit Service |
da4517 |
sec2->twin)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (is_special_static(is_rela_section(sec1) ?
|
|
Packit Service |
ac8aad |
sec1->base->secsym :
|
|
Packit Service |
ac8aad |
sec1->secsym))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Group sections must match exactly to be correlated.
|
|
Packit Service |
ac8aad |
* Changed group sections are currently not supported.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
if (sec1->sh.sh_type == SHT_GROUP) {
|
|
Packit Service |
ac8aad |
if (sec1->data->d_size != sec2->data->d_size)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
if (memcmp(sec1->data->d_buf, sec2->data->d_buf,
|
|
Packit Service |
ac8aad |
sec1->data->d_size))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
kpatch_correlate_section(sec1, sec2);
|
|
Packit Service |
ac8aad |
break;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_correlate_symbols(struct list_head *symlist1, struct list_head *symlist2)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct symbol *sym1, *sym2;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym1, symlist1, list) {
|
|
Packit Service |
da4517 |
if (sym1->twin)
|
|
Packit Service |
da4517 |
continue;
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym2, symlist2, list) {
|
|
Packit Service |
da4517 |
if (kpatch_mangled_strcmp(sym1->name, sym2->name) ||
|
|
Packit Service |
da4517 |
sym1->type != sym2->type || sym2->twin)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (is_special_static(sym1))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* The .LCx symbols point to strings, usually used for
|
|
Packit Service |
ac8aad |
* the bug table. Don't correlate and compare the
|
|
Packit Service |
ac8aad |
* symbols themselves, because the suffix number might
|
|
Packit Service |
ac8aad |
* change.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* If the symbol is used by the bug table (usual case),
|
|
Packit Service |
ac8aad |
* it may get pulled in by
|
|
Packit Service |
ac8aad |
* kpatch_regenerate_special_section().
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* If the symbol is used outside of the bug table (not
|
|
Packit Service |
ac8aad |
* sure if this actually happens anywhere), any string
|
|
Packit Service |
ac8aad |
* changes will be detected elsewhere in rela_equal().
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
if (sym1->type == STT_NOTYPE &&
|
|
Packit Service |
ac8aad |
!strncmp(sym1->name, ".LC", 3))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* group section symbols must have correlated sections */
|
|
Packit Service |
ac8aad |
if (sym1->sec &&
|
|
Packit Service |
ac8aad |
sym1->sec->sh.sh_type == SHT_GROUP &&
|
|
Packit Service |
ac8aad |
sym1->sec->twin != sym2->sec)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
kpatch_correlate_symbol(sym1, sym2);
|
|
Packit Service |
ac8aad |
break;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_compare_elf_headers(Elf *elf1, Elf *elf2)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
GElf_Ehdr eh1, eh2;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!gelf_getehdr(elf1, &eh1))
|
|
Packit Service |
ac8aad |
ERROR("gelf_getehdr");
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!gelf_getehdr(elf2, &eh2))
|
|
Packit Service |
ac8aad |
ERROR("gelf_getehdr");
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (memcmp(eh1.e_ident, eh2.e_ident, EI_NIDENT) ||
|
|
Packit Service |
ac8aad |
eh1.e_type != eh2.e_type ||
|
|
Packit Service |
ac8aad |
eh1.e_machine != eh2.e_machine ||
|
|
Packit Service |
ac8aad |
eh1.e_version != eh2.e_version ||
|
|
Packit Service |
ac8aad |
eh1.e_entry != eh2.e_entry ||
|
|
Packit Service |
ac8aad |
eh1.e_phoff != eh2.e_phoff ||
|
|
Packit Service |
ac8aad |
eh1.e_flags != eh2.e_flags ||
|
|
Packit Service |
ac8aad |
eh1.e_ehsize != eh2.e_ehsize ||
|
|
Packit Service |
ac8aad |
eh1.e_phentsize != eh2.e_phentsize ||
|
|
Packit Service |
ac8aad |
eh1.e_shentsize != eh2.e_shentsize)
|
|
Packit Service |
ac8aad |
DIFF_FATAL("ELF headers differ");
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_check_program_headers(Elf *elf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
size_t ph_nr;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (elf_getphdrnum(elf, &ph_nr))
|
|
Packit Service |
ac8aad |
ERROR("elf_getphdrnum");
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (ph_nr != 0)
|
|
Packit Service |
ac8aad |
DIFF_FATAL("ELF contains program header");
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_mark_grouped_sections(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct section *groupsec, *sec;
|
|
Packit Service |
ac8aad |
unsigned int *data, *end;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(groupsec, &kelf->sections, list) {
|
|
Packit Service |
ac8aad |
if (groupsec->sh.sh_type != SHT_GROUP)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
data = groupsec->data->d_buf;
|
|
Packit Service |
ac8aad |
end = groupsec->data->d_buf + groupsec->data->d_size;
|
|
Packit Service |
ac8aad |
data++; /* skip first flag word (e.g. GRP_COMDAT) */
|
|
Packit Service |
ac8aad |
while (data < end) {
|
|
Packit Service |
ac8aad |
sec = find_section_by_index(&kelf->sections, *data);
|
|
Packit Service |
ac8aad |
if (!sec)
|
|
Packit Service |
ac8aad |
ERROR("group section not found");
|
|
Packit Service |
ac8aad |
sec->grouped = 1;
|
|
Packit Service |
ac8aad |
log_debug("marking section %s (%d) as grouped\n",
|
|
Packit Service |
ac8aad |
sec->name, sec->index);
|
|
Packit Service |
ac8aad |
data++;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
static char *kpatch_section_function_name(struct section *sec)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
da4517 |
if (is_rela_section(sec))
|
|
Packit Service |
da4517 |
sec = sec->base;
|
|
Packit Service |
da4517 |
return sec->sym ? sec->sym->name : sec->name;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
static struct symbol *kpatch_find_uncorrelated_rela(struct section *rela_sec,
|
|
Packit Service |
da4517 |
struct symbol *sym)
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
struct rela *rela, *rela_toc;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
/* find the patched object's corresponding variable */
|
|
Packit Service |
da4517 |
list_for_each_entry(rela, &rela_sec->relas, list) {
|
|
Packit Service |
da4517 |
struct symbol *patched_sym;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
rela_toc = toc_rela(rela);
|
|
Packit Service |
da4517 |
if (!rela_toc)
|
|
Packit Service |
da4517 |
continue; /* skip toc constants */
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
patched_sym = rela_toc->sym;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (patched_sym->twin)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (sym->type != patched_sym->type ||
|
|
Packit Service |
da4517 |
(sym->type == STT_OBJECT &&
|
|
Packit Service |
da4517 |
sym->sym.st_size != patched_sym->sym.st_size))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (kpatch_mangled_strcmp(patched_sym->name, sym->name))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
return patched_sym;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
return NULL;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
static struct symbol *kpatch_find_static_twin_in_children(struct symbol *parent,
|
|
Packit Service |
da4517 |
struct symbol *sym)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
da4517 |
struct symbol *child;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
list_for_each_entry(child, &parent->children, subfunction_node) {
|
|
Packit Service |
da4517 |
struct symbol *res;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
/* Only look in children whose rela section differ from the parent's */
|
|
Packit Service |
da4517 |
if (child->sec->rela == parent->sec->rela || !child->sec->rela)
|
|
Packit Service |
da4517 |
continue;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
res = kpatch_find_uncorrelated_rela(child->sec->rela, sym);
|
|
Packit Service |
da4517 |
/* Need to go deeper */
|
|
Packit Service |
da4517 |
if (!res)
|
|
Packit Service |
da4517 |
res = kpatch_find_static_twin_in_children(child, sym);
|
|
Packit Service |
da4517 |
if (res != NULL)
|
|
Packit Service |
da4517 |
return res;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
return NULL;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Given a static local variable symbol and a rela section which references it
|
|
Packit Service |
ac8aad |
* in the base object, find a corresponding usage of a similarly named symbol
|
|
Packit Service |
ac8aad |
* in the patched object.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
static struct symbol *kpatch_find_static_twin(struct section *sec,
|
|
Packit Service |
ac8aad |
struct symbol *sym)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
da4517 |
struct symbol *res;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (!sec->twin && sec->base->sym) {
|
|
Packit Service |
da4517 |
struct symbol *parent = NULL;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* The static twin might have been in a .part. symbol in the
|
|
Packit Service |
da4517 |
* original object that got removed in the patched object.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
parent = kpatch_get_correlated_parent(sec->base->sym);
|
|
Packit Service |
da4517 |
if (parent)
|
|
Packit Service |
da4517 |
sec = parent->sec->rela;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (!sec->twin)
|
|
Packit Service |
da4517 |
return NULL;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
res = kpatch_find_uncorrelated_rela(sec->twin, sym);
|
|
Packit Service |
da4517 |
if (res != NULL)
|
|
Packit Service |
da4517 |
return res;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
/* Look if reference might have moved to child functions' sections */
|
|
Packit Service |
da4517 |
if (sec->twin->base->sym)
|
|
Packit Service |
da4517 |
return kpatch_find_static_twin_in_children(sec->twin->base->sym,
|
|
Packit Service |
da4517 |
sym);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
return NULL;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static int kpatch_is_normal_static_local(struct symbol *sym)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
if (sym->type != STT_OBJECT || sym->bind != STB_LOCAL)
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!strchr(sym->name, '.'))
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (is_special_static(sym))
|
|
Packit Service |
ac8aad |
return 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
return 1;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
static struct rela *kpatch_find_static_twin_ref(struct section *rela_sec, struct symbol *sym)
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
struct rela *rela;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
list_for_each_entry(rela, &rela_sec->relas, list) {
|
|
Packit Service |
da4517 |
if (rela->sym == sym->twin)
|
|
Packit Service |
da4517 |
return rela;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
/* Reference to static variable might have moved to child function section */
|
|
Packit Service |
da4517 |
if (rela_sec->base->sym) {
|
|
Packit Service |
da4517 |
struct symbol *parent = rela_sec->base->sym;
|
|
Packit Service |
da4517 |
struct symbol *child;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
list_for_each_entry(child, &parent->children, subfunction_node) {
|
|
Packit Service |
da4517 |
/* Only look in children whose rela section differ from the parent's */
|
|
Packit Service |
da4517 |
if (child->sec->rela == parent->sec->rela ||
|
|
Packit Service |
da4517 |
!child->sec->rela)
|
|
Packit Service |
da4517 |
continue;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
rela = kpatch_find_static_twin_ref(child->sec->rela, sym);
|
|
Packit Service |
da4517 |
if (rela)
|
|
Packit Service |
da4517 |
return rela;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
return NULL;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* gcc renames static local variables by appending a period and a number. For
|
|
Packit Service |
ac8aad |
* example, __foo could be renamed to __foo.31452. Unfortunately this number
|
|
Packit Service |
ac8aad |
* can arbitrarily change. Correlate them by comparing which functions
|
|
Packit Service |
ac8aad |
* reference them, and rename the patched symbols to match the base symbol
|
|
Packit Service |
ac8aad |
* names.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* Some surprising facts about static local variable symbols:
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* - It's possible for multiple functions to use the same
|
|
Packit Service |
ac8aad |
* static local variable if the variable is defined in an
|
|
Packit Service |
ac8aad |
* inlined function.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* - It's also possible for multiple static local variables
|
|
Packit Service |
ac8aad |
* with the same name to be used in the same function if they
|
|
Packit Service |
ac8aad |
* have different scopes. (We have to assume that in such
|
|
Packit Service |
ac8aad |
* cases, the order in which they're referenced remains the
|
|
Packit Service |
ac8aad |
* same between the base and patched objects, as there's no
|
|
Packit Service |
ac8aad |
* other way to distinguish them.)
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* - Static locals are usually referenced by functions, but
|
|
Packit Service |
ac8aad |
* they can occasionally be referenced by data sections as
|
|
Packit Service |
ac8aad |
* well.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
static void kpatch_correlate_static_local_variables(struct kpatch_elf *base,
|
|
Packit Service |
ac8aad |
struct kpatch_elf *patched)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct symbol *sym, *patched_sym;
|
|
Packit Service |
ac8aad |
struct section *sec;
|
|
Packit Service |
da4517 |
struct rela *rela;
|
|
Packit Service |
da4517 |
int bundled, patched_bundled;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* First undo the correlations for all static locals. Two static
|
|
Packit Service |
ac8aad |
* locals can have the same numbered suffix in the base and patched
|
|
Packit Service |
ac8aad |
* objects by coincidence.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym, &base->symbols, list) {
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!kpatch_is_normal_static_local(sym))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (sym->twin)
|
|
Packit Service |
da4517 |
UNCORRELATE_ELEMENT(sym);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
bundled = sym == sym->sec->sym;
|
|
Packit Service |
ac8aad |
if (bundled && sym->sec->twin) {
|
|
Packit Service |
da4517 |
UNCORRELATE_ELEMENT(sym->sec);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
UNCORRELATE_ELEMENT(sym->sec->secsym);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (sym->sec->rela)
|
|
Packit Service |
da4517 |
UNCORRELATE_ELEMENT(sym->sec->rela);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Do the correlations: for each section reference to a static local,
|
|
Packit Service |
ac8aad |
* look for a corresponding reference in the section's twin.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, &base->sections, list) {
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!is_rela_section(sec) ||
|
|
Packit Service |
ac8aad |
is_debug_section(sec) ||
|
|
Packit Service |
ac8aad |
!strcmp(sec->name, ".rela.toc"))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(rela, &sec->relas, list) {
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!toc_rela(rela))
|
|
Packit Service |
ac8aad |
continue; /* skip toc constants */
|
|
Packit Service |
ac8aad |
sym = toc_rela(rela)->sym;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!kpatch_is_normal_static_local(sym))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (sym->twin)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
bundled = sym == sym->sec->sym;
|
|
Packit Service |
ac8aad |
if (bundled && sym->sec == sec->base) {
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* A rare case where a static local data
|
|
Packit Service |
ac8aad |
* structure references itself. There's no
|
|
Packit Service |
ac8aad |
* reliable way to correlate this. Hopefully
|
|
Packit Service |
ac8aad |
* there's another reference to the symbol
|
|
Packit Service |
ac8aad |
* somewhere that can be used.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
log_debug("can't correlate static local %s's reference to itself\n",
|
|
Packit Service |
ac8aad |
sym->name);
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
patched_sym = kpatch_find_static_twin(sec, sym);
|
|
Packit Service |
ac8aad |
if (!patched_sym)
|
|
Packit Service |
ac8aad |
DIFF_FATAL("reference to static local variable %s in %s was removed",
|
|
Packit Service |
ac8aad |
sym->name,
|
|
Packit Service |
ac8aad |
kpatch_section_function_name(sec));
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
patched_bundled = patched_sym == patched_sym->sec->sym;
|
|
Packit Service |
ac8aad |
if (bundled != patched_bundled)
|
|
Packit Service |
ac8aad |
ERROR("bundle mismatch for symbol %s", sym->name);
|
|
Packit Service |
ac8aad |
if (!bundled && sym->sec->twin != patched_sym->sec)
|
|
Packit Service |
da4517 |
ERROR("sections %s and %s aren't correlated for symbol %s",
|
|
Packit Service |
da4517 |
sym->sec->name, patched_sym->sec->name, sym->name);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
kpatch_correlate_static_local(sym, patched_sym);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (bundled)
|
|
Packit Service |
da4517 |
kpatch_correlate_section(sym->sec, patched_sym->sec);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Make sure that:
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* 1. all the base object's referenced static locals have been
|
|
Packit Service |
ac8aad |
* correlated; and
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* 2. each reference to a static local in the base object has a
|
|
Packit Service |
ac8aad |
* corresponding reference in the patched object (because a static
|
|
Packit Service |
ac8aad |
* local can be referenced by more than one section).
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, &base->sections, list) {
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!is_rela_section(sec) ||
|
|
Packit Service |
ac8aad |
is_debug_section(sec))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(rela, &sec->relas, list) {
|
|
Packit Service |
da4517 |
struct section *target_sec = sec;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
sym = rela->sym;
|
|
Packit Service |
ac8aad |
if (!kpatch_is_normal_static_local(sym))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (!sec->twin && sec->base->sym) {
|
|
Packit Service |
da4517 |
struct symbol *parent = NULL;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
parent = kpatch_get_correlated_parent(sec->base->sym);
|
|
Packit Service |
da4517 |
if (parent)
|
|
Packit Service |
da4517 |
target_sec = parent->sec->rela;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (!sym->twin || !target_sec->twin)
|
|
Packit Service |
da4517 |
DIFF_FATAL("reference to static local variable %s in %s was removed",
|
|
Packit Service |
da4517 |
sym->name,
|
|
Packit Service |
da4517 |
kpatch_section_function_name(target_sec));
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (!kpatch_find_static_twin_ref(target_sec->twin, sym))
|
|
Packit Service |
ac8aad |
DIFF_FATAL("static local %s has been correlated with %s, but patched %s is missing a reference to it",
|
|
Packit Service |
ac8aad |
sym->name, sym->twin->name,
|
|
Packit Service |
da4517 |
kpatch_section_function_name(target_sec->twin));
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Now go through the patched object and look for any uncorrelated
|
|
Packit Service |
ac8aad |
* static locals to see if we need to print any warnings about new
|
|
Packit Service |
ac8aad |
* variables.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, &patched->sections, list) {
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!is_rela_section(sec) ||
|
|
Packit Service |
ac8aad |
is_debug_section(sec))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(rela, &sec->relas, list) {
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
sym = rela->sym;
|
|
Packit Service |
ac8aad |
if (!kpatch_is_normal_static_local(sym))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (sym->twin)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
log_normal("WARNING: unable to correlate static local variable %s used by %s, assuming variable is new\n",
|
|
Packit Service |
ac8aad |
sym->name,
|
|
Packit Service |
ac8aad |
kpatch_section_function_name(sec));
|
|
Packit Service |
ac8aad |
return;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_correlate_elfs(struct kpatch_elf *kelf1, struct kpatch_elf *kelf2)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
kpatch_correlate_sections(&kelf1->sections, &kelf2->sections);
|
|
Packit Service |
ac8aad |
kpatch_correlate_symbols(&kelf1->symbols, &kelf2->symbols);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_compare_correlated_elements(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
/* lists are already correlated at this point */
|
|
Packit Service |
ac8aad |
kpatch_compare_sections(&kelf->sections);
|
|
Packit Service |
ac8aad |
kpatch_compare_symbols(&kelf->symbols);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
#ifdef __x86_64__
|
|
Packit Service |
da4517 |
static void rela_insn(const struct section *sec, const struct rela *rela,
|
|
Packit Service |
da4517 |
struct insn *insn)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
unsigned long insn_addr, start, end, rela_addr;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
start = (unsigned long)sec->base->data->d_buf;
|
|
Packit Service |
ac8aad |
end = start + sec->base->sh.sh_size;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (end <= start)
|
|
Packit Service |
da4517 |
ERROR("bad section size");
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
rela_addr = start + rela->offset;
|
|
Packit Service |
ac8aad |
for (insn_addr = start; insn_addr < end; insn_addr += insn->length) {
|
|
Packit Service |
ac8aad |
insn_init(insn, (void *)insn_addr, 1);
|
|
Packit Service |
ac8aad |
insn_get_length(insn);
|
|
Packit Service |
ac8aad |
if (!insn->length)
|
|
Packit Service |
ac8aad |
ERROR("can't decode instruction in section %s at offset 0x%lx",
|
|
Packit Service |
ac8aad |
sec->base->name, insn_addr);
|
|
Packit Service |
ac8aad |
if (rela_addr >= insn_addr &&
|
|
Packit Service |
ac8aad |
rela_addr < insn_addr + insn->length)
|
|
Packit Service |
ac8aad |
return;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
#endif
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
static bool is_callback_section(struct section *sec) {
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
static char *callback_sections[] = {
|
|
Packit Service |
da4517 |
".kpatch.callbacks.pre_patch",
|
|
Packit Service |
da4517 |
".kpatch.callbacks.post_patch",
|
|
Packit Service |
da4517 |
".kpatch.callbacks.pre_unpatch",
|
|
Packit Service |
da4517 |
".kpatch.callbacks.post_unpatch",
|
|
Packit Service |
da4517 |
".rela.kpatch.callbacks.pre_patch",
|
|
Packit Service |
da4517 |
".rela.kpatch.callbacks.post_patch",
|
|
Packit Service |
da4517 |
".rela.kpatch.callbacks.pre_unpatch",
|
|
Packit Service |
da4517 |
".rela.kpatch.callbacks.post_unpatch",
|
|
Packit Service |
da4517 |
NULL,
|
|
Packit Service |
da4517 |
};
|
|
Packit Service |
da4517 |
char **callback_sec;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
for (callback_sec = callback_sections; *callback_sec; callback_sec++)
|
|
Packit Service |
da4517 |
if (!strcmp(sec->name, *callback_sec))
|
|
Packit Service |
da4517 |
return true;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
return false;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Mangle the relas a little. The compiler will sometimes use section symbols
|
|
Packit Service |
ac8aad |
* to reference local objects and functions rather than the object or function
|
|
Packit Service |
ac8aad |
* symbols themselves. We substitute the object/function symbols for the
|
|
Packit Service |
ac8aad |
* section symbol in this case so that the relas can be properly correlated and
|
|
Packit Service |
ac8aad |
* so that the existing object/function in vmlinux can be linked to.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
static void kpatch_replace_sections_syms(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct section *sec;
|
|
Packit Service |
ac8aad |
struct rela *rela;
|
|
Packit Service |
ac8aad |
struct symbol *sym;
|
|
Packit Service |
da4517 |
unsigned int add_off;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
log_debug("\n");
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, &kelf->sections, list) {
|
|
Packit Service |
ac8aad |
if (!is_rela_section(sec) ||
|
|
Packit Service |
ac8aad |
is_debug_section(sec))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(rela, &sec->relas, list) {
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (rela->sym->type != STT_SECTION || !rela->sym->sec)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Replace references to bundled sections with their
|
|
Packit Service |
ac8aad |
* symbols.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
da4517 |
if (rela->sym->sec->sym) {
|
|
Packit Service |
ac8aad |
rela->sym = rela->sym->sec->sym;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* On ppc64le with GCC6+, even with
|
|
Packit Service |
da4517 |
* -ffunction-sections, the function symbol
|
|
Packit Service |
da4517 |
* starts 8 bytes past the beginning of the
|
|
Packit Service |
da4517 |
* section, because the .TOC pointer is at the
|
|
Packit Service |
da4517 |
* beginning, right before the code. So even
|
|
Packit Service |
da4517 |
* though the symbol is bundled, we can't
|
|
Packit Service |
da4517 |
* assume it's at offset 0 in the section.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
rela->addend -= rela->sym->sym.st_value;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
#ifdef __powerpc64__
|
|
Packit Service |
ac8aad |
add_off = 0;
|
|
Packit Service |
ac8aad |
#else
|
|
Packit Service |
da4517 |
if (rela->type == R_X86_64_PC32 ||
|
|
Packit Service |
da4517 |
rela->type == R_X86_64_PLT32) {
|
|
Packit Service |
ac8aad |
struct insn insn;
|
|
Packit Service |
ac8aad |
rela_insn(sec, rela, &insn);
|
|
Packit Service |
da4517 |
add_off = (unsigned int)((long)insn.next_byte -
|
|
Packit Service |
ac8aad |
(long)sec->base->data->d_buf -
|
|
Packit Service |
da4517 |
rela->offset);
|
|
Packit Service |
ac8aad |
} else if (rela->type == R_X86_64_64 ||
|
|
Packit Service |
ac8aad |
rela->type == R_X86_64_32S)
|
|
Packit Service |
ac8aad |
add_off = 0;
|
|
Packit Service |
ac8aad |
else
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
#endif
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Attempt to replace references to unbundled sections
|
|
Packit Service |
ac8aad |
* with their symbols.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
Packit Service |
da4517 |
long start, end;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (sym->type == STT_SECTION ||
|
|
Packit Service |
ac8aad |
sym->sec != rela->sym->sec)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
start = sym->sym.st_value;
|
|
Packit Service |
ac8aad |
end = sym->sym.st_value + sym->sym.st_size;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!is_text_section(sym->sec) &&
|
|
Packit Service |
ac8aad |
rela->type == R_X86_64_32S &&
|
|
Packit Service |
da4517 |
rela->addend == (long)sym->sec->sh.sh_size &&
|
|
Packit Service |
da4517 |
end == (long)sym->sec->sh.sh_size) {
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* A special case where gcc needs a
|
|
Packit Service |
ac8aad |
* pointer to the address at the end of
|
|
Packit Service |
ac8aad |
* a data section.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* This is usually used with a compare
|
|
Packit Service |
ac8aad |
* instruction to determine when to end
|
|
Packit Service |
ac8aad |
* a loop. The code doesn't actually
|
|
Packit Service |
ac8aad |
* dereference the pointer so this is
|
|
Packit Service |
ac8aad |
* "normal" and we just replace the
|
|
Packit Service |
ac8aad |
* section reference with a reference
|
|
Packit Service |
ac8aad |
* to the last symbol in the section.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* Note that this only catches the
|
|
Packit Service |
ac8aad |
* issue when it happens at the end of
|
|
Packit Service |
ac8aad |
* a section. It can also happen in
|
|
Packit Service |
ac8aad |
* the middle of a section. In that
|
|
Packit Service |
ac8aad |
* case, the wrong symbol will be
|
|
Packit Service |
ac8aad |
* associated with the reference. But
|
|
Packit Service |
ac8aad |
* that's ok because:
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* 1) This situation only occurs when
|
|
Packit Service |
ac8aad |
* gcc is trying to get the address
|
|
Packit Service |
ac8aad |
* of the symbol, not the contents
|
|
Packit Service |
ac8aad |
* of its data; and
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* 2) Because kpatch doesn't allow data
|
|
Packit Service |
ac8aad |
* sections to change,
|
|
Packit Service |
ac8aad |
* &(var1+sizeof(var1)) will always
|
|
Packit Service |
ac8aad |
* be the same as &var2.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
} else if (rela->addend + add_off < start ||
|
|
Packit Service |
ac8aad |
rela->addend + add_off >= end)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
log_debug("%s: replacing %s+%ld reference with %s+%ld\n",
|
|
Packit Service |
ac8aad |
sec->name,
|
|
Packit Service |
ac8aad |
rela->sym->name, rela->addend,
|
|
Packit Service |
ac8aad |
sym->name, rela->addend - start);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
rela->sym = sym;
|
|
Packit Service |
ac8aad |
rela->addend -= start;
|
|
Packit Service |
ac8aad |
break;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
log_debug("\n");
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_check_func_profiling_calls(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct symbol *sym;
|
|
Packit Service |
ac8aad |
int errs = 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
Packit Service |
da4517 |
if (sym->type != STT_FUNC || sym->status != CHANGED ||
|
|
Packit Service |
da4517 |
(sym->parent && sym->parent->status == CHANGED))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
if (!sym->twin->has_func_profiling) {
|
|
Packit Service |
ac8aad |
log_normal("function %s has no fentry/mcount call, unable to patch\n",
|
|
Packit Service |
ac8aad |
sym->name);
|
|
Packit Service |
ac8aad |
errs++;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (errs)
|
|
Packit Service |
ac8aad |
DIFF_FATAL("%d function(s) can not be patched", errs);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_verify_patchability(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct section *sec;
|
|
Packit Service |
ac8aad |
int errs = 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, &kelf->sections, list) {
|
|
Packit Service |
ac8aad |
if (sec->status == CHANGED && !sec->include) {
|
|
Packit Service |
ac8aad |
log_normal("changed section %s not selected for inclusion\n",
|
|
Packit Service |
ac8aad |
sec->name);
|
|
Packit Service |
ac8aad |
errs++;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (sec->status != SAME && sec->grouped) {
|
|
Packit Service |
ac8aad |
log_normal("changed section %s is part of a section group\n",
|
|
Packit Service |
ac8aad |
sec->name);
|
|
Packit Service |
ac8aad |
errs++;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (sec->sh.sh_type == SHT_GROUP && sec->status == NEW) {
|
|
Packit Service |
ac8aad |
log_normal("new/changed group sections are not supported\n");
|
|
Packit Service |
ac8aad |
errs++;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* ensure we aren't including .data.* or .bss.*
|
|
Packit Service |
ac8aad |
* (.data.unlikely and .data.once is ok b/c it only has __warned vars)
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
if (sec->include && sec->status != NEW &&
|
|
Packit Service |
ac8aad |
(!strncmp(sec->name, ".data", 5) || !strncmp(sec->name, ".bss", 4)) &&
|
|
Packit Service |
ac8aad |
(strcmp(sec->name, ".data.unlikely") && strcmp(sec->name, ".data.once"))) {
|
|
Packit Service |
ac8aad |
log_normal("data section %s selected for inclusion\n",
|
|
Packit Service |
ac8aad |
sec->name);
|
|
Packit Service |
ac8aad |
errs++;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (errs)
|
|
Packit Service |
ac8aad |
DIFF_FATAL("%d unsupported section change(s)", errs);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_include_symbol(struct symbol *sym);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_include_section(struct section *sec)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct rela *rela;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* Include the section and its section symbol */
|
|
Packit Service |
ac8aad |
if (sec->include)
|
|
Packit Service |
ac8aad |
return;
|
|
Packit Service |
ac8aad |
sec->include = 1;
|
|
Packit Service |
ac8aad |
if (sec->secsym)
|
|
Packit Service |
ac8aad |
sec->secsym->include = 1;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Include the section's rela section and then recursively include the
|
|
Packit Service |
ac8aad |
* symbols needed by its relas.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
if (!sec->rela)
|
|
Packit Service |
ac8aad |
return;
|
|
Packit Service |
ac8aad |
sec->rela->include = 1;
|
|
Packit Service |
ac8aad |
list_for_each_entry(rela, &sec->rela->relas, list)
|
|
Packit Service |
ac8aad |
kpatch_include_symbol(rela->sym);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_include_symbol(struct symbol *sym)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* This function is called recursively from kpatch_include_section().
|
|
Packit Service |
ac8aad |
* Make sure we don't get into an endless loop.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
if (sym->include)
|
|
Packit Service |
ac8aad |
return;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* The symbol gets included even if its section isn't needed, as it
|
|
Packit Service |
ac8aad |
* might be needed: either permanently for a rela, or temporarily for
|
|
Packit Service |
ac8aad |
* the later creation of a dynrela.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
sym->include = 1;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* For a function/object symbol, if it has a section, we only need to
|
|
Packit Service |
ac8aad |
* include the section if it has changed. Otherwise the symbol will be
|
|
Packit Service |
ac8aad |
* used by relas/dynrelas to link to the real symbol externally.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* For section symbols, we always include the section because
|
|
Packit Service |
ac8aad |
* references to them can't otherwise be resolved externally.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
if (sym->sec && (sym->type == STT_SECTION || sym->status != SAME))
|
|
Packit Service |
ac8aad |
kpatch_include_section(sym->sec);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_include_standard_elements(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct section *sec;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, &kelf->sections, list) {
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Include the following sections even if they haven't changed.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* Notes about some of the more interesting sections:
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* - With -fdata-sections, .rodata is only used for:
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* switch jump tables;
|
|
Packit Service |
ac8aad |
* KASAN data (with KASAN enabled, which is rare); and
|
|
Packit Service |
ac8aad |
* an ugly hack in vmx_vcpu_run().
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* Those data are all local to the functions which use them.
|
|
Packit Service |
ac8aad |
* So it's safe to include .rodata.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* - On ppc64le, the .toc section is used for all data
|
|
Packit Service |
ac8aad |
* accesses.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* Note that if any of these sections have rela sections, they
|
|
Packit Service |
ac8aad |
* will also be included in their entirety. That may result in
|
|
Packit Service |
ac8aad |
* some extra (unused) dynrelas getting created, which should
|
|
Packit Service |
ac8aad |
* be harmless.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
if (!strcmp(sec->name, ".shstrtab") ||
|
|
Packit Service |
ac8aad |
!strcmp(sec->name, ".strtab") ||
|
|
Packit Service |
ac8aad |
!strcmp(sec->name, ".symtab") ||
|
|
Packit Service |
ac8aad |
!strcmp(sec->name, ".toc") ||
|
|
Packit Service |
ac8aad |
!strcmp(sec->name, ".rodata") ||
|
|
Packit Service |
ac8aad |
(!strncmp(sec->name, ".rodata.", 8) &&
|
|
Packit Service |
ac8aad |
strstr(sec->name, ".str1."))) {
|
|
Packit Service |
ac8aad |
kpatch_include_section(sec);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* include the NULL symbol */
|
|
Packit Service |
ac8aad |
list_entry(kelf->symbols.next, struct symbol, list)->include = 1;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static int kpatch_include_callback_elements(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct section *sec;
|
|
Packit Service |
ac8aad |
struct symbol *sym;
|
|
Packit Service |
ac8aad |
struct rela *rela;
|
|
Packit Service |
ac8aad |
int found = 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* include load/unload sections */
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, &kelf->sections, list) {
|
|
Packit Service |
da4517 |
if (!is_callback_section(sec))
|
|
Packit Service |
da4517 |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
sec->include = 1;
|
|
Packit Service |
da4517 |
found = 1;
|
|
Packit Service |
da4517 |
if (is_rela_section(sec)) {
|
|
Packit Service |
da4517 |
/* include callback dependencies */
|
|
Packit Service |
da4517 |
rela = list_entry(sec->relas.next, struct rela, list);
|
|
Packit Service |
da4517 |
sym = rela->sym;
|
|
Packit Service |
da4517 |
log_normal("found callback: %s\n",sym->name);
|
|
Packit Service |
da4517 |
kpatch_include_symbol(sym);
|
|
Packit Service |
da4517 |
} else {
|
|
Packit Service |
da4517 |
sec->secsym->include = 1;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
/* Strip temporary structure symbols used by the callback macros. */
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
Packit Service |
da4517 |
if (sym->type == STT_OBJECT && sym->sec &&
|
|
Packit Service |
da4517 |
is_callback_section(sym->sec))
|
|
Packit Service |
da4517 |
sym->include = 0;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
return found;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_include_force_elements(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct section *sec;
|
|
Packit Service |
ac8aad |
struct symbol *sym;
|
|
Packit Service |
ac8aad |
struct rela *rela;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* include force sections */
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, &kelf->sections, list) {
|
|
Packit Service |
ac8aad |
if (!strcmp(sec->name, ".kpatch.force") ||
|
|
Packit Service |
ac8aad |
!strcmp(sec->name, ".rela.kpatch.force")) {
|
|
Packit Service |
ac8aad |
sec->include = 1;
|
|
Packit Service |
ac8aad |
if (!is_rela_section(sec)) {
|
|
Packit Service |
ac8aad |
/* .kpatch.force */
|
|
Packit Service |
ac8aad |
sec->secsym->include = 1;
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
/* .rela.kpatch.force */
|
|
Packit Service |
ac8aad |
list_for_each_entry(rela, &sec->relas, list)
|
|
Packit Service |
ac8aad |
log_normal("function '%s' marked with KPATCH_FORCE_UNSAFE!\n",
|
|
Packit Service |
ac8aad |
rela->sym->name);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* strip temporary global kpatch_force_func_* symbols */
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym, &kelf->symbols, list)
|
|
Packit Service |
ac8aad |
if (!strncmp(sym->name, "__kpatch_force_func_",
|
|
Packit Service |
ac8aad |
strlen("__kpatch_force_func_")))
|
|
Packit Service |
ac8aad |
sym->include = 0;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static int kpatch_include_new_globals(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct symbol *sym;
|
|
Packit Service |
ac8aad |
int nr = 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
Packit Service |
ac8aad |
if (sym->bind == STB_GLOBAL && sym->sec &&
|
|
Packit Service |
ac8aad |
sym->status == NEW) {
|
|
Packit Service |
ac8aad |
kpatch_include_symbol(sym);
|
|
Packit Service |
ac8aad |
nr++;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
return nr;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static int kpatch_include_changed_functions(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct symbol *sym;
|
|
Packit Service |
ac8aad |
int changed_nr = 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
Packit Service |
ac8aad |
if (sym->status == CHANGED &&
|
|
Packit Service |
ac8aad |
sym->type == STT_FUNC) {
|
|
Packit Service |
ac8aad |
changed_nr++;
|
|
Packit Service |
ac8aad |
kpatch_include_symbol(sym);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (sym->type == STT_FILE)
|
|
Packit Service |
ac8aad |
sym->include = 1;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
return changed_nr;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_print_changes(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct symbol *sym;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
Packit Service |
da4517 |
if (!sym->include || !sym->sec || sym->type != STT_FUNC || sym->parent)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
if (sym->status == NEW)
|
|
Packit Service |
ac8aad |
log_normal("new function: %s\n", sym->name);
|
|
Packit Service |
ac8aad |
else if (sym->status == CHANGED)
|
|
Packit Service |
ac8aad |
log_normal("changed function: %s\n", sym->name);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_migrate_symbols(struct list_head *src,
|
|
Packit Service |
ac8aad |
struct list_head *dst,
|
|
Packit Service |
ac8aad |
int (*select)(struct symbol *))
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct symbol *sym, *safe;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry_safe(sym, safe, src, list) {
|
|
Packit Service |
ac8aad |
if (select && !select(sym))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_del(&sym->list);
|
|
Packit Service |
ac8aad |
list_add_tail(&sym->list, dst);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_migrate_included_elements(struct kpatch_elf *kelf, struct kpatch_elf **kelfout)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct section *sec, *safesec;
|
|
Packit Service |
ac8aad |
struct symbol *sym, *safesym;
|
|
Packit Service |
ac8aad |
struct kpatch_elf *out;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* allocate output kelf */
|
|
Packit Service |
ac8aad |
out = malloc(sizeof(*out));
|
|
Packit Service |
ac8aad |
if (!out)
|
|
Packit Service |
ac8aad |
ERROR("malloc");
|
|
Packit Service |
ac8aad |
memset(out, 0, sizeof(*out));
|
|
Packit Service |
ac8aad |
INIT_LIST_HEAD(&out->sections);
|
|
Packit Service |
ac8aad |
INIT_LIST_HEAD(&out->symbols);
|
|
Packit Service |
ac8aad |
INIT_LIST_HEAD(&out->strings);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* migrate included sections from kelf to out */
|
|
Packit Service |
ac8aad |
list_for_each_entry_safe(sec, safesec, &kelf->sections, list) {
|
|
Packit Service |
ac8aad |
if (!sec->include)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
list_del(&sec->list);
|
|
Packit Service |
ac8aad |
list_add_tail(&sec->list, &out->sections);
|
|
Packit Service |
ac8aad |
sec->index = 0;
|
|
Packit Service |
ac8aad |
if (!is_rela_section(sec) && sec->secsym && !sec->secsym->include)
|
|
Packit Service |
ac8aad |
/* break link to non-included section symbol */
|
|
Packit Service |
ac8aad |
sec->secsym = NULL;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* migrate included symbols from kelf to out */
|
|
Packit Service |
ac8aad |
list_for_each_entry_safe(sym, safesym, &kelf->symbols, list) {
|
|
Packit Service |
ac8aad |
if (!sym->include)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
list_del(&sym->list);
|
|
Packit Service |
ac8aad |
list_add_tail(&sym->list, &out->symbols);
|
|
Packit Service |
ac8aad |
sym->index = 0;
|
|
Packit Service |
ac8aad |
sym->strip = 0;
|
|
Packit Service |
ac8aad |
if (sym->sec && !sym->sec->include)
|
|
Packit Service |
ac8aad |
/* break link to non-included section */
|
|
Packit Service |
ac8aad |
sym->sec = NULL;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
*kelfout = out;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_reorder_symbols(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
LIST_HEAD(symbols);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* migrate NULL sym */
|
|
Packit Service |
ac8aad |
kpatch_migrate_symbols(&kelf->symbols, &symbols, is_null_sym);
|
|
Packit Service |
ac8aad |
/* migrate LOCAL FILE sym */
|
|
Packit Service |
ac8aad |
kpatch_migrate_symbols(&kelf->symbols, &symbols, is_file_sym);
|
|
Packit Service |
ac8aad |
/* migrate LOCAL FUNC syms */
|
|
Packit Service |
ac8aad |
kpatch_migrate_symbols(&kelf->symbols, &symbols, is_local_func_sym);
|
|
Packit Service |
ac8aad |
/* migrate all other LOCAL syms */
|
|
Packit Service |
ac8aad |
kpatch_migrate_symbols(&kelf->symbols, &symbols, is_local_sym);
|
|
Packit Service |
ac8aad |
/* migrate all other (GLOBAL) syms */
|
|
Packit Service |
ac8aad |
kpatch_migrate_symbols(&kelf->symbols, &symbols, NULL);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_replace(&symbols, &kelf->symbols);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static int bug_table_group_size(struct kpatch_elf *kelf, int offset)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
static int size = 0;
|
|
Packit Service |
ac8aad |
char *str;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!size) {
|
|
Packit Service |
ac8aad |
str = getenv("BUG_STRUCT_SIZE");
|
|
Packit Service |
ac8aad |
if (!str)
|
|
Packit Service |
ac8aad |
ERROR("BUG_STRUCT_SIZE not set");
|
|
Packit Service |
ac8aad |
size = atoi(str);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
return size;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static int ex_table_group_size(struct kpatch_elf *kelf, int offset)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
static int size = 0;
|
|
Packit Service |
ac8aad |
char *str;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!size) {
|
|
Packit Service |
ac8aad |
str = getenv("EX_STRUCT_SIZE");
|
|
Packit Service |
ac8aad |
if (!str)
|
|
Packit Service |
ac8aad |
ERROR("EX_STRUCT_SIZE not set");
|
|
Packit Service |
ac8aad |
size = atoi(str);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
return size;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
static int jump_table_group_size(struct kpatch_elf *kelf, int offset)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
static int size = 0;
|
|
Packit Service |
ac8aad |
char *str;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!size) {
|
|
Packit Service |
da4517 |
str = getenv("JUMP_STRUCT_SIZE");
|
|
Packit Service |
ac8aad |
if (!str)
|
|
Packit Service |
da4517 |
ERROR("JUMP_STRUCT_SIZE not set");
|
|
Packit Service |
ac8aad |
size = atoi(str);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
return size;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
#ifdef __x86_64__
|
|
Packit Service |
da4517 |
static int parainstructions_group_size(struct kpatch_elf *kelf, int offset)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
static int size = 0;
|
|
Packit Service |
ac8aad |
char *str;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!size) {
|
|
Packit Service |
da4517 |
str = getenv("PARA_STRUCT_SIZE");
|
|
Packit Service |
da4517 |
if (!str)
|
|
Packit Service |
da4517 |
ERROR("PARA_STRUCT_SIZE not set");
|
|
Packit Service |
da4517 |
size = atoi(str);
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
return size;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
static int altinstructions_group_size(struct kpatch_elf *kelf, int offset)
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
static int size = 0;
|
|
Packit Service |
da4517 |
char *str;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (!size) {
|
|
Packit Service |
da4517 |
str = getenv("ALT_STRUCT_SIZE");
|
|
Packit Service |
ac8aad |
if (!str)
|
|
Packit Service |
ac8aad |
ERROR("ALT_STRUCT_SIZE not set");
|
|
Packit Service |
ac8aad |
size = atoi(str);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
return size;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static int smp_locks_group_size(struct kpatch_elf *kelf, int offset)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
return 4;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
#endif
|
|
Packit Service |
ac8aad |
#ifdef __powerpc64__
|
|
Packit Service |
ac8aad |
static int fixup_entry_group_size(struct kpatch_elf *kelf, int offset)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
static int size = 0;
|
|
Packit Service |
ac8aad |
char *str;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!size) {
|
|
Packit Service |
ac8aad |
str = getenv("FIXUP_STRUCT_SIZE");
|
|
Packit Service |
ac8aad |
if (!str)
|
|
Packit Service |
ac8aad |
ERROR("FIXUP_STRUCT_SIZE not set");
|
|
Packit Service |
ac8aad |
size = atoi(str);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
return size;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static int fixup_lwsync_group_size(struct kpatch_elf *kelf, int offset)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
da4517 |
return 8;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
static int fixup_barrier_nospec_group_size(struct kpatch_elf *kelf, int offset)
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
return 8;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
#endif
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* The rela groups in the .fixup section vary in size. The beginning of each
|
|
Packit Service |
ac8aad |
* .fixup rela group is referenced by the __ex_table section. To find the size
|
|
Packit Service |
ac8aad |
* of a .fixup rela group, we have to traverse the __ex_table relas.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
static int fixup_group_size(struct kpatch_elf *kelf, int offset)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct section *sec;
|
|
Packit Service |
ac8aad |
struct rela *rela;
|
|
Packit Service |
ac8aad |
int found;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
sec = find_section_by_name(&kelf->sections, ".rela__ex_table");
|
|
Packit Service |
ac8aad |
if (!sec)
|
|
Packit Service |
ac8aad |
ERROR("missing .rela__ex_table section");
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* find beginning of this group */
|
|
Packit Service |
ac8aad |
found = 0;
|
|
Packit Service |
ac8aad |
list_for_each_entry(rela, &sec->relas, list) {
|
|
Packit Service |
ac8aad |
if (!strcmp(rela->sym->name, ".fixup") &&
|
|
Packit Service |
ac8aad |
rela->addend == offset) {
|
|
Packit Service |
ac8aad |
found = 1;
|
|
Packit Service |
ac8aad |
break;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!found)
|
|
Packit Service |
ac8aad |
ERROR("can't find .fixup rela group at offset %d\n", offset);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* find beginning of next group */
|
|
Packit Service |
ac8aad |
found = 0;
|
|
Packit Service |
ac8aad |
list_for_each_entry_continue(rela, &sec->relas, list) {
|
|
Packit Service |
ac8aad |
if (!strcmp(rela->sym->name, ".fixup") &&
|
|
Packit Service |
ac8aad |
rela->addend > offset) {
|
|
Packit Service |
ac8aad |
found = 1;
|
|
Packit Service |
ac8aad |
break;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!found) {
|
|
Packit Service |
ac8aad |
/* last group */
|
|
Packit Service |
ac8aad |
struct section *fixupsec;
|
|
Packit Service |
ac8aad |
fixupsec = find_section_by_name(&kelf->sections, ".fixup");
|
|
Packit Service |
da4517 |
if (!fixupsec)
|
|
Packit Service |
da4517 |
ERROR("missing .fixup section");
|
|
Packit Service |
da4517 |
return (int)(fixupsec->sh.sh_size - offset);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
return (int)(rela->addend - offset);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static struct special_section special_sections[] = {
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
.name = "__bug_table",
|
|
Packit Service |
ac8aad |
.group_size = bug_table_group_size,
|
|
Packit Service |
ac8aad |
},
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
.name = ".fixup",
|
|
Packit Service |
ac8aad |
.group_size = fixup_group_size,
|
|
Packit Service |
ac8aad |
},
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
.name = "__ex_table", /* must come after .fixup */
|
|
Packit Service |
ac8aad |
.group_size = ex_table_group_size,
|
|
Packit Service |
ac8aad |
},
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
.name = "__jump_table",
|
|
Packit Service |
da4517 |
.group_size = jump_table_group_size,
|
|
Packit Service |
da4517 |
},
|
|
Packit Service |
ac8aad |
#ifdef __x86_64__
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
da4517 |
.name = ".smp_locks",
|
|
Packit Service |
da4517 |
.group_size = smp_locks_group_size,
|
|
Packit Service |
da4517 |
},
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
.name = ".parainstructions",
|
|
Packit Service |
da4517 |
.group_size = parainstructions_group_size,
|
|
Packit Service |
da4517 |
},
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
ac8aad |
.name = ".altinstructions",
|
|
Packit Service |
ac8aad |
.group_size = altinstructions_group_size,
|
|
Packit Service |
ac8aad |
},
|
|
Packit Service |
ac8aad |
#endif
|
|
Packit Service |
ac8aad |
#ifdef __powerpc64__
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
.name = "__ftr_fixup",
|
|
Packit Service |
ac8aad |
.group_size = fixup_entry_group_size,
|
|
Packit Service |
ac8aad |
},
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
.name = "__mmu_ftr_fixup",
|
|
Packit Service |
ac8aad |
.group_size = fixup_entry_group_size,
|
|
Packit Service |
ac8aad |
},
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
.name = "__fw_ftr_fixup",
|
|
Packit Service |
ac8aad |
.group_size = fixup_entry_group_size,
|
|
Packit Service |
ac8aad |
},
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
.name = "__lwsync_fixup",
|
|
Packit Service |
ac8aad |
.group_size = fixup_lwsync_group_size,
|
|
Packit Service |
ac8aad |
},
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
.name = "__barrier_nospec_fixup",
|
|
Packit Service |
da4517 |
.group_size = fixup_barrier_nospec_group_size,
|
|
Packit Service |
da4517 |
},
|
|
Packit Service |
ac8aad |
#endif
|
|
Packit Service |
ac8aad |
{},
|
|
Packit Service |
ac8aad |
};
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
static bool should_keep_jump_label(struct lookup_table *lookup,
|
|
Packit Service |
da4517 |
struct section *sec,
|
|
Packit Service |
da4517 |
unsigned int group_offset,
|
|
Packit Service |
da4517 |
unsigned int group_size,
|
|
Packit Service |
da4517 |
int *jump_labels_found)
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
struct rela *code = NULL, *key = NULL, *rela;
|
|
Packit Service |
da4517 |
bool tracepoint = false, dynamic_debug = false;
|
|
Packit Service |
da4517 |
struct lookup_result symbol;
|
|
Packit Service |
da4517 |
int i = 0;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* Here we hard-code knowledge about the contents of the jump_entry
|
|
Packit Service |
da4517 |
* struct. It has three fields: code, target, and key. Each field has
|
|
Packit Service |
da4517 |
* a relocation associated with it.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
list_for_each_entry(rela, &sec->relas, list) {
|
|
Packit Service |
da4517 |
if (rela->offset >= group_offset &&
|
|
Packit Service |
da4517 |
rela->offset < group_offset + group_size) {
|
|
Packit Service |
da4517 |
if (i == 0)
|
|
Packit Service |
da4517 |
code = rela;
|
|
Packit Service |
da4517 |
else if (i == 2)
|
|
Packit Service |
da4517 |
key = rela;
|
|
Packit Service |
da4517 |
i++;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (i != 3 || !key || !code)
|
|
Packit Service |
da4517 |
ERROR("BUG: __jump_table has an unexpected format");
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (!strncmp(key->sym->name, "__tracepoint_", 13))
|
|
Packit Service |
da4517 |
tracepoint = true;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (is_dynamic_debug_symbol(key->sym))
|
|
Packit Service |
da4517 |
dynamic_debug = true;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (KLP_ARCH) {
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* On older kernels (with .klp.arch support), jump labels
|
|
Packit Service |
da4517 |
* aren't supported at all. Error out when they occur in a
|
|
Packit Service |
da4517 |
* replacement function, with the exception of tracepoints and
|
|
Packit Service |
da4517 |
* dynamic debug printks. An inert tracepoint or printk is
|
|
Packit Service |
da4517 |
* harmless enough, but a broken jump label can cause
|
|
Packit Service |
da4517 |
* unexpected behavior.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
if (tracepoint || dynamic_debug)
|
|
Packit Service |
da4517 |
return false;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* This will be upgraded to an error after all jump labels have
|
|
Packit Service |
da4517 |
* been reported.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
log_normal("Found a jump label at %s()+0x%lx, using key %s. Jump labels aren't supported with this kernel. Use static_key_enabled() instead.\n",
|
|
Packit Service |
da4517 |
code->sym->name, code->addend, key->sym->name);
|
|
Packit Service |
da4517 |
(*jump_labels_found)++;
|
|
Packit Service |
da4517 |
return false;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* On newer (5.8+) kernels, jump labels are supported in the case where
|
|
Packit Service |
da4517 |
* the corresponding static key lives in vmlinux. That's because such
|
|
Packit Service |
da4517 |
* kernels apply vmlinux-specific .klp.rela sections at the same time
|
|
Packit Service |
da4517 |
* (in the klp module load) as normal relas, before jump label init.
|
|
Packit Service |
da4517 |
* On the other hand, jump labels based on static keys which are
|
|
Packit Service |
da4517 |
* defined in modules aren't supported, because late module patching
|
|
Packit Service |
da4517 |
* can result in the klp relas getting applied *after* the klp module's
|
|
Packit Service |
da4517 |
* jump label init.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (lookup_symbol(lookup, key->sym->name, &symbol) &&
|
|
Packit Service |
da4517 |
strcmp(symbol.objname, "vmlinux")) {
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
/* The static key lives in a module -- not supported */
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
/* Inert tracepoints and dynamic debug printks are harmless */
|
|
Packit Service |
da4517 |
if (tracepoint || dynamic_debug)
|
|
Packit Service |
da4517 |
return false;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* This will be upgraded to an error after all jump labels have
|
|
Packit Service |
da4517 |
* been reported.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
log_normal("Found a jump label at %s()+0x%lx, using key %s, which is defined in a module. Use static_key_enabled() instead.\n",
|
|
Packit Service |
da4517 |
code->sym->name, code->addend, key->sym->name);
|
|
Packit Service |
da4517 |
(*jump_labels_found)++;
|
|
Packit Service |
da4517 |
return false;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
/* The static key lives in vmlinux or the patch module itself */
|
|
Packit Service |
da4517 |
return true;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
static bool should_keep_rela_group(struct lookup_table *lookup,
|
|
Packit Service |
da4517 |
struct section *sec, unsigned int offset,
|
|
Packit Service |
da4517 |
unsigned int size, int *jump_labels_found)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct rela *rela;
|
|
Packit Service |
da4517 |
bool found = false;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* check if any relas in the group reference any changed functions */
|
|
Packit Service |
ac8aad |
list_for_each_entry(rela, &sec->relas, list) {
|
|
Packit Service |
da4517 |
if (rela->offset >= offset &&
|
|
Packit Service |
da4517 |
rela->offset < offset + size &&
|
|
Packit Service |
ac8aad |
rela->sym->type == STT_FUNC &&
|
|
Packit Service |
ac8aad |
rela->sym->sec->include) {
|
|
Packit Service |
da4517 |
found = true;
|
|
Packit Service |
ac8aad |
log_debug("new/changed symbol %s found in special section %s\n",
|
|
Packit Service |
ac8aad |
rela->sym->name, sec->name);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (!found)
|
|
Packit Service |
da4517 |
return false;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (!strcmp(sec->name, ".rela__jump_table"))
|
|
Packit Service |
da4517 |
return should_keep_jump_label(lookup, sec, offset, size,
|
|
Packit Service |
da4517 |
jump_labels_found);
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
return true;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* When updating .fixup, the corresponding addends in .ex_table need to be
|
|
Packit Service |
ac8aad |
* updated too. Stash the result in rela.r_addend so that the calculation in
|
|
Packit Service |
ac8aad |
* fixup_group_size() is not affected.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
static void kpatch_update_ex_table_addend(struct kpatch_elf *kelf,
|
|
Packit Service |
ac8aad |
struct special_section *special,
|
|
Packit Service |
ac8aad |
int src_offset, int dest_offset,
|
|
Packit Service |
ac8aad |
int group_size)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct rela *rela;
|
|
Packit Service |
ac8aad |
struct section *sec;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
sec = find_section_by_name(&kelf->sections, ".rela__ex_table");
|
|
Packit Service |
ac8aad |
if (!sec)
|
|
Packit Service |
ac8aad |
ERROR("missing .rela__ex_table section");
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(rela, &sec->relas, list) {
|
|
Packit Service |
ac8aad |
if (!strcmp(rela->sym->name, ".fixup") &&
|
|
Packit Service |
ac8aad |
rela->addend >= src_offset &&
|
|
Packit Service |
ac8aad |
rela->addend < src_offset + group_size)
|
|
Packit Service |
ac8aad |
rela->rela.r_addend = rela->addend - (src_offset - dest_offset);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_regenerate_special_section(struct kpatch_elf *kelf,
|
|
Packit Service |
da4517 |
struct lookup_table *lookup,
|
|
Packit Service |
ac8aad |
struct special_section *special,
|
|
Packit Service |
ac8aad |
struct section *sec)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct rela *rela, *safe;
|
|
Packit Service |
ac8aad |
char *src, *dest;
|
|
Packit Service |
da4517 |
unsigned int group_size, src_offset, dest_offset;
|
|
Packit Service |
da4517 |
int jump_labels_found = 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
LIST_HEAD(newrelas);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
src = sec->base->data->d_buf;
|
|
Packit Service |
ac8aad |
/* alloc buffer for new base section */
|
|
Packit Service |
ac8aad |
dest = malloc(sec->base->sh.sh_size);
|
|
Packit Service |
ac8aad |
if (!dest)
|
|
Packit Service |
ac8aad |
ERROR("malloc");
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* Restore the stashed r_addend from kpatch_update_ex_table_addend. */
|
|
Packit Service |
ac8aad |
if (!strcmp(special->name, "__ex_table")) {
|
|
Packit Service |
ac8aad |
list_for_each_entry(rela, &sec->relas, list) {
|
|
Packit Service |
ac8aad |
if (!strcmp(rela->sym->name, ".fixup"))
|
|
Packit Service |
ac8aad |
rela->addend = rela->rela.r_addend;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
src_offset = 0;
|
|
Packit Service |
ac8aad |
dest_offset = 0;
|
|
Packit Service |
ac8aad |
for ( ; src_offset < sec->base->sh.sh_size; src_offset += group_size) {
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
group_size = special->group_size(kelf, src_offset);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* In some cases the struct has padding at the end to ensure
|
|
Packit Service |
ac8aad |
* that all structs after it are properly aligned. But the
|
|
Packit Service |
ac8aad |
* last struct in the section may not be padded. In that case,
|
|
Packit Service |
ac8aad |
* shrink the group_size such that it still (hopefully)
|
|
Packit Service |
ac8aad |
* contains the data but doesn't go past the end of the
|
|
Packit Service |
ac8aad |
* section.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
if (src_offset + group_size > sec->base->sh.sh_size)
|
|
Packit Service |
da4517 |
group_size = (unsigned int)(sec->base->sh.sh_size - src_offset);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (!should_keep_rela_group(lookup, sec, src_offset, group_size,
|
|
Packit Service |
da4517 |
&jump_labels_found))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Copy all relas in the group. It's possible that the relas
|
|
Packit Service |
ac8aad |
* aren't sorted (e.g. .rela.fixup), so go through the entire
|
|
Packit Service |
ac8aad |
* rela list each time.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
list_for_each_entry_safe(rela, safe, &sec->relas, list) {
|
|
Packit Service |
ac8aad |
if (rela->offset >= src_offset &&
|
|
Packit Service |
ac8aad |
rela->offset < src_offset + group_size) {
|
|
Packit Service |
ac8aad |
/* copy rela entry */
|
|
Packit Service |
ac8aad |
list_del(&rela->list);
|
|
Packit Service |
ac8aad |
list_add_tail(&rela->list, &newrelas);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
rela->offset -= src_offset - dest_offset;
|
|
Packit Service |
ac8aad |
rela->rela.r_offset = rela->offset;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
rela->sym->include = 1;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!strcmp(special->name, ".fixup"))
|
|
Packit Service |
ac8aad |
kpatch_update_ex_table_addend(kelf, special,
|
|
Packit Service |
ac8aad |
src_offset,
|
|
Packit Service |
ac8aad |
dest_offset,
|
|
Packit Service |
ac8aad |
group_size);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* copy base section group */
|
|
Packit Service |
ac8aad |
memcpy(dest + dest_offset, src + src_offset, group_size);
|
|
Packit Service |
ac8aad |
dest_offset += group_size;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (jump_labels_found)
|
|
Packit Service |
da4517 |
ERROR("Found %d jump label(s) in the patched code. Jump labels aren't currently supported. Use static_key_enabled() instead.",
|
|
Packit Service |
da4517 |
jump_labels_found);
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
if (!dest_offset) {
|
|
Packit Service |
ac8aad |
/* no changed or global functions referenced */
|
|
Packit Service |
ac8aad |
sec->status = sec->base->status = SAME;
|
|
Packit Service |
ac8aad |
sec->include = sec->base->include = 0;
|
|
Packit Service |
ac8aad |
free(dest);
|
|
Packit Service |
ac8aad |
return;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* overwrite with new relas list */
|
|
Packit Service |
ac8aad |
list_replace(&newrelas, &sec->relas);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* include both rela and base sections */
|
|
Packit Service |
ac8aad |
sec->include = 1;
|
|
Packit Service |
ac8aad |
sec->base->include = 1;
|
|
Packit Service |
ac8aad |
/* include secsym so .kpatch.arch relas can point to section symbols */
|
|
Packit Service |
ac8aad |
sec->base->secsym->include = 1;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Update text section data buf and size.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* The rela section's data buf and size will be regenerated in
|
|
Packit Service |
ac8aad |
* kpatch_rebuild_rela_section_data().
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
sec->base->data->d_buf = dest;
|
|
Packit Service |
ac8aad |
sec->base->data->d_size = dest_offset;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
#define ORC_IP_PTR_SIZE 4
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* This function is similar to kpatch_regenerate_special_section(), but
|
|
Packit Service |
ac8aad |
* customized for the ORC-related sections. ORC is more special than the other
|
|
Packit Service |
ac8aad |
* special sections because each ORC entry is split into .orc_unwind (struct
|
|
Packit Service |
ac8aad |
* orc_entry) and .orc_unwind_ip.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
static void kpatch_regenerate_orc_sections(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct rela *rela, *safe;
|
|
Packit Service |
ac8aad |
char *src, *dest, *str;
|
|
Packit Service |
ac8aad |
unsigned int src_idx = 0, dest_idx = 0, orc_entry_size;
|
|
Packit Service |
ac8aad |
struct section *orc_sec, *ip_sec;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
str = getenv("ORC_STRUCT_SIZE");
|
|
Packit Service |
ac8aad |
if (!str)
|
|
Packit Service |
ac8aad |
return;
|
|
Packit Service |
ac8aad |
orc_entry_size = atoi(str);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (!orc_entry_size)
|
|
Packit Service |
da4517 |
ERROR("bad ORC_STRUCT_SIZE");
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
LIST_HEAD(newrelas);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
orc_sec = find_section_by_name(&kelf->sections, ".orc_unwind");
|
|
Packit Service |
ac8aad |
ip_sec = find_section_by_name(&kelf->sections, ".orc_unwind_ip");
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!orc_sec || !ip_sec)
|
|
Packit Service |
ac8aad |
return;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (orc_sec->sh.sh_size % orc_entry_size != 0)
|
|
Packit Service |
ac8aad |
ERROR("bad .orc_unwind size");
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (ip_sec->sh.sh_size !=
|
|
Packit Service |
ac8aad |
(orc_sec->sh.sh_size / orc_entry_size) * ORC_IP_PTR_SIZE)
|
|
Packit Service |
ac8aad |
ERROR(".orc_unwind/.orc_unwind_ip size mismatch");
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
src = orc_sec->data->d_buf;
|
|
Packit Service |
ac8aad |
dest = malloc(orc_sec->sh.sh_size);
|
|
Packit Service |
ac8aad |
if (!dest)
|
|
Packit Service |
ac8aad |
ERROR("malloc");
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry_safe(rela, safe, &ip_sec->rela->relas, list) {
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (rela->sym->type != STT_FUNC || !rela->sym->sec->include)
|
|
Packit Service |
ac8aad |
goto next;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* copy orc entry */
|
|
Packit Service |
ac8aad |
memcpy(dest + (dest_idx * orc_entry_size),
|
|
Packit Service |
ac8aad |
src + (src_idx * orc_entry_size),
|
|
Packit Service |
ac8aad |
orc_entry_size);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* move ip rela */
|
|
Packit Service |
ac8aad |
list_del(&rela->list);
|
|
Packit Service |
ac8aad |
list_add_tail(&rela->list, &newrelas);
|
|
Packit Service |
ac8aad |
rela->offset = dest_idx * ORC_IP_PTR_SIZE;
|
|
Packit Service |
ac8aad |
rela->sym->include = 1;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
dest_idx++;
|
|
Packit Service |
ac8aad |
next:
|
|
Packit Service |
ac8aad |
src_idx++;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!dest_idx) {
|
|
Packit Service |
ac8aad |
/* no changed or global functions referenced */
|
|
Packit Service |
ac8aad |
orc_sec->status = ip_sec->status = ip_sec->rela->status = SAME;
|
|
Packit Service |
ac8aad |
orc_sec->include = ip_sec->include = ip_sec->rela->include = 0;
|
|
Packit Service |
ac8aad |
free(dest);
|
|
Packit Service |
ac8aad |
return;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* overwrite with new relas list */
|
|
Packit Service |
ac8aad |
list_replace(&newrelas, &ip_sec->rela->relas);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* include the sections */
|
|
Packit Service |
ac8aad |
orc_sec->include = ip_sec->include = ip_sec->rela->include = 1;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Update data buf/size.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
ac8aad |
* The ip section can keep its old (zeroed data), though its size has
|
|
Packit Service |
ac8aad |
* possibly decreased. The ip rela section's data buf and size will be
|
|
Packit Service |
ac8aad |
* regenerated in kpatch_rebuild_rela_section_data().
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
orc_sec->data->d_buf = dest;
|
|
Packit Service |
ac8aad |
orc_sec->data->d_size = dest_idx * orc_entry_size;
|
|
Packit Service |
ac8aad |
ip_sec->data->d_size = dest_idx * ORC_IP_PTR_SIZE;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_check_relocations(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct rela *rela;
|
|
Packit Service |
ac8aad |
struct section *sec;
|
|
Packit Service |
ac8aad |
Elf_Data *sdata;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, &kelf->sections, list) {
|
|
Packit Service |
ac8aad |
if (!is_rela_section(sec))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
list_for_each_entry(rela, &sec->relas, list) {
|
|
Packit Service |
ac8aad |
if (rela->sym->sec) {
|
|
Packit Service |
ac8aad |
sdata = rela->sym->sec->data;
|
|
Packit Service |
da4517 |
if (rela->addend > (long)sdata->d_size) {
|
|
Packit Service |
da4517 |
ERROR("out-of-range relocation %s+%lx in %s", rela->sym->sec->name,
|
|
Packit Service |
ac8aad |
rela->addend, sec->name);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_include_debug_sections(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct section *sec;
|
|
Packit Service |
ac8aad |
struct rela *rela, *saferela;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* include all .debug_* sections */
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, &kelf->sections, list) {
|
|
Packit Service |
ac8aad |
if (is_debug_section(sec)) {
|
|
Packit Service |
ac8aad |
sec->include = 1;
|
|
Packit Service |
ac8aad |
if (!is_rela_section(sec))
|
|
Packit Service |
ac8aad |
sec->secsym->include = 1;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Go through the .rela.debug_ sections and strip entries
|
|
Packit Service |
ac8aad |
* referencing unchanged symbols
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, &kelf->sections, list) {
|
|
Packit Service |
ac8aad |
if (!is_rela_section(sec) || !is_debug_section(sec))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
list_for_each_entry_safe(rela, saferela, &sec->relas, list)
|
|
Packit Service |
ac8aad |
if (!rela->sym->sec->include)
|
|
Packit Service |
ac8aad |
list_del(&rela->list);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_mark_ignored_sections(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct section *sec, *strsec, *ignoresec;
|
|
Packit Service |
ac8aad |
struct rela *rela;
|
|
Packit Service |
ac8aad |
char *name;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* Ignore any discarded sections */
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, &kelf->sections, list) {
|
|
Packit Service |
ac8aad |
if (!strncmp(sec->name, ".discard", 8) ||
|
|
Packit Service |
ac8aad |
!strncmp(sec->name, ".rela.discard", 13))
|
|
Packit Service |
ac8aad |
sec->ignore = 1;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
sec = find_section_by_name(&kelf->sections, ".kpatch.ignore.sections");
|
|
Packit Service |
ac8aad |
if (!sec)
|
|
Packit Service |
ac8aad |
return;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(rela, &sec->rela->relas, list) {
|
|
Packit Service |
ac8aad |
strsec = rela->sym->sec;
|
|
Packit Service |
ac8aad |
strsec->status = CHANGED;
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Include the string section here. This is because the
|
|
Packit Service |
ac8aad |
* KPATCH_IGNORE_SECTION() macro is passed a literal string
|
|
Packit Service |
ac8aad |
* by the patch author, resulting in a change to the string
|
|
Packit Service |
ac8aad |
* section. If we don't include it, then we will potentially
|
|
Packit Service |
ac8aad |
* get a "changed section not included" error in
|
|
Packit Service |
ac8aad |
* kpatch_verify_patchability() if no other function based change
|
|
Packit Service |
ac8aad |
* also changes the string section. We could try to exclude each
|
|
Packit Service |
ac8aad |
* literal string added to the section by KPATCH_IGNORE_SECTION()
|
|
Packit Service |
ac8aad |
* from the section data comparison, but this is a simpler way.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
strsec->include = 1;
|
|
Packit Service |
da4517 |
strsec->secsym->include = 1;
|
|
Packit Service |
ac8aad |
name = strsec->data->d_buf + rela->addend;
|
|
Packit Service |
ac8aad |
ignoresec = find_section_by_name(&kelf->sections, name);
|
|
Packit Service |
ac8aad |
if (!ignoresec)
|
|
Packit Service |
ac8aad |
ERROR("KPATCH_IGNORE_SECTION: can't find %s", name);
|
|
Packit Service |
ac8aad |
log_normal("ignoring section: %s\n", name);
|
|
Packit Service |
ac8aad |
if (is_rela_section(ignoresec))
|
|
Packit Service |
ac8aad |
ignoresec = ignoresec->base;
|
|
Packit Service |
ac8aad |
ignoresec->ignore = 1;
|
|
Packit Service |
ac8aad |
if (ignoresec->twin)
|
|
Packit Service |
ac8aad |
ignoresec->twin->ignore = 1;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_mark_ignored_sections_same(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct section *sec;
|
|
Packit Service |
ac8aad |
struct symbol *sym;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, &kelf->sections, list) {
|
|
Packit Service |
ac8aad |
if (!sec->ignore)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
sec->status = SAME;
|
|
Packit Service |
ac8aad |
if (!is_rela_section(sec)) {
|
|
Packit Service |
ac8aad |
if (sec->secsym)
|
|
Packit Service |
ac8aad |
sec->secsym->status = SAME;
|
|
Packit Service |
ac8aad |
if (sec->rela)
|
|
Packit Service |
ac8aad |
sec->rela->status = SAME;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
Packit Service |
ac8aad |
if (sym->sec != sec)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
sym->status = SAME;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* strip temporary global __UNIQUE_ID_kpatch_ignore_section_* symbols */
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym, &kelf->symbols, list)
|
|
Packit Service |
ac8aad |
if (!strncmp(sym->name, "__UNIQUE_ID_kpatch_ignore_section_",
|
|
Packit Service |
ac8aad |
strlen("__UNIQUE_ID_kpatch_ignore_section_")))
|
|
Packit Service |
ac8aad |
sym->status = SAME;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
static void kpatch_mark_ignored_children_same(struct symbol *sym)
|
|
Packit Service |
da4517 |
{
|
|
Packit Service |
da4517 |
struct symbol *child;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
list_for_each_entry(child, &sym->children, subfunction_node) {
|
|
Packit Service |
da4517 |
child->status = SAME;
|
|
Packit Service |
da4517 |
kpatch_mark_ignored_children_same(child);
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
static void kpatch_mark_ignored_functions_same(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct section *sec;
|
|
Packit Service |
ac8aad |
struct symbol *sym;
|
|
Packit Service |
ac8aad |
struct rela *rela;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
sec = find_section_by_name(&kelf->sections, ".kpatch.ignore.functions");
|
|
Packit Service |
ac8aad |
if (!sec)
|
|
Packit Service |
ac8aad |
return;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(rela, &sec->rela->relas, list) {
|
|
Packit Service |
ac8aad |
if (!rela->sym->sec)
|
|
Packit Service |
ac8aad |
ERROR("expected bundled symbol");
|
|
Packit Service |
ac8aad |
if (rela->sym->type != STT_FUNC)
|
|
Packit Service |
ac8aad |
ERROR("expected function symbol");
|
|
Packit Service |
ac8aad |
log_normal("ignoring function: %s\n", rela->sym->name);
|
|
Packit Service |
ac8aad |
if (rela->sym->status != CHANGED)
|
|
Packit Service |
ac8aad |
log_normal("NOTICE: no change detected in function %s, unnecessary KPATCH_IGNORE_FUNCTION()?\n", rela->sym->name);
|
|
Packit Service |
ac8aad |
rela->sym->status = SAME;
|
|
Packit Service |
ac8aad |
rela->sym->sec->status = SAME;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
kpatch_mark_ignored_children_same(rela->sym);
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
if (rela->sym->sec->secsym)
|
|
Packit Service |
ac8aad |
rela->sym->sec->secsym->status = SAME;
|
|
Packit Service |
ac8aad |
if (rela->sym->sec->rela)
|
|
Packit Service |
ac8aad |
rela->sym->sec->rela->status = SAME;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* strip temporary global kpatch_ignore_func_* symbols */
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym, &kelf->symbols, list)
|
|
Packit Service |
ac8aad |
if (!strncmp(sym->name, "__kpatch_ignore_func_",
|
|
Packit Service |
ac8aad |
strlen("__kpatch_ignore_func_")))
|
|
Packit Service |
ac8aad |
sym->status = SAME;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_create_kpatch_arch_section(struct kpatch_elf *kelf, char *objname)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct special_section *special;
|
|
Packit Service |
ac8aad |
struct symbol *strsym;
|
|
Packit Service |
ac8aad |
struct section *sec, *karch_sec;
|
|
Packit Service |
ac8aad |
struct rela *rela;
|
|
Packit Service |
ac8aad |
int nr, index = 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (!KLP_ARCH)
|
|
Packit Service |
da4517 |
return;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
nr = sizeof(special_sections) / sizeof(special_sections[0]);
|
|
Packit Service |
da4517 |
karch_sec = create_section_pair(kelf, ".kpatch.arch", sizeof(struct kpatch_arch), nr);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* lookup strings symbol */
|
|
Packit Service |
ac8aad |
strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings");
|
|
Packit Service |
ac8aad |
if (!strsym)
|
|
Packit Service |
ac8aad |
ERROR("can't find .kpatch.strings symbol");
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
for (special = special_sections; special->name; special++) {
|
|
Packit Service |
ac8aad |
if (strcmp(special->name, ".parainstructions") &&
|
|
Packit Service |
ac8aad |
strcmp(special->name, ".altinstructions"))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
sec = find_section_by_name(&kelf->sections, special->name);
|
|
Packit Service |
ac8aad |
if (!sec)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* entries[index].sec */
|
|
Packit Service |
ac8aad |
ALLOC_LINK(rela, &karch_sec->rela->relas);
|
|
Packit Service |
ac8aad |
rela->sym = sec->secsym;
|
|
Packit Service |
ac8aad |
rela->type = ABSOLUTE_RELA_TYPE;
|
|
Packit Service |
ac8aad |
rela->addend = 0;
|
|
Packit Service |
da4517 |
rela->offset = (unsigned int)(index * sizeof(struct kpatch_arch) + \
|
|
Packit Service |
da4517 |
offsetof(struct kpatch_arch, sec));
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* entries[index].objname */
|
|
Packit Service |
ac8aad |
ALLOC_LINK(rela, &karch_sec->rela->relas);
|
|
Packit Service |
ac8aad |
rela->sym = strsym;
|
|
Packit Service |
ac8aad |
rela->type = ABSOLUTE_RELA_TYPE;
|
|
Packit Service |
ac8aad |
rela->addend = offset_of_string(&kelf->strings, objname);
|
|
Packit Service |
da4517 |
rela->offset = (unsigned int)(index * sizeof(struct kpatch_arch) + \
|
|
Packit Service |
da4517 |
offsetof(struct kpatch_arch, objname));
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
index++;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
karch_sec->data->d_size = index * sizeof(struct kpatch_arch);
|
|
Packit Service |
ac8aad |
karch_sec->sh.sh_size = karch_sec->data->d_size;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
static void kpatch_process_special_sections(struct kpatch_elf *kelf,
|
|
Packit Service |
da4517 |
struct lookup_table *lookup)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct special_section *special;
|
|
Packit Service |
ac8aad |
struct section *sec;
|
|
Packit Service |
ac8aad |
struct symbol *sym;
|
|
Packit Service |
ac8aad |
struct rela *rela;
|
|
Packit Service |
ac8aad |
int altinstr = 0;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
for (special = special_sections; special->name; special++) {
|
|
Packit Service |
ac8aad |
sec = find_section_by_name(&kelf->sections, special->name);
|
|
Packit Service |
da4517 |
if (!sec || !sec->rela)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
kpatch_regenerate_special_section(kelf, lookup, special, sec->rela);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (!strcmp(special->name, ".altinstructions") && sec->include)
|
|
Packit Service |
ac8aad |
altinstr = 1;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* The following special sections don't have relas which reference
|
|
Packit Service |
ac8aad |
* non-included symbols, so their entire rela section can be included.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, &kelf->sections, list) {
|
|
Packit Service |
ac8aad |
if (strcmp(sec->name, ".altinstr_replacement"))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Only include .altinstr_replacement if .altinstructions
|
|
Packit Service |
ac8aad |
* is also included.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
if (!altinstr)
|
|
Packit Service |
ac8aad |
break;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* include base section */
|
|
Packit Service |
ac8aad |
sec->include = 1;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* include all symbols in the section */
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym, &kelf->symbols, list)
|
|
Packit Service |
ac8aad |
if (sym->sec == sec)
|
|
Packit Service |
ac8aad |
sym->include = 1;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* include rela section */
|
|
Packit Service |
ac8aad |
if (sec->rela) {
|
|
Packit Service |
ac8aad |
sec->rela->include = 1;
|
|
Packit Service |
ac8aad |
/* include all symbols referenced by relas */
|
|
Packit Service |
ac8aad |
list_for_each_entry(rela, &sec->rela->relas, list)
|
|
Packit Service |
da4517 |
kpatch_include_symbol(rela->sym);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (KLP_ARCH) {
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* The following special sections aren't supported with older
|
|
Packit Service |
da4517 |
* kernels, so make sure we don't ever try to include them.
|
|
Packit Service |
da4517 |
* Otherwise the kernel will see the jump table during module
|
|
Packit Service |
da4517 |
* loading and get confused. Generally it should be safe to
|
|
Packit Service |
da4517 |
* exclude them, it just means that you can't modify jump
|
|
Packit Service |
da4517 |
* labels and enable tracepoints in a patched function.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
list_for_each_entry(sec, &kelf->sections, list) {
|
|
Packit Service |
da4517 |
if (strcmp(sec->name, "__jump_table") &&
|
|
Packit Service |
da4517 |
strcmp(sec->name, "__tracepoints") &&
|
|
Packit Service |
da4517 |
strcmp(sec->name, "__tracepoints_ptrs") &&
|
|
Packit Service |
da4517 |
strcmp(sec->name, "__tracepoints_strings"))
|
|
Packit Service |
da4517 |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
sec->status = SAME;
|
|
Packit Service |
da4517 |
sec->include = 0;
|
|
Packit Service |
da4517 |
if (sec->rela) {
|
|
Packit Service |
da4517 |
sec->rela->status = SAME;
|
|
Packit Service |
da4517 |
sec->rela->include = 0;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
kpatch_regenerate_orc_sections(kelf);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static struct sym_compare_type *kpatch_elf_locals(struct kpatch_elf *kelf)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
struct symbol *sym;
|
|
Packit Service |
ac8aad |
int i = 0, sym_num = 0;
|
|
Packit Service |
ac8aad |
struct sym_compare_type *sym_array;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
Packit Service |
ac8aad |
if (sym->bind != STB_LOCAL)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
if (sym->type != STT_FUNC && sym->type != STT_OBJECT)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
sym_num++;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
if (!sym_num)
|
|
Packit Service |
ac8aad |
return NULL;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
sym_array = malloc((sym_num + 1) * sizeof(struct sym_compare_type));
|
|
Packit Service |
ac8aad |
if (!sym_array)
|
|
Packit Service |
ac8aad |
ERROR("malloc");
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
Packit Service |
ac8aad |
if (sym->bind != STB_LOCAL)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
if (sym->type != STT_FUNC && sym->type != STT_OBJECT)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
sym_array[i].type = sym->type;
|
|
Packit Service |
da4517 |
sym_array[i++].name = strdup(sym->name);
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
sym_array[i].type = 0;
|
|
Packit Service |
ac8aad |
sym_array[i].name = NULL;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
return sym_array;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static void kpatch_create_patches_sections(struct kpatch_elf *kelf,
|
|
Packit Service |
ac8aad |
struct lookup_table *table,
|
|
Packit Service |
ac8aad |
char *objname)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
int nr, index, objname_offset;
|
|
Packit Service |
ac8aad |
struct section *sec, *relasec;
|
|
Packit Service |
ac8aad |
struct symbol *sym, *strsym;
|
|
Packit Service |
ac8aad |
struct rela *rela;
|
|
Packit Service |
da4517 |
struct lookup_result symbol;
|
|
Packit Service |
ac8aad |
struct kpatch_patch_func *funcs;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* count patched functions */
|
|
Packit Service |
ac8aad |
nr = 0;
|
|
Packit Service |
da4517 |
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
Packit Service |
da4517 |
if (sym->type != STT_FUNC || sym->status != CHANGED ||
|
|
Packit Service |
da4517 |
sym->parent)
|
|
Packit Service |
da4517 |
continue;
|
|
Packit Service |
da4517 |
nr++;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* create text/rela section pair */
|
|
Packit Service |
ac8aad |
sec = create_section_pair(kelf, ".kpatch.funcs", sizeof(*funcs), nr);
|
|
Packit Service |
ac8aad |
relasec = sec->rela;
|
|
Packit Service |
ac8aad |
funcs = sec->data->d_buf;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* lookup strings symbol */
|
|
Packit Service |
ac8aad |
strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings");
|
|
Packit Service |
ac8aad |
if (!strsym)
|
|
Packit Service |
ac8aad |
ERROR("can't find .kpatch.strings symbol");
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* add objname to strings */
|
|
Packit Service |
ac8aad |
objname_offset = offset_of_string(&kelf->strings, objname);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* populate sections */
|
|
Packit Service |
ac8aad |
index = 0;
|
|
Packit Service |
ac8aad |
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
Packit Service |
da4517 |
if (sym->type != STT_FUNC || sym->status != CHANGED ||
|
|
Packit Service |
da4517 |
sym->parent)
|
|
Packit Service |
da4517 |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (!lookup_symbol(table, sym->name, &symbol))
|
|
Packit Service |
da4517 |
ERROR("can't find symbol '%s' in symbol table", sym->name);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (sym->bind == STB_LOCAL && symbol.global)
|
|
Packit Service |
da4517 |
ERROR("can't find local symbol '%s' in symbol table", sym->name);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
log_debug("lookup for %s: obj=%s sympos=%lu size=%lu",
|
|
Packit Service |
da4517 |
sym->name, symbol.objname, symbol.sympos,
|
|
Packit Service |
da4517 |
symbol.size);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* Convert global symbols to local so other objects in the
|
|
Packit Service |
da4517 |
* patch module (like the patch callback object's init code)
|
|
Packit Service |
da4517 |
* won't link to this function and call it before its
|
|
Packit Service |
da4517 |
* relocations have been applied.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
sym->bind = STB_LOCAL;
|
|
Packit Service |
da4517 |
sym->sym.st_info = (unsigned char)
|
|
Packit Service |
da4517 |
GELF_ST_INFO(sym->bind, sym->type);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
/* add entry in text section */
|
|
Packit Service |
da4517 |
funcs[index].old_addr = symbol.addr;
|
|
Packit Service |
da4517 |
funcs[index].old_size = symbol.size;
|
|
Packit Service |
da4517 |
funcs[index].new_size = sym->sym.st_size;
|
|
Packit Service |
da4517 |
funcs[index].sympos = symbol.sympos;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* Add a relocation that will populate the
|
|
Packit Service |
da4517 |
* funcs[index].new_addr field at module load time.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
ALLOC_LINK(rela, &relasec->relas);
|
|
Packit Service |
da4517 |
rela->sym = sym;
|
|
Packit Service |
da4517 |
rela->type = ABSOLUTE_RELA_TYPE;
|
|
Packit Service |
da4517 |
rela->addend = 0;
|
|
Packit Service |
da4517 |
rela->offset = (unsigned int)(index * sizeof(*funcs));
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* Add a relocation that will populate the funcs[index].name
|
|
Packit Service |
da4517 |
* field.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
ALLOC_LINK(rela, &relasec->relas);
|
|
Packit Service |
da4517 |
rela->sym = strsym;
|
|
Packit Service |
da4517 |
rela->type = ABSOLUTE_RELA_TYPE;
|
|
Packit Service |
da4517 |
rela->addend = offset_of_string(&kelf->strings, sym->name);
|
|
Packit Service |
da4517 |
rela->offset = (unsigned int)(index * sizeof(*funcs) +
|
|
Packit Service |
da4517 |
offsetof(struct kpatch_patch_func, name));
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* Add a relocation that will populate the funcs[index].objname
|
|
Packit Service |
da4517 |
* field.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
ALLOC_LINK(rela, &relasec->relas);
|
|
Packit Service |
da4517 |
rela->sym = strsym;
|
|
Packit Service |
da4517 |
rela->type = ABSOLUTE_RELA_TYPE;
|
|
Packit Service |
da4517 |
rela->addend = objname_offset;
|
|
Packit Service |
da4517 |
rela->offset = (unsigned int)(index * sizeof(*funcs) +
|
|
Packit Service |
da4517 |
offsetof(struct kpatch_patch_func,objname));
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
index++;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* sanity check, index should equal nr */
|
|
Packit Service |
ac8aad |
if (index != nr)
|
|
Packit Service |
ac8aad |
ERROR("size mismatch in funcs sections");
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static int kpatch_is_core_module_symbol(char *name)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
return (!strcmp(name, "kpatch_shadow_alloc") ||
|
|
Packit Service |
ac8aad |
!strcmp(name, "kpatch_shadow_free") ||
|
|
Packit Service |
ac8aad |
!strcmp(name, "kpatch_shadow_get"));
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
static int function_ptr_rela(const struct rela *rela)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
const struct rela *rela_toc = toc_rela(rela);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
return (rela_toc && rela_toc->sym->type == STT_FUNC &&
|
|
Packit Service |
da4517 |
!rela_toc->sym->parent &&
|
|
Packit Service |
ac8aad |
rela_toc->addend == (int)rela_toc->sym->sym.st_value &&
|
|
Packit Service |
ac8aad |
(rela->type == R_X86_64_32S ||
|
|
Packit Service |
ac8aad |
rela->type == R_PPC64_TOC16_HA ||
|
|
Packit Service |
ac8aad |
rela->type == R_PPC64_TOC16_LO_DS));
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
static bool need_dynrela(struct lookup_table *table, const struct rela *rela)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
da4517 |
struct lookup_result symbol;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
da4517 |
* These references are treated specially by the module loader and
|
|
Packit Service |
ac8aad |
* should never be converted to dynrelas.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
if (rela->type == R_PPC64_REL16_HA || rela->type == R_PPC64_REL16_LO ||
|
|
Packit Service |
da4517 |
rela->type == R_PPC64_REL64 || rela->type == R_PPC64_ENTRY)
|
|
Packit Service |
da4517 |
return false;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* On powerpc, the function prologue generated by GCC 6 has the
|
|
Packit Service |
da4517 |
* sequence:
|
|
Packit Service |
da4517 |
*
|
|
Packit Service |
da4517 |
* .globl my_func
|
|
Packit Service |
da4517 |
* .type my_func, @function
|
|
Packit Service |
da4517 |
* .quad .TOC.-my_func
|
|
Packit Service |
da4517 |
* my_func:
|
|
Packit Service |
da4517 |
* .reloc ., R_PPC64_ENTRY ; optional
|
|
Packit Service |
da4517 |
* ld r2,-8(r12)
|
|
Packit Service |
da4517 |
* add r2,r2,r12
|
|
Packit Service |
da4517 |
* .localentry my_func, .-my_func
|
|
Packit Service |
da4517 |
*
|
|
Packit Service |
da4517 |
* The R_PPC64_ENTRY is optional and its symbol might have an empty
|
|
Packit Service |
da4517 |
* name. Leave it as a normal rela.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
if (rela->type == R_PPC64_ENTRY)
|
|
Packit Service |
da4517 |
return false;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
da4517 |
* Allow references to core module symbols to remain as normal
|
|
Packit Service |
da4517 |
* relas. They should be exported.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
da4517 |
if (kpatch_is_core_module_symbol(rela->sym->name))
|
|
Packit Service |
da4517 |
return false;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (rela->sym->sec) {
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* Internal symbols usually don't need dynrelas, because they
|
|
Packit Service |
da4517 |
* live in the patch module and can be relocated normally.
|
|
Packit Service |
da4517 |
*
|
|
Packit Service |
da4517 |
* There's one exception: function pointers.
|
|
Packit Service |
da4517 |
*
|
|
Packit Service |
da4517 |
* If the rela references a function pointer, we convert it to
|
|
Packit Service |
da4517 |
* a dynrela, so that the function pointer will refer to the
|
|
Packit Service |
da4517 |
* original function rather than the patched function. This
|
|
Packit Service |
da4517 |
* can prevent crashes in cases where the function pointer is
|
|
Packit Service |
da4517 |
* called asynchronously after the patch module has been
|
|
Packit Service |
da4517 |
* unloaded.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
if (!function_ptr_rela(rela))
|
|
Packit Service |
da4517 |
return false;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* Function pointers which refer to _nested_ functions are a
|
|
Packit Service |
da4517 |
* special case. They are not supposed to be visible outside
|
|
Packit Service |
da4517 |
* of the function that defines them. Their names may differ
|
|
Packit Service |
da4517 |
* in the original and the patched kernels which makes it
|
|
Packit Service |
da4517 |
* difficult to use dynrelas. Fortunately, nested functions
|
|
Packit Service |
da4517 |
* are rare and are unlikely to be used as asynchronous
|
|
Packit Service |
da4517 |
* callbacks, so the patched code can refer to them directly.
|
|
Packit Service |
da4517 |
* It seems, one can only distinguish such functions by their
|
|
Packit Service |
da4517 |
* names containing a dot. Other kinds of functions with such
|
|
Packit Service |
da4517 |
* names (e.g. optimized copies of functions) are unlikely to
|
|
Packit Service |
da4517 |
* be used as callbacks.
|
|
Packit Service |
da4517 |
*
|
|
Packit Service |
da4517 |
* Function pointers to *new* functions don't have this issue,
|
|
Packit Service |
da4517 |
* just use a normal rela for them.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
return toc_rela(rela)->sym->status != NEW &&
|
|
Packit Service |
da4517 |
!strchr(toc_rela(rela)->sym->name, '.');
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (!lookup_symbol(table, rela->sym->name, &symbol)) {
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* Assume the symbol lives in another .o in the patch module.
|
|
Packit Service |
da4517 |
* A normal rela should work.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
return false;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (rela->sym->bind == STB_LOCAL) {
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (symbol.global)
|
|
Packit Service |
da4517 |
ERROR("can't find local symbol '%s' in symbol table",
|
|
Packit Service |
da4517 |
rela->sym->name);
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* The symbol is (formerly) local. Use a dynrela to access the
|
|
Packit Service |
da4517 |
* original version of the symbol in the patched object.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
return true;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (symbol.exported) {
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (is_gcc6_localentry_bundled_sym(rela->sym)) {
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* On powerpc, the symbol is global and exported, but
|
|
Packit Service |
da4517 |
* it was also in the changed object file. In this
|
|
Packit Service |
da4517 |
* case the rela refers to the 'localentry' point, so a
|
|
Packit Service |
da4517 |
* normal rela wouldn't work. Force a dynrela so it
|
|
Packit Service |
da4517 |
* can be handled correctly by the livepatch relocation
|
|
Packit Service |
da4517 |
* code.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
return true;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (!strcmp(symbol.objname, "vmlinux")) {
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* The symbol is exported by vmlinux. Use a normal
|
|
Packit Service |
da4517 |
* rela.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
return false;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* The symbol is exported by the to-be-patched module, or by
|
|
Packit Service |
da4517 |
* another module which the patched module depends on. Use a
|
|
Packit Service |
da4517 |
* dynrela because of late module loading: the patch module may
|
|
Packit Service |
da4517 |
* be loaded before the to-be-patched (or other) module.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
return true;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
if (symbol.global) {
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* The symbol is global in the to-be-patched object, but not
|
|
Packit Service |
da4517 |
* exported. Use a dynrela to work around the fact that it's
|
|
Packit Service |
da4517 |
* an unexported sybmbol.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
return true;
|
|
Packit Service |
da4517 |
}
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* The symbol is global and not exported, but it's not in the parent
|
|
Packit Service |
da4517 |
* object. The only explanation is that it's defined in another object
|
|
Packit Service |
da4517 |
* in the patch module. A normal rela should resolve it.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
da4517 |
return false;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
/*
|
|
Packit Service |
da4517 |
* kpatch_create_intermediate_sections()
|
|
Packit Service |
da4517 |
*
|
|
Packit Service |
da4517 |
* The primary purpose of this function is to convert some relas (also known as
|
|
Packit Service |
da4517 |
* relocations) to dynrelas (also known as dynamic relocations or livepatch
|
|
Packit Service |
da4517 |
* relocations or klp relas).
|
|
Packit Service |
da4517 |
*
|
|
Packit Service |
da4517 |
* If the patched code refers to a symbol, for example, if it calls a function
|
|
Packit Service |
da4517 |
* or stores a pointer to a function somewhere or accesses some global data,
|
|
Packit Service |
da4517 |
* the address of that symbol must be resolved somehow before the patch is
|
|
Packit Service |
da4517 |
* applied.
|
|
Packit Service |
da4517 |
*
|
|
Packit Service |
da4517 |
* If the symbol lives outside the patch module, and if it's not exported by
|
|
Packit Service |
da4517 |
* vmlinux (e.g., with EXPORT_SYMBOL) then the rela needs to be converted to a
|
|
Packit Service |
da4517 |
* dynrela so the livepatch code can resolve it at runtime.
|
|
Packit Service |
da4517 |
*/
|
|
Packit Service |
ac8aad |
static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
|
|
Packit Service |
ac8aad |
struct lookup_table *table,
|
|
Packit Service |
ac8aad |
char *objname,
|
|
Packit Service |
ac8aad |
char *pmod_name)
|
|
Packit Service |
ac8aad |
{
|
|
Packit Service |
ac8aad |
int nr, index;
|
|
Packit Service |
ac8aad |
struct section *sec, *ksym_sec, *krela_sec;
|
|
Packit Service |
ac8aad |
struct rela *rela, *rela2, *safe;
|
|
Packit Service |
ac8aad |
struct symbol *strsym, *ksym_sec_sym;
|
|
Packit Service |
ac8aad |
struct kpatch_symbol *ksyms;
|
|
Packit Service |
ac8aad |
struct kpatch_relocation *krelas;
|
|
Packit Service |
da4517 |
struct lookup_result symbol;
|
|
Packit Service |
da4517 |
bool special;
|
|
Packit Service |
da4517 |
bool vmlinux = !strcmp(objname, "vmlinux");
|
|
Packit Service |
da4517 |
struct special_section *s;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* count rela entries that need to be dynamic */
|
|
Packit Service |
ac8aad |
nr = 0;
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, &kelf->sections, list) {
|
|
Packit Service |
ac8aad |
if (!is_rela_section(sec))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
if (!strcmp(sec->name, ".rela.kpatch.funcs"))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
list_for_each_entry(rela, &sec->relas, list) {
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
/* upper bound on number of kpatch relas and symbols */
|
|
Packit Service |
da4517 |
nr++;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
da4517 |
* We set 'need_dynrela' here in the first pass because
|
|
Packit Service |
da4517 |
* the .toc section's 'need_dynrela' values are
|
|
Packit Service |
da4517 |
* dependent on all the other sections. Otherwise, if
|
|
Packit Service |
da4517 |
* we did this analysis in the second pass, we'd have
|
|
Packit Service |
da4517 |
* to convert .toc dynrelas at the very end.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
da4517 |
* Specifically, this is needed for the powerpc
|
|
Packit Service |
da4517 |
* internal symbol function pointer check which is done
|
|
Packit Service |
da4517 |
* via .toc indirection in need_dynrela().
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
da4517 |
if (need_dynrela(table, rela))
|
|
Packit Service |
ac8aad |
toc_rela(rela)->need_dynrela = 1;
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
}
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* create .kpatch.relocations text/rela section pair */
|
|
Packit Service |
ac8aad |
krela_sec = create_section_pair(kelf, ".kpatch.relocations", sizeof(*krelas), nr);
|
|
Packit Service |
ac8aad |
krelas = krela_sec->data->d_buf;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* create .kpatch.symbols text/rela section pair */
|
|
Packit Service |
ac8aad |
ksym_sec = create_section_pair(kelf, ".kpatch.symbols", sizeof(*ksyms), nr);
|
|
Packit Service |
ac8aad |
ksyms = ksym_sec->data->d_buf;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* create .kpatch.symbols section symbol (to set rela->sym later) */
|
|
Packit Service |
ac8aad |
ALLOC_LINK(ksym_sec_sym, &kelf->symbols);
|
|
Packit Service |
ac8aad |
ksym_sec_sym->sec = ksym_sec;
|
|
Packit Service |
ac8aad |
ksym_sec_sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION);
|
|
Packit Service |
ac8aad |
ksym_sec_sym->type = STT_SECTION;
|
|
Packit Service |
ac8aad |
ksym_sec_sym->bind = STB_LOCAL;
|
|
Packit Service |
ac8aad |
ksym_sec_sym->name = ".kpatch.symbols";
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* lookup strings symbol */
|
|
Packit Service |
ac8aad |
strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings");
|
|
Packit Service |
ac8aad |
if (!strsym)
|
|
Packit Service |
ac8aad |
ERROR("can't find .kpatch.strings symbol");
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* populate sections */
|
|
Packit Service |
ac8aad |
index = 0;
|
|
Packit Service |
ac8aad |
list_for_each_entry(sec, &kelf->sections, list) {
|
|
Packit Service |
ac8aad |
if (!is_rela_section(sec))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
if (!strcmp(sec->name, ".rela.kpatch.funcs") ||
|
|
Packit Service |
da4517 |
!strcmp(sec->name, ".rela.kpatch.relocations") ||
|
|
Packit Service |
da4517 |
!strcmp(sec->name, ".rela.kpatch.symbols"))
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
da4517 |
special = false;
|
|
Packit Service |
da4517 |
for (s = special_sections; s->name; s++)
|
|
Packit Service |
da4517 |
if (!strcmp(sec->base->name, s->name))
|
|
Packit Service |
da4517 |
special = true;
|
|
Packit Service |
da4517 |
|
|
Packit Service |
ac8aad |
list_for_each_entry_safe(rela, safe, &sec->relas, list) {
|
|
Packit Service |
ac8aad |
if (!rela->need_dynrela)
|
|
Packit Service |
ac8aad |
continue;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
da4517 |
* Starting with Linux 5.8, .klp.arch sections are no
|
|
Packit Service |
da4517 |
* longer supported: now that vmlinux relocations are
|
|
Packit Service |
da4517 |
* written early, before paravirt and alternative
|
|
Packit Service |
da4517 |
* module init, .klp.arch is technically not needed.
|
|
Packit Service |
ac8aad |
*
|
|
Packit Service |
da4517 |
* For sanity we just need to make sure that there are
|
|
Packit Service |
da4517 |
* no .klp.rela.{module}.{section} sections for special
|
|
Packit Service |
da4517 |
* sections. Otherwise there might be ordering issues,
|
|
Packit Service |
da4517 |
* if the .klp.relas are applied after the module
|
|
Packit Service |
da4517 |
* special section init code (e.g., apply_paravirt)
|
|
Packit Service |
da4517 |
* runs due to late module patching.
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
da4517 |
if (!KLP_ARCH && !vmlinux && special)
|
|
Packit Service |
da4517 |
ERROR("unsupported dynrela reference to symbol '%s' in module-specific special section '%s'",
|
|
Packit Service |
da4517 |
rela->sym->name, sec->base->name);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
if (!lookup_symbol(table, rela->sym->name, &symbol))
|
|
Packit Service |
da4517 |
ERROR("can't find symbol '%s' in symbol table",
|
|
Packit Service |
da4517 |
rela->sym->name);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
da4517 |
log_debug("lookup for %s: obj=%s sympos=%lu",
|
|
Packit Service |
da4517 |
rela->sym->name, symbol.objname,
|
|
Packit Service |
da4517 |
symbol.sympos);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* Fill in ksyms[index] */
|
|
Packit Service |
ac8aad |
if (vmlinux)
|
|
Packit Service |
da4517 |
ksyms[index].src = symbol.addr;
|
|
Packit Service |
ac8aad |
else
|
|
Packit Service |
ac8aad |
/* for modules, src is discovered at runtime */
|
|
Packit Service |
ac8aad |
ksyms[index].src = 0;
|
|
Packit Service |
da4517 |
ksyms[index].sympos = symbol.sympos;
|
|
Packit Service |
ac8aad |
ksyms[index].type = rela->sym->type;
|
|
Packit Service |
ac8aad |
ksyms[index].bind = rela->sym->bind;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* add rela to fill in ksyms[index].name field */
|
|
Packit Service |
ac8aad |
ALLOC_LINK(rela2, &ksym_sec->rela->relas);
|
|
Packit Service |
ac8aad |
rela2->sym = strsym;
|
|
Packit Service |
ac8aad |
rela2->type = ABSOLUTE_RELA_TYPE;
|
|
Packit Service |
ac8aad |
rela2->addend = offset_of_string(&kelf->strings, rela->sym->name);
|
|
Packit Service |
da4517 |
rela2->offset = (unsigned int)(index * sizeof(*ksyms) + \
|
|
Packit Service |
da4517 |
offsetof(struct kpatch_symbol, name));
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* add rela to fill in ksyms[index].objname field */
|
|
Packit Service |
ac8aad |
ALLOC_LINK(rela2, &ksym_sec->rela->relas);
|
|
Packit Service |
ac8aad |
rela2->sym = strsym;
|
|
Packit Service |
ac8aad |
rela2->type = ABSOLUTE_RELA_TYPE;
|
|
Packit Service |
da4517 |
rela2->addend = offset_of_string(&kelf->strings, symbol.objname);
|
|
Packit Service |
da4517 |
rela2->offset = (unsigned int)(index * sizeof(*ksyms) + \
|
|
Packit Service |
da4517 |
offsetof(struct kpatch_symbol, objname));
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* Fill in krelas[index] */
|
|
Packit Service |
ac8aad |
if (is_gcc6_localentry_bundled_sym(rela->sym) &&
|
|
Packit Service |
ac8aad |
rela->addend == (int)rela->sym->sym.st_value)
|
|
Packit Service |
ac8aad |
rela->addend -= rela->sym->sym.st_value;
|
|
Packit Service |
ac8aad |
krelas[index].addend = rela->addend;
|
|
Packit Service |
ac8aad |
krelas[index].type = rela->type;
|
|
Packit Service |
da4517 |
krelas[index].external = !vmlinux && symbol.exported;
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* add rela to fill in krelas[index].dest field */
|
|
Packit Service |
ac8aad |
ALLOC_LINK(rela2, &krela_sec->rela->relas);
|
|
Packit Service |
ac8aad |
if (sec->base->secsym)
|
|
Packit Service |
ac8aad |
rela2->sym = sec->base->secsym;
|
|
Packit Service |
ac8aad |
else
|
|
Packit Service |
ac8aad |
ERROR("can't create dynrela for section %s (symbol %s): no bundled or section symbol",
|
|
Packit Service |
ac8aad |
sec->name, rela->sym->name);
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
rela2->type = ABSOLUTE_RELA_TYPE;
|
|
Packit Service |
ac8aad |
rela2->addend = rela->offset;
|
|
Packit Service |
da4517 |
rela2->offset = (unsigned int)(index * sizeof(*krelas) + \
|
|
Packit Service |
da4517 |
offsetof(struct kpatch_relocation, dest));
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* add rela to fill in krelas[index].objname field */
|
|
Packit Service |
ac8aad |
ALLOC_LINK(rela2, &krela_sec->rela->relas);
|
|
Packit Service |
ac8aad |
rela2->sym = strsym;
|
|
Packit Service |
ac8aad |
rela2->type = ABSOLUTE_RELA_TYPE;
|
|
Packit Service |
ac8aad |
rela2->addend = offset_of_string(&kelf->strings, objname);
|
|
Packit Service |
da4517 |
rela2->offset = (unsigned int)(index * sizeof(*krelas) + \
|
|
Packit Service |
da4517 |
offsetof(struct kpatch_relocation, objname));
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/* add rela to fill in krelas[index].ksym field */
|
|
Packit Service |
ac8aad |
ALLOC_LINK(rela2, &krela_sec->rela->relas);
|
|
Packit Service |
ac8aad |
rela2->sym = ksym_sec_sym;
|
|
Packit Service |
ac8aad |
rela2->type = ABSOLUTE_RELA_TYPE;
|
|
Packit Service |
da4517 |
rela2->addend = (unsigned int)(index * sizeof(*ksyms));
|
|
Packit Service |
da4517 |
rela2->offset = (unsigned int)(index * sizeof(*krelas) + \
|
|
Packit Service |
da4517 |
offsetof(struct kpatch_relocation, ksym));
|
|
Packit Service |
ac8aad |
|
|
Packit Service |
ac8aad |
/*
|
|
Packit Service |
ac8aad |
* Mark the referred to symbol for removal but
|
|
Packit Service |
ac8aad |
* only if it is not from this object file.
|
|
Packit Service |
ac8aad |
* The symbols from this object file may be needed
|
|
Packit Service |
ac8aad |
* later (for example, they may have relocations
|
|
Packit Service |
ac8aad |
* of their own which should be processed).
|
|
Packit Service |
ac8aad |
*/
|
|
Packit Service |
ac8aad |
if (!rela->sym->sec)
|
|
Packit Service |
ac8aad |
rela->sym->strip = 1;
|
|
Packit Service |
ac8aad |
list_del(&rela->list);
|
|
Packit Service |
|