Merge pull request #737 from jpoimboe/bundling

kpatch-build: do symbol/section bundling in create-diff-object
This commit is contained in:
Joe Lawrence 2017-09-21 10:32:19 -04:00 committed by GitHub
commit 10b37a16a4
4 changed files with 114 additions and 100 deletions

View File

@ -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);

View File

@ -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 {

View File

@ -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);

View File

@ -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;