Skip to content

Commit

Permalink
Merge pull request #757 from jpoimboe/TODO-ppc-fix
Browse files Browse the repository at this point in the history
Some ppc64le cleanups and fixes
  • Loading branch information
jpoimboe authored Dec 21, 2017
2 parents f4c0f32 + 63a94b8 commit 258ac3f
Show file tree
Hide file tree
Showing 9 changed files with 1,267 additions and 85 deletions.
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

0 comments on commit 258ac3f

Please sign in to comment.