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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*.mod.c
*.swp
*.d
*.so
.tmp_versions
Module.symvers
kpatch-build/lookup
Expand Down
19 changes: 14 additions & 5 deletions kpatch-build/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,31 @@ SOURCES = create-diff-object.c kpatch-elf.c \

ifeq ($(ARCH),x86_64)
SOURCES += insn/insn.c insn/inat.c
INSN = insn/insn.o insn/inat.o
INSN = insn/insn.o insn/inat.o
else ifeq ($(ARCH),ppc64le)
SOURCES += gcc-plugins/ppc64le-plugin.c
PLUGIN = gcc-plugins/ppc64le-plugin.so
TARGETS += $(PLUGIN)
GCC_PLUGINS_DIR := $(shell gcc -print-file-name=plugin)
PLUGIN_CFLAGS = -shared $(CFLAGS) -I$(GCC_PLUGINS_DIR)/include \
-Igcc-plugins -fPIC -fno-rtti -O2 -Wall
endif


all: $(TARGETS)

-include $(SOURCES:.c=.d)

create-diff-object: create-diff-object.o kpatch-elf.o \
lookup.o $(INSN)
create-diff-object: create-diff-object.o kpatch-elf.o lookup.o $(INSN)
create-klp-module: create-klp-module.o kpatch-elf.o
create-kpatch-module: create-kpatch-module.o kpatch-elf.o

$(PLUGIN): gcc-plugins/ppc64le-plugin.c
g++ $(PLUGIN_CFLAGS) $< -o $@

install: all
$(INSTALL) -d $(LIBEXECDIR)
$(INSTALL) $(TARGETS) kpatch-gcc $(LIBEXECDIR)
$(INSTALL) $(TARGETS) kpatch-gcc $(PLUGIN) $(LIBEXECDIR)
$(INSTALL) -d $(BINDIR)
$(INSTALL) kpatch-build $(BINDIR)

Expand All @@ -35,4 +44,4 @@ uninstall:
$(RM) $(BINDIR)/kpatch-build

clean:
$(RM) $(TARGETS) *.o *.d insn/*.o insn/*.d
$(RM) $(TARGETS) *.{o,d} insn/*.{o,d} gcc-plugins/*.{so,d}
34 changes: 15 additions & 19 deletions kpatch-build/create-diff-object.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,21 +135,13 @@ static int is_bundleable(struct symbol *sym)
* the object file. The local entry point is 8 bytes after the global entry
* point.
*/
static int is_localentry_sym(struct symbol *sym)
static int is_gcc6_localentry_bundled_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;
return (PPC64_LOCAL_ENTRY_OFFSET(sym->sym.st_other) &&
sym->sym.st_value == 8);
}
#else
static int is_localentry_sym(struct symbol *sym)
static int is_gcc6_localentry_bundled_sym(struct symbol *sym)
{
return 0;
}
Expand All @@ -166,7 +158,8 @@ static void kpatch_bundle_symbols(struct kpatch_elf *kelf)

