Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions kpatch-build/create-diff-object.c
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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);
Expand Down
18 changes: 8 additions & 10 deletions kpatch-build/create-klp-module.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -232,19 +230,19 @@ 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");

/* Add the rela to the .klp.rela. 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
Expand All @@ -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 {
Expand Down
14 changes: 6 additions & 8 deletions kpatch-build/create-kpatch-module.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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 */
Expand Down Expand Up @@ -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);
Expand Down
83 changes: 1 addition & 82 deletions kpatch-build/kpatch-elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down