mirror of
https://github.com/dynup/kpatch
synced 2024-12-26 07:12:03 +00:00
Merge pull request #737 from jpoimboe/bundling
kpatch-build: do symbol/section bundling in create-diff-object
This commit is contained in:
commit
10b37a16a4
@ -81,6 +81,102 @@ struct special_section {
|
||||
* Functions
|
||||
* **********/
|
||||
|
||||
static int is_bundleable(struct symbol *sym)
|
||||
{
|
||||
if (sym->type == STT_FUNC &&
|
||||
!strncmp(sym->sec->name, ".text.",6) &&
|
||||
!strcmp(sym->sec->name + 6, sym->name))
|
||||
return 1;
|
||||
|
||||
if (sym->type == STT_FUNC &&
|
||||
!strncmp(sym->sec->name, ".text.unlikely.",15) &&
|
||||
!strcmp(sym->sec->name + 15, sym->name))
|
||||
return 1;
|
||||
|
||||
if (sym->type == STT_OBJECT &&
|
||||
!strncmp(sym->sec->name, ".data.",6) &&
|
||||
!strcmp(sym->sec->name + 6, sym->name))
|
||||
return 1;
|
||||
|
||||
if (sym->type == STT_OBJECT &&
|
||||
!strncmp(sym->sec->name, ".rodata.",8) &&
|
||||
!strcmp(sym->sec->name + 8, sym->name))
|
||||
return 1;
|
||||
|
||||
if (sym->type == STT_OBJECT &&
|
||||
!strncmp(sym->sec->name, ".bss.",5) &&
|
||||
!strcmp(sym->sec->name + 5, sym->name))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __powerpc__
|
||||
/* Symbol st_others value for powerpc */
|
||||
#define STO_PPC64_LOCAL_BIT 5
|
||||
#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT)
|
||||
#define PPC64_LOCAL_ENTRY_OFFSET(other) \
|
||||
(((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2)
|
||||
|
||||
/*
|
||||
* On ppc64le, the function prologue generated by GCC 6+ has the sequence:
|
||||
*
|
||||
* .globl my_func
|
||||
* .type my_func, @function
|
||||
* .quad .TOC.-my_func
|
||||
* my_func:
|
||||
* .reloc ., R_PPC64_ENTRY ; optional
|
||||
* ld r2,-8(r12)
|
||||
* add r2,r2,r12
|
||||
* .localentry my_func, .-my_func
|
||||
*
|
||||
* my_func is the global entry point, which, when called, sets up the TOC.
|
||||
* .localentry is the local entry point, for calls to the function from within
|
||||
* the object file. The local entry point is 8 bytes after the global entry
|
||||
* point.
|
||||
*/
|
||||
static int is_localentry_sym(struct symbol *sym)
|
||||
{
|
||||
if (sym->type != STT_FUNC || sym->sym.st_shndx == SHN_UNDEF)
|
||||
return 0;
|
||||
|
||||
if (sym->sym.st_value != 0x8)
|
||||
return 0;
|
||||
|
||||
if (!PPC64_LOCAL_ENTRY_OFFSET(sym->sym.st_other))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
static int is_localentry_sym(struct symbol *sym)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* When compiling with -ffunction-sections and -fdata-sections, almost every
|
||||
* symbol gets its own dedicated section. We call such symbols "bundled"
|
||||
* symbols. They're indicated by "sym->sec->sym == sym".
|
||||
*/
|
||||
static void kpatch_bundle_symbols(struct kpatch_elf *kelf)
|
||||
{
|
||||
struct symbol *sym;
|
||||
|
||||
list_for_each_entry(sym, &kelf->symbols, list) {
|
||||
if (is_bundleable(sym)) {
|
||||
if (sym->sym.st_value != 0 && !is_localentry_sym(sym)) {
|
||||
ERROR("symbol %s at offset %lu within section %s, expected 0",
|
||||
sym->name, sym->sym.st_value,
|
||||
sym->sec->name);
|
||||
}
|
||||
|
||||
sym->sec->sym = sym;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function detects whether the given symbol is a "special" static local
|
||||
* variable (for lack of a better term).
|
||||
@ -2713,6 +2809,9 @@ int main(int argc, char *argv[])
|
||||
kelf_base = kpatch_elf_open(arguments.args[0]);
|
||||
kelf_patched = kpatch_elf_open(arguments.args[1]);
|
||||
|
||||
kpatch_bundle_symbols(kelf_base);
|
||||
kpatch_bundle_symbols(kelf_patched);
|
||||
|
||||
kpatch_compare_elf_headers(kelf_base->elf, kelf_patched->elf);
|
||||
kpatch_check_program_headers(kelf_base->elf);
|
||||
kpatch_check_program_headers(kelf_patched->elf);
|
||||
|
@ -155,9 +155,9 @@ static struct section *find_or_add_klp_relasec(struct kpatch_elf *kelf,
|
||||
static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section *krelasec,
|
||||
struct section *ksymsec, char *strings)
|
||||
{
|
||||
struct section *base, *klp_relasec;
|
||||
struct section *klp_relasec;
|
||||
struct kpatch_relocation *krelas;
|
||||
struct symbol *sym;
|
||||
struct symbol *sym, *dest;
|
||||
struct rela *rela;
|
||||
char *objname;
|
||||
int nr, index, offset, toc_offset;
|
||||
@ -207,9 +207,7 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section
|
||||
*/
|
||||
toc_offset = rela->addend;
|
||||
|
||||
base = rela->sym->sec;
|
||||
if (!base)
|
||||
ERROR("base sec of krela not found");
|
||||
dest = rela->sym;
|
||||
|
||||
/* Get the name of the object the rela belongs to */
|
||||
rela = find_rela_by_offset(krelasec->rela,
|
||||
@ -232,8 +230,8 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section
|
||||
if (!sym)
|
||||
ERROR("error finding or adding ksym to symtab");
|
||||
|
||||
/* Create (or find) the .klp.rela. section for this base sec and object */
|
||||
klp_relasec = find_or_add_klp_relasec(kelf, base, objname);
|
||||
/* Create (or find) the .klp.rela. section for this dest sec and object */
|
||||
klp_relasec = find_or_add_klp_relasec(kelf, dest->sec, objname);
|
||||
if (!klp_relasec)
|
||||
ERROR("error finding or adding klp relasec");
|
||||
|
||||
@ -241,10 +239,10 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section
|
||||
ALLOC_LINK(rela, &klp_relasec->relas);
|
||||
rela->sym = sym;
|
||||
rela->type = krelas[index].type;
|
||||
if (!strcmp(base->name, ".toc"))
|
||||
if (!strcmp(dest->sec->name, ".toc"))
|
||||
rela->offset = toc_offset;
|
||||
else
|
||||
rela->offset = krelas[index].offset;
|
||||
rela->offset = krelas[index].offset + dest->sym.st_value;
|
||||
|
||||
/*
|
||||
* GCC 6+ adds 0x8 to the offset of every local function entry
|
||||
@ -253,7 +251,7 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section
|
||||
* local function becomes global, we don't want to skip the
|
||||
* .toc setup anymore.
|
||||
*/
|
||||
if (!strcmp(base->name, ".toc") &&
|
||||
if (!strcmp(dest->sec->name, ".toc") &&
|
||||
rela->sym->type == STT_FUNC && rela->sym->bind == STB_LOCAL) {
|
||||
rela->addend = 0;
|
||||
} else {
|
||||
|
@ -43,7 +43,8 @@ static void create_dynamic_rela_sections(struct kpatch_elf *kelf, struct section
|
||||
struct kpatch_patch_dynrela *dynrelas;
|
||||
struct kpatch_relocation *krelas;
|
||||
struct kpatch_symbol *ksym, *ksyms;
|
||||
struct section *base, *dynsec;
|
||||
struct section *dynsec;
|
||||
struct symbol *sym;
|
||||
struct rela *rela;
|
||||
int index, nr, offset, dest_offset, objname_offset, name_offset;
|
||||
|
||||
@ -58,19 +59,16 @@ static void create_dynamic_rela_sections(struct kpatch_elf *kelf, struct section
|
||||
offset = index * sizeof(*krelas);
|
||||
|
||||
/*
|
||||
* To fill in each dynrela entry, find base section (dest),
|
||||
* To fill in each dynrela entry, find dest location,
|
||||
* objname offset, ksym, and symbol name offset
|
||||
*/
|
||||
|
||||
/* Get base section */
|
||||
/* Get dest location */
|
||||
rela = find_rela_by_offset(krelasec->rela,
|
||||
offset + offsetof(struct kpatch_relocation, dest));
|
||||
if (!rela)
|
||||
ERROR("find_rela_by_offset");
|
||||
|
||||
base = rela->sym->sec;
|
||||
if (!base)
|
||||
ERROR("base sec of krela not found");
|
||||
sym = rela->sym;
|
||||
dest_offset = rela->addend;
|
||||
|
||||
/* Get objname offset */
|
||||
@ -103,7 +101,7 @@ static void create_dynamic_rela_sections(struct kpatch_elf *kelf, struct section
|
||||
|
||||
/* dest */
|
||||
ALLOC_LINK(rela, &dynsec->rela->relas);
|
||||
rela->sym = base->sym ? base->sym : base->secsym;
|
||||
rela->sym = sym;
|
||||
rela->type = R_X86_64_64;
|
||||
rela->addend = dest_offset;
|
||||
rela->offset = index * sizeof(*dynrelas);
|
||||
|
@ -76,46 +76,6 @@ int is_debug_section(struct section *sec)
|
||||
!strncmp(name, ".eh_frame", 9);
|
||||
}
|
||||
|
||||
#ifdef __powerpc__
|
||||
|
||||
/* Symbol st_others value for powerpc */
|
||||
#define STO_PPC64_LOCAL_BIT 5
|
||||
#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT)
|
||||
#define PPC64_LOCAL_ENTRY_OFFSET(other) \
|
||||
(((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2)
|
||||
|
||||
/*
|
||||
* On ppc64le, the function prologue generated by GCC 6+ has the sequence:
|
||||
*
|
||||
* .globl my_func
|
||||
* .type my_func, @function
|
||||
* .quad .TOC.-my_func
|
||||
* my_func:
|
||||
* .reloc ., R_PPC64_ENTRY ; optional
|
||||
* ld r2,-8(r12)
|
||||
* add r2,r2,r12
|
||||
* .localentry my_func, .-my_func
|
||||
*
|
||||
* my_func is the global entry point, which, when called, sets up the TOC.
|
||||
* .localentry is the local entry point, for calls to the function from within
|
||||
* the object file. The local entry point is 8 bytes after the global entry
|
||||
* point.
|
||||
*/
|
||||
static int is_localentry_sym(struct symbol *sym)
|
||||
{
|
||||
if (sym->type != STT_FUNC || sym->sym.st_shndx == SHN_UNDEF)
|
||||
return 0;
|
||||
|
||||
if (sym->sym.st_value != 0x8)
|
||||
return 0;
|
||||
|
||||
if (!PPC64_LOCAL_ENTRY_OFFSET(sym->sym.st_other))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct section *find_section_by_index(struct list_head *list, unsigned int index)
|
||||
{
|
||||
struct section *sec;
|
||||
@ -299,36 +259,6 @@ void kpatch_create_section_list(struct kpatch_elf *kelf)
|
||||
ERROR("expected NULL");
|
||||
}
|
||||
|
||||
static int is_bundleable(struct symbol *sym)
|
||||
{
|
||||
if (sym->type == STT_FUNC &&
|
||||
!strncmp(sym->sec->name, ".text.",6) &&
|
||||
!strcmp(sym->sec->name + 6, sym->name))
|
||||
return 1;
|
||||
|
||||
if (sym->type == STT_FUNC &&
|
||||
!strncmp(sym->sec->name, ".text.unlikely.",15) &&
|
||||
!strcmp(sym->sec->name + 15, sym->name))
|
||||
return 1;
|
||||
|
||||
if (sym->type == STT_OBJECT &&
|
||||
!strncmp(sym->sec->name, ".data.",6) &&
|
||||
!strcmp(sym->sec->name + 6, sym->name))
|
||||
return 1;
|
||||
|
||||
if (sym->type == STT_OBJECT &&
|
||||
!strncmp(sym->sec->name, ".rodata.",8) &&
|
||||
!strcmp(sym->sec->name + 8, sym->name))
|
||||
return 1;
|
||||
|
||||
if (sym->type == STT_OBJECT &&
|
||||
!strncmp(sym->sec->name, ".bss.",5) &&
|
||||
!strcmp(sym->sec->name + 5, sym->name))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kpatch_create_symbol_list(struct kpatch_elf *kelf)
|
||||
{
|
||||
struct section *symtab;
|
||||
@ -367,18 +297,7 @@ void kpatch_create_symbol_list(struct kpatch_elf *kelf)
|
||||
ERROR("couldn't find section for symbol %s\n",
|
||||
sym->name);
|
||||
|
||||
if (is_bundleable(sym)) {
|
||||
if (sym->sym.st_value != 0) {
|
||||
#ifdef __powerpc__
|
||||
if (!is_localentry_sym(sym))
|
||||
#endif
|
||||
ERROR("symbol %s at offset %lu within section %s, expected 0",
|
||||
sym->name, sym->sym.st_value, sym->sec->name);
|
||||
}
|
||||
|
||||
sym->sec->sym = sym;
|
||||
|
||||
} else if (sym->type == STT_SECTION) {
|
||||
if (sym->type == STT_SECTION) {
|
||||
sym->sec->secsym = sym;
|
||||
/* use the section name as the symbol name */
|
||||
sym->name = sym->sec->name;
|
||||
|
Loading…
Reference in New Issue
Block a user