list_for_each_entry(sym, &kelf->symbols, list) {
if (is_bundleable(sym)) {
if (sym->sym.st_value != 0 && !is_localentry_sym(sym)) {
if (sym->sym.st_value != 0 &&
!is_gcc6_localentry_bundled_sym(sym)) {
ERROR("symbol %s at offset %lu within section %s, expected 0",
sym->name, sym->sym.st_value,
sym->sec->name);
Expand Down Expand Up @@ -551,7 +544,6 @@ static void kpatch_compare_correlated_symbol(struct symbol *sym)
struct symbol *sym1 = sym, *sym2 = sym->twin;

if (sym1->sym.st_info != sym2->sym.st_info ||
sym1->sym.st_other != sym2->sym.st_other ||
(sym1->sec && !sym2->sec) ||
(sym2->sec && !sym1->sec))
DIFF_FATAL("symbol info mismatch: %s", sym1->name);
Expand Down Expand Up @@ -1121,6 +1113,13 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf)
*/
if (rela->sym->sec && rela->sym->sec->sym) {
rela->sym = rela->sym->sec->sym;

/*
* ppc64le: a GCC 6+ bundled function is at
* offset 8 in its section.
*/
rela->addend -= rela->sym->sym.st_value;

continue;
}

Expand Down Expand Up @@ -2518,16 +2517,13 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
krelas[index].addend = rela->addend;
krelas[index].type = rela->type;
krelas[index].external = external;
krelas[index].offset = rela->offset;

/* add rela to fill in krelas[index].dest field */
ALLOC_LINK(rela2, &krela_sec->rela->relas);
if (sec->base->sym)
rela2->sym = sec->base->sym;
else if (sec->base->secsym)
if (sec->base->secsym)
rela2->sym = sec->base->secsym;
else
ERROR("can't create dynrela for section %s (symbol %s): no bundled section or section symbol",
ERROR("can't create dynrela for section %s (symbol %s): no bundled or section symbol",
sec->name, rela->sym->name);

rela2->type = ABSOLUTE_RELA_TYPE;
Expand Down
70 changes: 11 additions & 59 deletions kpatch-build/create-klp-module.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,56 +160,24 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section
struct symbol *sym, *dest;
struct rela *rela;
char *objname;
int nr, index, offset, toc_offset;
int nr, index, offset, dest_off;

krelas = krelasec->data->d_buf;
nr = krelasec->data->d_size / sizeof(*krelas);

for (index = 0; index < nr; index++) {
offset = index * sizeof(*krelas);

/* Get the base section to which the rela applies */
/* Get the rela dest sym + offset */
rela = find_rela_by_offset(krelasec->rela,
offset + offsetof(struct kpatch_relocation, dest));
if (!rela)
ERROR("find_rela_by_offset");

/*
* Patched file:
* Relocation section '.rela.toc' at offset 0x46358 contains 60 entries:
* Offset Info Type Symbol's Value Symbol's Name + Addend
* 0000000000000000 000001ee00000026 R_PPC64_ADDR64 0000000000000000 jiffies + 0
* 0000000000000008 0000009400000026 R_PPC64_ADDR64 0000000000000000 __tracepoints + 0
* 0000000000000010 000001db00000026 R_PPC64_ADDR64 0000000000000000 __cpu_online_mask + 0
* 0000000000000018 0000009c00000026 R_PPC64_ADDR64 0000000000000000 .data..percpu + 0
* 0000000000000020 000001ac00000026 R_PPC64_ADDR64 0000000000000000 __per_cpu_offset + 0
* 0000000000000028 0000006900000026 R_PPC64_ADDR64 0000000000000000 .rodata.str1.8 + 0
* [...]
*
* Output file:
* Relocation section '.rela.toc' at offset 0x1270 contains 58 entries:
* Offset Info Type Symbol's Value Symbol's Name + Addend
* 0000000000000000 0000000700000026 R_PPC64_ADDR64 0000000000000000 .data + 0
* 0000000000000008 0000003c00000026 R_PPC64_ADDR64 0000000000000000 __kpatch_funcs + 0
* 0000000000000010 0000005300000026 R_PPC64_ADDR64 0000000000000000 kmalloc_caches + 0
* 0000000000000018 0000001100000026 R_PPC64_ADDR64 0000000000000000 .rodata.str1.8 + 0
* 0000000000000020 0000001600000026 R_PPC64_ADDR64 0000000000000000 .bss + 0
* 0000000000000028 0000004200000026 R_PPC64_ADDR64 0000000000000038 __kpatch_funcs_end + 0
* 0000000000000030 0000003400000026 R_PPC64_ADDR64 0000000000000000 __this_module + 0
* 0000000000000038 0000004d00000026 R_PPC64_ADDR64 0000000000000000 jiffies + 0
* 0000000000000048 0000004500000026 R_PPC64_ADDR64 0000000000000000 __cpu_online_mask + 0
* 0000000000000058 0000003900000026 R_PPC64_ADDR64 0000000000000000 __per_cpu_offset + 0
* [...]
*
* On ppc64le, when .o files are linked together, the .toc
* entries might get re-arranged. Capture the new .toc rela
* offset value, which is used below to set the rela->addend.
*/
toc_offset = rela->addend;

dest = rela->sym;
dest_off = rela->addend;

/* Get the name of the object the rela belongs to */
/* Get the name of the object the dest belongs to */
rela = find_rela_by_offset(krelasec->rela,
offset + offsetof(struct kpatch_relocation, objname));
if (!rela)
Expand All @@ -219,44 +187,28 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section
if (!objname)
ERROR("strdup");

/* Get the corresponding .kpatch.symbol entry */
/* Get the .kpatch.symbol entry for the rela src */
rela = find_rela_by_offset(krelasec->rela,
offset + offsetof(struct kpatch_relocation, ksym));
if (!rela)
ERROR("find_rela_by_offset");

/* Create (or find) a real symbol out of the .kpatch.symbol entry */
/* Create (or find) a klp symbol from the rela src entry */
sym = find_or_add_ksym_to_symbols(kelf, ksymsec, strings, rela->addend);
if (!sym)
ERROR("error finding or adding ksym to symtab");

/* Create (or find) the .klp.rela. section for this dest sec and object */
/* Create (or find) the .klp.rela. section for the 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 */
/* Add the klp rela to the .klp.rela. section */
ALLOC_LINK(rela, &klp_relasec->relas);
rela->sym = sym;
rela->offset = dest->sym.st_value + dest_off;
rela->type = krelas[index].type;
if (!strcmp(dest->sec->name, ".toc"))
rela->offset = toc_offset;
else
rela->offset = krelas[index].offset + dest->sym.st_value;

/*
* GCC 6+ adds 0x8 to the offset of every local function entry
* in the .toc section, for avoiding the setup of the toc when
* the function is called locally. But when the previously
* local function becomes global, we don't want to skip the
* .toc setup anymore.
*/
if (!strcmp(dest->sec->name, ".toc") &&
rela->sym->type == STT_FUNC && rela->sym->bind == STB_LOCAL) {
rela->addend = 0;
} else {
rela->addend = krelas[index].addend;
}
rela->sym = sym;
rela->addend = krelas[index].addend;
}
}

Expand Down
Loading