Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F171213565
D59492.id186223.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
35 KB
Referenced Files
None
Subscribers
None
D59492.id186223.diff
View Options
diff --git a/share/examples/kld/Makefile b/share/examples/kld/Makefile
--- a/share/examples/kld/Makefile
+++ b/share/examples/kld/Makefile
@@ -68,6 +68,6 @@
PACKAGE=examples
FILESDIR=${SHAREDIR}/examples/kld
-SUBDIR= cdev dyn_sysctl firmware khelp random_adaptor syscall
+SUBDIR= cdev dyn_sysctl firmware khelp random_adaptor syscall kpatch
.include <bsd.subdir.mk>
diff --git a/share/examples/kld/kpatch/Makefile b/share/examples/kld/kpatch/Makefile
new file mode 100644
--- /dev/null
+++ b/share/examples/kld/kpatch/Makefile
@@ -0,0 +1,6 @@
+PACKAGE=examples
+FILESDIR=${SHAREDIR}/examples/kld/${KMOD}
+SRCS= kpatch.c
+KMOD= kpatch
+
+.include <buildpatch.mk>
diff --git a/share/examples/kld/kpatch/kpatch.c b/share/examples/kld/kpatch/kpatch.c
new file mode 100644
--- /dev/null
+++ b/share/examples/kld/kpatch/kpatch.c
@@ -0,0 +1,87 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+
+#include <buildpatch.h>
+
+#define HELPER(str) \
+ do { \
+ char tmpname[MAXHOSTNAMELEN]; \
+ int error, len; \
+ len = arg2; \
+ KASSERT(len <= sizeof(tmpname), \
+ ("length %d too long for %s", len, __func__)); \
+ strlcpy(tmpname, str, len); \
+ error = sysctl_handle_string(oidp, tmpname, len, req); \
+ return (error); \
+ } while (0)
+
+static int
+patch_sysctl_hostname1(SYSCTL_HANDLER_ARGS)
+{
+ HELPER("PATCHED");
+}
+PATCH_FUNC(test1, patch_sysctl_hostname1,
+ "sysctl_hostname", "kernel", "kern_mib.c");
+
+static int
+patch_sysctl_hostname2(SYSCTL_HANDLER_ARGS)
+{
+ HELPER("HELLO");
+}
+PATCH_FUNC(test2, patch_sysctl_hostname2,
+ "sysctl_hostname", "kernel", "kern_mib.c");
+
+static void
+patch_test3(void)
+{
+ panic("Not supposed to work\n");
+}
+PATCH_FUNC(test3, patch_test3,
+ "sys___sysctl", "kernel", "kern_sysctl.c");
+
+static int
+pre_patch1(void)
+{
+ printf("Patching test1 (pre)\n");
+ return (0);
+}
+
+static void
+post_patch1(int x)
+{
+ printf("Patching test1 (post) -> %d\n", x);
+}
+
+PATCH_DECLARE_FULL(test1, 0,
+ pre_patch1, post_patch1,
+ pre_patch1, post_patch1);
+
+PATCH_DECLARE(test2, 0);
+PATCH_DECLARE(test3, 0);
+
+/*
+ * Example to see if custom relocs work
+ */
+extern void vga_suspend(void *);
+PATCH_RELOC(vga_suspend, "vga_suspend", "kernel", "vga_isa.c");
+
+extern int loadcnt;
+PATCH_RELOC(loadcnt, "loadcnt", "kernel", "kern_linker.c");
+
+extern void linker_init(void *arg);
+PATCH_RELOC(linker_init, "linker_init", "kernel", "kern_linker.c");
+
+void *rel = vga_suspend;
+
+static void
+kpatch_sysinit(void *dummy __unused)
+{
+ printf("vga_suspend was relocated to %p\n", rel);
+ printf("loadcnt was relocated to %p\n", &loadcnt);
+ printf("linker_init was relocated to %p\n", linker_init);
+}
+
+SYSINIT(kpatch_mod, SI_SUB_KLD, SI_ORDER_ANY, kpatch_sysinit, NULL);
diff --git a/share/mk/Makefile b/share/mk/Makefile
--- a/share/mk/Makefile
+++ b/share/mk/Makefile
@@ -59,6 +59,7 @@
bsd.sys.mk \
bsd.sysdir.mk \
bsd.test.mk \
+ buildpatch.mk \
dirdeps.mk \
dirdeps-options.mk \
dirdeps-targets.mk \
diff --git a/share/mk/buildpatch.mk b/share/mk/buildpatch.mk
new file mode 100644
--- /dev/null
+++ b/share/mk/buildpatch.mk
@@ -0,0 +1,24 @@
+.if !defined(KERNFILE)
+.if defined(KERNBUILDDIR)
+KERNFILE= ${KERNBUILDDIR}/kernel.full
+.else
+KERNFILE= /usr/obj/usr/src/${MACHINE}.${MACHINE_ARCH}/sys/GENERIC/kernel.full
+.endif
+.endif
+
+BUILDPATCH?= buildpatch
+
+CFLAGS+= -I${SYSDIR}/../tools/tools/buildpatch
+
+.include <bsd.kmod.mk>
+
+all: .buildpatch_done
+load: .buildpatch_done
+realinstall: .buildpatch_done
+
+.buildpatch_done: ${KMOD}.ko
+ @mv ${KMOD}.ko ${KMOD}.pre.ko
+ @${BUILDPATCH} ${KMOD}.pre.ko ${KMOD}.ko ${KERNFILE}
+ @touch ${.TARGET}
+
+CLEANFILES+= ${KMOD}.pre.ko .buildpatch_done
diff --git a/tools/tools/buildpatch/Makefile b/tools/tools/buildpatch/Makefile
new file mode 100644
--- /dev/null
+++ b/tools/tools/buildpatch/Makefile
@@ -0,0 +1,9 @@
+PROG= buildpatch
+MAN=
+BINDIR?= /usr/sbin
+SRCS= buildpatch.c
+LIBADD= util elf
+
+CFLAGS+= -I${SRCTOP}/sys
+
+.include <bsd.prog.mk>
diff --git a/tools/tools/buildpatch/buildpatch.h b/tools/tools/buildpatch/buildpatch.h
new file mode 100644
--- /dev/null
+++ b/tools/tools/buildpatch/buildpatch.h
@@ -0,0 +1,74 @@
+#ifndef BUILDPATCH_H
+#define BUILDPATCH_H
+
+#include <sys/types.h>
+
+#define PATCH_USING_SYMPOS 0x1
+
+struct set_metadata {
+ const char *name;
+ long flags;
+ void *pre_patch;
+ void *post_patch;
+ void *pre_unpatch;
+ void *post_unpatch;
+};
+
+struct func_metadata {
+ const char *patch;
+ const char *new_sym;
+ void *new_addr;
+ const char *old_sym;
+ const char *old_obj;
+ union {
+ const char *old_file;
+ long sympos;
+ } uniquifier;
+ long flags;
+};
+
+struct reloc_metadata {
+ const char *local_sym;
+ const char *real_sym;
+ const char *real_obj;
+ union {
+ const char *real_file;
+ long sympos;
+ } uniquifier;
+ long flags;
+};
+
+#define PATCH_SET_SECTION ".buildpatch.sets"
+#define PATCH_FUNC_SECTION ".buildpatch.funcs"
+#define PATCH_RELOC_SECTION ".buildpatch.relocs"
+
+#define PATCH_CONCAT(name, uniquifier) __patch_##name##_##uniquifier
+
+#define PATCH_DECLARE_FULL(name, flags, pre_p, post_p, pre_u, post_u) \
+ __used __section(PATCH_SET_SECTION) \
+ static struct set_metadata PATCH_CONCAT(name, info) = { \
+ #name, flags, pre_p, post_p, pre_u, post_u \
+ };
+
+#define PATCH_DECLARE(name, flags) \
+ PATCH_DECLARE_FULL(name, flags, NULL, NULL, NULL, NULL)
+
+#define PATCH_FUNC_FULL(patch, new_sym, old_sym, old_obj, uniquifier, flags) \
+ __used __section(PATCH_FUNC_SECTION) \
+ static struct func_metadata PATCH_CONCAT(patch, func__##new_sym) = { \
+ #patch, #new_sym, new_sym, old_sym, old_obj, { uniquifier }, flags \
+ };
+
+#define PATCH_FUNC(patch, new_sym, old_sym, old_obj, old_file) \
+ PATCH_FUNC_FULL(patch, new_sym, old_sym, old_obj, old_file, 0)
+
+#define PATCH_RELOC_FULL(local, sym, obj, uniquifier, flags) \
+ __used __section(PATCH_RELOC_SECTION) \
+ static struct reloc_metadata PATCH_CONCAT(, reloc__##local) = { \
+ #local, sym, obj, { uniquifier }, flags \
+ };
+
+#define PATCH_RELOC(local, sym, obj, file) \
+ PATCH_RELOC_FULL(local, sym, obj, file, 0)
+
+#endif
diff --git a/tools/tools/buildpatch/buildpatch.c b/tools/tools/buildpatch/buildpatch.c
new file mode 100644
--- /dev/null
+++ b/tools/tools/buildpatch/buildpatch.c
@@ -0,0 +1,1089 @@
+#include <sys/types.h>
+#include <sys/kpatch.h>
+#include <sys/elf_common.h>
+
+#include <err.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <gelf.h>
+#include <libelf.h>
+#include <string.h>
+#include <stddef.h>
+
+#include "buildpatch.h"
+
+static struct {
+ int fd;
+ Elf *elf;
+ Elf_Scn *symtab_scn;
+ GElf_Shdr symtab_shdr;
+ Elf_Data *symtab_data;
+ int symtab_count;
+ size_t build_id_len;
+ uint8_t build_id[32];
+} kern;
+
+static struct {
+ int fd;
+ Elf *elf;
+ Elf_Scn *symtab_scn;
+ GElf_Shdr symtab_shdr;
+ Elf_Data *symtab_data;
+ int symtab_count;
+ long shstrndx;
+ Elf_Scn *shstr_scn;
+ Elf_Data *shstr_data;
+ Elf_Scn *relocs_scn;
+ Elf_Scn *relocs_rela;
+ Elf_Scn *funcs_scn;
+ Elf_Scn *funcs_rela;
+ Elf_Scn *sets_scn;
+ Elf_Scn *sets_rela;
+ struct set_metadata *sets_md;
+ int sets_count;
+ struct func_metadata *funcs_md;
+ int funcs_count;
+ struct reloc_metadata *relocs_md;
+ int relocs_count;
+} in_patch;
+
+static struct {
+ int fd;
+ Elf *elf;
+ unsigned *idx_map;
+ size_t idx_size;
+ unsigned last_ndx;
+} out_patch;
+
+static void
+extract_build_id(void)
+{
+ Elf_Scn *scn;
+ GElf_Shdr shdr;
+ Elf_Data *data;
+ uint32_t *note_hdr;
+ size_t offset;
+ const char *name;
+ const uint8_t *desc;
+ uint32_t namesz, descsz, type;
+
+ scn = NULL;
+ while ((scn = elf_nextscn(kern.elf, scn)) != NULL) {
+ if (gelf_getshdr(scn, &shdr) == NULL)
+ continue;
+
+ if (shdr.sh_type != SHT_NOTE)
+ continue;
+
+ data = elf_getdata(scn, NULL);
+ if (data == NULL)
+ continue;
+
+ offset = 0;
+ while (offset + 12 <= data->d_size) {
+ note_hdr = (void *)((char *)data->d_buf + offset);
+ namesz = note_hdr[0];
+ descsz = note_hdr[1];
+ type = note_hdr[2];
+ name = (const char *)¬e_hdr[3];
+ desc = (const uint8_t *)name + ((namesz + 3) & ~3);
+
+ if (offset + 12 + ((namesz + 3) & ~3) + ((descsz + 3) & ~3) > data->d_size)
+ break;
+
+ /* NT_GNU_BUILD_ID is 3, and the vendor string is "GNU\0" */
+ if (type == 3 && namesz == 4 && memcmp(name, "GNU", 4) == 0) {
+ kern.build_id_len = descsz;
+ if (kern.build_id_len > sizeof(kern.build_id))
+ kern.build_id_len = sizeof(kern.build_id);
+
+ memcpy(kern.build_id, desc, kern.build_id_len);
+ printf("Extracted kernel build-id (%zu bytes)\n", kern.build_id_len);
+ return;
+ }
+
+ offset += 12 + ((namesz + 3) & ~3) + ((descsz + 3) & ~3);
+ }
+ }
+
+ errx(1, "No build-id found in kernel");
+}
+
+static void
+open_kernel(const char *path)
+{
+ Elf_Scn *scn;
+ GElf_Shdr shdr;
+
+ kern.fd = open(path, O_RDONLY);
+ if (kern.fd < 0)
+ errx(1, "Failed to open %s", path);
+
+ kern.elf = elf_begin(kern.fd, ELF_C_READ, NULL);
+ if (kern.elf == NULL)
+ errx(1, "Failed to parse elf: %s", elf_errmsg(-1));
+
+ if (elf_kind(kern.elf) != ELF_K_ELF)
+ errx(1, "%s is not an ELF object", path);
+
+ for (scn = NULL; (scn = elf_nextscn(kern.elf, scn)) != NULL; ) {
+ if (gelf_getshdr(scn, &shdr) == NULL)
+ errx(1, "gelf_getshdr failed: %s", elf_errmsg(-1));
+
+ if (shdr.sh_type == SHT_SYMTAB)
+ break;
+ }
+
+ if (scn == NULL)
+ errx(1, "No .symtab section found in %s", path);
+
+ if ((kern.symtab_data = elf_getdata(scn, NULL)) == NULL)
+ errx(1, "elf_getdata failed: %s", elf_errmsg(-1));
+
+ kern.symtab_scn = scn;
+ kern.symtab_shdr = shdr;
+ kern.symtab_count = shdr.sh_size / shdr.sh_entsize;
+
+ extract_build_id();
+}
+
+static void
+close_kernel(void)
+{
+ elf_end(kern.elf);
+ close(kern.fd);
+}
+
+static int
+find_kernel_symbol(const char *func, const char *srcfile)
+{
+ int i, n;
+ GElf_Sym sym;
+ const char *name, *curfile;
+
+ curfile = "";
+ for (i = 0, n = 0; i < kern.symtab_count; i++) {
+ if (gelf_getsym(kern.symtab_data, i, &sym) == NULL)
+ errx(1, "gelf_getsym failed: %s", elf_errmsg(-1));
+
+ name = elf_strptr(kern.elf, kern.symtab_shdr.sh_link, sym.st_name);
+ if (name == NULL || *name == '\0')
+ continue;
+
+ if (GELF_ST_TYPE(sym.st_info) == STT_FILE) {
+ curfile = name;
+ continue;
+ }
+
+ if (GELF_ST_TYPE(sym.st_info) == STT_FUNC) {
+ if (strcmp(name, func) != 0)
+ continue;
+
+ if (GELF_ST_BIND(sym.st_info) == STB_GLOBAL ||
+ srcfile[0] == '\0' || !strcmp(curfile, srcfile)) {
+ return (n);
+ }
+
+ n++;
+ printf("Homonym of %s found at index %d\n", func, n);
+ }
+ }
+
+ return (-1);
+}
+
+static void
+open_patch(const char *in_path, const char *out_path)
+{
+ GElf_Ehdr ehdr;
+
+ memset(&in_patch, 0, sizeof(in_patch));
+ memset(&out_patch, 0, sizeof(out_patch));
+
+ in_patch.fd = open(in_path, O_RDONLY);
+ if (in_patch.fd < 0)
+ errx(1, "Failed to open %s", in_path);
+
+ in_patch.elf = elf_begin(in_patch.fd, ELF_C_READ, NULL);
+ if (in_patch.elf == NULL)
+ errx(1, "Failed to parse elf: %s", elf_errmsg(-1));
+
+ out_patch.fd = open(out_path, O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if (out_patch.fd < 0)
+ errx(1, "Failed to open %s", out_path);
+
+ out_patch.elf = elf_begin(out_patch.fd, ELF_C_WRITE, NULL);
+ if (out_patch.elf == NULL)
+ errx(1, "Failed to parse elf: %s", elf_errmsg(-1));
+
+ /* Copy ELF Header */
+ gelf_getehdr(in_patch.elf, &ehdr);
+ gelf_newehdr(out_patch.elf, gelf_getclass(in_patch.elf));
+ gelf_update_ehdr(out_patch.elf, &ehdr);
+
+ elf_getshdrstrndx(in_patch.elf, &in_patch.shstrndx);
+ in_patch.shstr_scn = elf_getscn(in_patch.elf, in_patch.shstrndx);
+ in_patch.shstr_data = elf_getdata(in_patch.shstr_scn, NULL);
+
+ out_patch.idx_size = 1024;
+ out_patch.idx_map = calloc(out_patch.idx_size, sizeof(unsigned));
+}
+
+static int
+check_special_section(const char *name, Elf_Scn *scn)
+{
+ if (!strcmp(name, PATCH_RELOC_SECTION)) {
+ in_patch.relocs_scn = scn;
+ return (1);
+ }
+
+ if (!strcmp(name, ".rela" PATCH_RELOC_SECTION)) {
+ in_patch.relocs_rela = scn;
+ return (1);
+ }
+
+ if (!strcmp(name, PATCH_FUNC_SECTION)) {
+ in_patch.funcs_scn = scn;
+ return (1);
+ }
+
+ if (!strcmp(name, ".rela" PATCH_FUNC_SECTION)) {
+ in_patch.funcs_rela = scn;
+ return (1);
+ }
+
+ if (!strcmp(name, PATCH_SET_SECTION)) {
+ in_patch.sets_scn = scn;
+ return (1);
+ }
+
+ if (!strcmp(name, ".rela" PATCH_SET_SECTION)) {
+ in_patch.sets_rela = scn;
+ return (1);
+ }
+
+ return (0);
+}
+
+static inline void
+index_map_set(unsigned ndx_in, unsigned ndx_out)
+{
+ if (ndx_in > out_patch.idx_size) {
+ out_patch.idx_size++;
+ out_patch.idx_map = realloc(out_patch.idx_map, sizeof(unsigned) * out_patch.idx_size);
+ }
+
+ out_patch.idx_map[ndx_in] = ndx_out;
+}
+
+static inline unsigned
+index_map_get(unsigned ndx_in)
+{
+ if (ndx_in >= out_patch.idx_size)
+ return 0;
+
+ return out_patch.idx_map[ndx_in];
+}
+
+static size_t
+add_shstrtab_string(const char *str)
+{
+ Elf_Scn *scn_out;
+ GElf_Shdr shdr;
+ Elf_Data *data_out;
+ size_t str_len, offset;
+ char *new_str;
+
+ scn_out = elf_getscn(out_patch.elf, out_patch.idx_map[in_patch.shstrndx]);
+ if (gelf_getshdr(scn_out, &shdr) == NULL)
+ errx(1, "gelf_getshdr failed: %s", elf_errmsg(-1));
+
+ offset = shdr.sh_size;
+ str_len = strlen(str) + 1;
+ new_str = strdup(str);
+
+ data_out = elf_newdata(scn_out);
+ if (data_out == NULL)
+ errx(1, "elf_newdata failed: %s", elf_errmsg(-1));
+
+ data_out->d_align = 1;
+ data_out->d_buf = new_str;
+ data_out->d_size = str_len;
+ data_out->d_type = ELF_T_BYTE;
+ data_out->d_version = EV_CURRENT;
+
+ shdr.sh_size += str_len;
+ gelf_update_shdr(scn_out, &shdr);
+
+ return (offset);
+}
+
+static size_t
+add_strtab_string(const char *str)
+{
+ Elf_Scn *symtab_scn, *strtab_scn;
+ GElf_Shdr symtab_shdr, strtab_shdr;
+ Elf_Data *data_out;
+ size_t str_len, offset;
+ char *new_str;
+
+ symtab_scn = elf_getscn(out_patch.elf, index_map_get(elf_ndxscn(in_patch.symtab_scn)));
+ gelf_getshdr(symtab_scn, &symtab_shdr);
+
+ strtab_scn = elf_getscn(out_patch.elf, index_map_get(symtab_shdr.sh_link));
+ gelf_getshdr(strtab_scn, &strtab_shdr);
+
+ offset = strtab_shdr.sh_size;
+ str_len = strlen(str) + 1;
+ new_str = strdup(str);
+
+ data_out = elf_newdata(strtab_scn);
+ data_out->d_align = 1;
+ data_out->d_buf = new_str;
+ data_out->d_size = str_len;
+ data_out->d_type = ELF_T_BYTE;
+ data_out->d_version = EV_CURRENT;
+
+ strtab_shdr.sh_size += str_len;
+ gelf_update_shdr(strtab_scn, &strtab_shdr);
+
+ return (offset);
+}
+
+static unsigned
+add_symbol(Elf_Scn *symtab_scn, const char *name, unsigned shndx, size_t size)
+{
+ GElf_Shdr shdr;
+ GElf_Sym sym;
+ int count;
+ size_t name_idx;
+
+ gelf_getshdr(symtab_scn, &shdr);
+ count = shdr.sh_size / shdr.sh_entsize;
+
+ name_idx = add_strtab_string(name);
+
+ memset(&sym, 0, sizeof(sym));
+ sym.st_name = name_idx;
+ sym.st_info = GELF_ST_INFO(STB_GLOBAL, STT_OBJECT);
+ sym.st_shndx = shndx;
+ sym.st_value = 0;
+ sym.st_size = size;
+
+ Elf_Data *new_data = elf_newdata(symtab_scn);
+ GElf_Sym *sym_buf = calloc(1, sizeof(GElf_Sym));
+ *sym_buf = sym;
+
+ new_data->d_align = 8;
+ new_data->d_buf = sym_buf;
+ new_data->d_size = sizeof(GElf_Sym);
+ new_data->d_type = ELF_T_SYM;
+ new_data->d_version = EV_CURRENT;
+
+ shdr.sh_size += sizeof(GElf_Sym);
+ gelf_update_shdr(symtab_scn, &shdr);
+
+ return (count);
+}
+
+static void
+copy_patch_sections(void)
+{
+ Elf_Scn *scn_in, *scn_out;
+ unsigned ndx_in, ndx_out;
+ Elf_Data *data_in, *data_out;
+ GElf_Shdr shdr;
+ const char *name;
+
+ scn_in = NULL;
+ while ((scn_in = elf_nextscn(in_patch.elf, scn_in)) != NULL) {
+ ndx_in = elf_ndxscn(scn_in);
+
+ gelf_getshdr(scn_in, &shdr);
+ name = elf_strptr(in_patch.elf, in_patch.shstrndx, shdr.sh_name);
+
+ if (check_special_section(name, scn_in)) {
+ printf("Dropping section %s\n", name);
+ index_map_set(ndx_in, 0);
+ continue;
+ }
+
+ scn_out = elf_newscn(out_patch.elf);
+ ndx_out = elf_ndxscn(scn_out);
+ index_map_set(ndx_in, ndx_out);
+ printf("Copying section %s\n", name);
+
+ if (ndx_out > out_patch.last_ndx)
+ out_patch.last_ndx = ndx_out;
+
+ data_in = NULL;
+ while ((data_in = elf_getdata(scn_in, data_in)) != NULL) {
+ data_out = elf_newdata(scn_out);
+ *data_out = *data_in;
+ }
+
+ if (shdr.sh_type == SHT_SYMTAB) {
+ in_patch.symtab_scn = scn_in;
+ in_patch.symtab_shdr = shdr;
+ in_patch.symtab_data = elf_getdata(scn_in, NULL);
+ in_patch.symtab_count = shdr.sh_size / shdr.sh_entsize;
+ }
+
+ gelf_update_shdr(scn_out, &shdr);
+ }
+}
+
+struct reloc_string {
+ const char *str;
+ GElf_Rela rela;
+};
+
+#define RELSTR_STR(rstr) (((const struct reloc_string *)(const void *)rstr)->str)
+#define RELSTR_RELA(rstr) (((const struct reloc_string *)(const void *)rstr)->rela)
+
+static const char *
+resolve_reloc_string(GElf_Rela rela)
+{
+ GElf_Sym sym;
+ Elf_Scn *scn;
+ Elf_Data *data;
+ struct reloc_string *rstr;
+
+ if (gelf_getsym(in_patch.symtab_data, GELF_R_SYM(rela.r_info), &sym) == NULL)
+ errx(1, "gelf_getsym failed: %s", elf_errmsg(-1));
+
+ scn = elf_getscn(in_patch.elf, sym.st_shndx);
+ if (scn == NULL)
+ return (NULL);
+
+ data = elf_getdata(scn, NULL);
+ if (data == NULL)
+ return (NULL);
+
+ rstr = malloc(sizeof(struct reloc_string));
+ rstr->rela = rela;
+ rstr->str = (const char *)data->d_buf + sym.st_value + rela.r_addend;
+
+ return (const char *)rstr;
+}
+
+static void
+parse_patch_sets(void)
+{
+ Elf_Data *data, *rela_data;
+ GElf_Shdr rela_shdr;
+ GElf_Rela rela;
+ int i, nrelas;
+ const char *str;
+
+ data = elf_getdata(in_patch.sets_scn, NULL);
+ rela_data = elf_getdata(in_patch.sets_rela, NULL);
+ gelf_getshdr(in_patch.sets_rela, &rela_shdr);
+
+ in_patch.sets_count = data->d_size / sizeof(struct set_metadata);
+ in_patch.sets_md = malloc(data->d_size);
+ memcpy(in_patch.sets_md, data->d_buf, data->d_size);
+
+ nrelas = rela_shdr.sh_size / rela_shdr.sh_entsize;
+ for (i = 0; i < nrelas; i++) {
+ gelf_getrela(rela_data, i, &rela);
+ str = resolve_reloc_string(rela);
+ if (str == NULL)
+ continue;
+
+ memcpy((char *)in_patch.sets_md + rela.r_offset, &str, sizeof(const char *));
+ }
+
+ for (i = 0; i < in_patch.sets_count; i++) {
+ printf("Parsed patch set:\n");
+ printf("\tName: %s\n", RELSTR_STR(in_patch.sets_md[i].name));
+ printf("\tFlags: %lx\n", in_patch.sets_md[i].flags);
+ printf("\tPre patch: %s\n", in_patch.sets_md[i].pre_patch ? "yes" : "no");
+ printf("\tPost patch: %s\n", in_patch.sets_md[i].post_patch ? "yes" : "no");
+ printf("\tPre unpatch: %s\n", in_patch.sets_md[i].pre_unpatch ? "yes" : "no");
+ printf("\tPost unpatch: %s\n", in_patch.sets_md[i].post_unpatch ? "yes" : "no");
+ }
+}
+
+static void
+parse_patch_funcs(void)
+{
+ Elf_Data *data, *rela_data;
+ GElf_Shdr rela_shdr;
+ GElf_Rela rela;
+ int i, nrelas;
+ const char *str;
+
+ data = elf_getdata(in_patch.funcs_scn, NULL);
+ rela_data = elf_getdata(in_patch.funcs_rela, NULL);
+ gelf_getshdr(in_patch.funcs_rela, &rela_shdr);
+
+ in_patch.funcs_count = data->d_size / sizeof(struct func_metadata);
+ in_patch.funcs_md = malloc(data->d_size);
+ memcpy(in_patch.funcs_md, data->d_buf, data->d_size);
+
+ nrelas = rela_shdr.sh_size / rela_shdr.sh_entsize;
+ for (i = 0; i < nrelas; i++) {
+ gelf_getrela(rela_data, i, &rela);
+ str = resolve_reloc_string(rela);
+ if (str == NULL)
+ continue;
+
+ memcpy((char *)in_patch.funcs_md + rela.r_offset, &str, sizeof(const char *));
+ }
+
+ for (i = 0; i < in_patch.funcs_count; i++) {
+ printf("Parsed patch func:\n");
+ printf("\tPatch: %s\n", RELSTR_STR(in_patch.funcs_md[i].patch));
+ printf("\tNew: %s\n", RELSTR_STR(in_patch.funcs_md[i].new_sym));
+ printf("\tOld: %s\n", RELSTR_STR(in_patch.funcs_md[i].old_sym));
+ printf("\tObj: %s\n", RELSTR_STR(in_patch.funcs_md[i].old_obj));
+ if (in_patch.funcs_md[i].flags & PATCH_USING_SYMPOS) {
+ printf("\tSympos: %ld\n", in_patch.funcs_md[i].uniquifier.sympos);
+ } else {
+ printf("\tFile: %s\n", RELSTR_STR(in_patch.funcs_md[i].uniquifier.old_file));
+ }
+ printf("\tFlags: %lx\n", in_patch.funcs_md[i].flags);
+ }
+}
+
+static void
+parse_patch_relocs(void)
+{
+ Elf_Data *data, *rela_data;
+ GElf_Shdr rela_shdr;
+ GElf_Rela rela;
+ int i, nrelas;
+ const char *str;
+
+ data = elf_getdata(in_patch.relocs_scn, NULL);
+ rela_data = elf_getdata(in_patch.relocs_rela, NULL);
+ gelf_getshdr(in_patch.relocs_rela, &rela_shdr);
+
+ in_patch.relocs_count = data->d_size / sizeof(struct reloc_metadata);
+ in_patch.relocs_md = malloc(data->d_size);
+ memcpy(in_patch.relocs_md, data->d_buf, data->d_size);
+
+ nrelas = rela_shdr.sh_size / rela_shdr.sh_entsize;
+ for (i = 0; i < nrelas; i++) {
+ gelf_getrela(rela_data, i, &rela);
+ str = resolve_reloc_string(rela);
+ if (str == NULL)
+ continue;
+
+ memcpy((char *)in_patch.relocs_md + rela.r_offset, &str, sizeof(const char *));
+ }
+
+ for (i = 0; i < in_patch.relocs_count; i++) {
+ printf("Parsed patch reloc:\n");
+ printf("\tLocal: %s\n", RELSTR_STR(in_patch.relocs_md[i].local_sym));
+ printf("\tReal: %s\n", RELSTR_STR(in_patch.relocs_md[i].real_sym));
+ printf("\tObj: %s\n", RELSTR_STR(in_patch.relocs_md[i].real_obj));
+ if (in_patch.relocs_md[i].flags & PATCH_USING_SYMPOS) {
+ printf("\tSympos: %ld\n", in_patch.relocs_md[i].uniquifier.sympos);
+ } else {
+ printf("\tFile: %s\n", RELSTR_STR(in_patch.relocs_md[i].uniquifier.real_file));
+ }
+ printf("\tFlags: %lx\n", in_patch.relocs_md[i].flags);
+ }
+}
+
+static void
+parse_patch_metadata(void)
+{
+ if (!in_patch.sets_scn || !in_patch.sets_rela)
+ errx(1, "Invalid patch sets metadata section");
+
+ parse_patch_sets();
+
+ if (in_patch.funcs_scn && !in_patch.funcs_rela)
+ errx(1, "Invalid patch funcs metadata section");
+
+ parse_patch_funcs();
+
+ if (in_patch.relocs_scn && !in_patch.relocs_rela)
+ errx(1, "Invalid patch relocs metadata section");
+
+ if (in_patch.relocs_scn && in_patch.relocs_rela)
+ parse_patch_relocs();
+}
+
+static Elf_Scn *
+create_section(const char *name, uint32_t type, uint64_t flags,
+ uint64_t addralign, uint64_t entsize, void *buf, size_t size)
+{
+ Elf_Scn *scn;
+ GElf_Shdr shdr;
+ Elf_Data *data;
+
+ size_t name_idx = add_shstrtab_string(name);
+ scn = elf_newscn(out_patch.elf);
+ if (scn == NULL)
+ errx(1, "elf_newscn failed: %s", elf_errmsg(-1));
+
+ if (gelf_getshdr(scn, &shdr) == NULL)
+ errx(1, "gelf_getshdr failed: %s", elf_errmsg(-1));
+
+ shdr.sh_name = name_idx;
+ shdr.sh_type = type;
+ shdr.sh_flags = flags;
+ shdr.sh_addralign = addralign;
+ shdr.sh_entsize = entsize;
+ shdr.sh_size = size;
+ gelf_update_shdr(scn, &shdr);
+
+ data = elf_newdata(scn);
+ if (data == NULL)
+ errx(1, "elf_newdata failed: %s", elf_errmsg(-1));
+
+ data->d_buf = buf;
+ data->d_size = size;
+ data->d_align = addralign;
+ data->d_type = (type == SHT_RELA) ? ELF_T_RELA : ELF_T_BYTE;
+ data->d_version = EV_CURRENT;
+
+ return (scn);
+}
+
+static Elf_Scn *
+create_rela_section(const char *name, Elf_Scn *target_scn, GElf_Rela *relas, int count)
+{
+ Elf_Scn *rela_scn, *symtab_scn;
+ GElf_Shdr shdr;
+
+ symtab_scn = elf_getscn(out_patch.elf, index_map_get(elf_ndxscn(in_patch.symtab_scn)));
+
+ rela_scn = create_section(name, SHT_RELA, SHF_INFO_LINK, 8,
+ sizeof(GElf_Rela), relas, count * sizeof(GElf_Rela));
+
+ gelf_getshdr(rela_scn, &shdr);
+ shdr.sh_link = elf_ndxscn(symtab_scn);
+ shdr.sh_info = elf_ndxscn(target_scn);
+ gelf_update_shdr(rela_scn, &shdr);
+
+ return (rela_scn);
+}
+
+static unsigned
+add_section_symbol(Elf_Scn *symtab_scn, unsigned shndx)
+{
+ GElf_Shdr shdr;
+ GElf_Sym sym;
+ int count;
+
+ gelf_getshdr(symtab_scn, &shdr);
+ count = shdr.sh_size / shdr.sh_entsize;
+
+ /* Append a brand new local section symbol */
+ memset(&sym, 0, sizeof(sym));
+ sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION);
+ sym.st_shndx = shndx;
+
+ Elf_Data *new_data = elf_newdata(symtab_scn);
+ GElf_Sym *sym_buf = calloc(1, sizeof(GElf_Sym));
+ *sym_buf = sym;
+
+ new_data->d_align = 8;
+ new_data->d_buf = sym_buf;
+ new_data->d_size = sizeof(GElf_Sym);
+ new_data->d_type = ELF_T_SYM;
+ new_data->d_version = EV_CURRENT;
+
+ shdr.sh_size += sizeof(GElf_Sym);
+ shdr.sh_info = count + 1;
+ gelf_update_shdr(symtab_scn, &shdr);
+
+ return (count);
+}
+
+static Elf_Scn *
+create_kpatch_funcs(void)
+{
+ struct kpatch_func_metadata *funcs_buf;
+ GElf_Rela *relas;
+ Elf_Scn *scn;
+ size_t total_size, f_off;
+ const char *old_file;
+ int i, relocs_count;
+
+ total_size = in_patch.funcs_count * sizeof(struct kpatch_func_metadata);
+ funcs_buf = calloc(in_patch.funcs_count, sizeof(struct kpatch_func_metadata));
+
+ // Current number of relas for each func is 3
+ relas = calloc(in_patch.funcs_count * 3, sizeof(GElf_Rela));
+ relocs_count = 0;
+
+ for (i = 0; i < in_patch.funcs_count; i++) {
+ f_off = i * sizeof(struct kpatch_func_metadata);
+
+ if (in_patch.funcs_md[i].flags & PATCH_USING_SYMPOS) {
+ funcs_buf[i].sympos = in_patch.funcs_md[i].uniquifier.sympos;
+ } else {
+ old_file = in_patch.funcs_md[i].uniquifier.old_file ?
+ RELSTR_STR(in_patch.funcs_md[i].uniquifier.old_file) : "";
+
+ funcs_buf[i].sympos = find_kernel_symbol(
+ RELSTR_STR(in_patch.funcs_md[i].old_sym), old_file);
+ }
+
+ funcs_buf[i].flags = in_patch.funcs_md[i].flags;
+
+ /* Reuse old relocations from RELSTR_RELA */
+ if (in_patch.funcs_md[i].new_addr) {
+ relas[relocs_count] = RELSTR_RELA(in_patch.funcs_md[i].new_addr);
+ relas[relocs_count].r_offset = f_off + offsetof(struct kpatch_func_metadata, new_addr);
+ relocs_count++;
+ }
+ if (in_patch.funcs_md[i].old_sym) {
+ relas[relocs_count] = RELSTR_RELA(in_patch.funcs_md[i].old_sym);
+ relas[relocs_count].r_offset = f_off + offsetof(struct kpatch_func_metadata, old_sym);
+ relocs_count++;
+ }
+ if (in_patch.funcs_md[i].old_obj) {
+ relas[relocs_count] = RELSTR_RELA(in_patch.funcs_md[i].old_obj);
+ relas[relocs_count].r_offset = f_off + offsetof(struct kpatch_func_metadata, old_obj);
+ relocs_count++;
+ }
+ }
+
+ scn = create_section(".kpatch.funcs", SHT_PROGBITS, SHF_ALLOC, 8,
+ sizeof(struct kpatch_func_metadata), funcs_buf, total_size);
+
+ if (relocs_count > 0)
+ create_rela_section(".rela.kpatch.funcs", scn, relas, relocs_count);
+
+ printf("Created .kpatch.funcs (%zu bytes, %d relocations)\n", total_size, relocs_count);
+ return (scn);
+}
+
+static Elf_Scn *
+create_kpatch_sets(Elf_Scn *funcs_scn)
+{
+ struct kpatch_set_metadata *sets_buf;
+ GElf_Rela *relas;
+ Elf_Scn *scn, *symtab_scn;
+ unsigned long funcs_sec_sym_idx;
+ size_t total_size, s_off;
+ int i, j, fcount, first_func_idx, relocs_count;
+
+ symtab_scn = elf_getscn(out_patch.elf, index_map_get(elf_ndxscn(in_patch.symtab_scn)));
+ funcs_sec_sym_idx = add_section_symbol(symtab_scn, elf_ndxscn(funcs_scn));
+
+ total_size = in_patch.sets_count * sizeof(struct kpatch_set_metadata);
+ sets_buf = calloc(in_patch.sets_count, sizeof(struct kpatch_set_metadata));
+
+ // Current number of relas for each set is 6
+ relas = calloc(in_patch.sets_count * 6, sizeof(GElf_Rela));
+ relocs_count = 0;
+
+ for (i = 0; i < in_patch.sets_count; i++) {
+ s_off = i * sizeof(struct kpatch_set_metadata);
+ sets_buf[i].flags = in_patch.sets_md[i].flags;
+
+ /* Count matching functions and identify start index */
+ fcount = 0;
+ first_func_idx = -1;
+ for (j = 0; j < in_patch.funcs_count; j++) {
+ if (strcmp(RELSTR_STR(in_patch.funcs_md[j].patch), RELSTR_STR(in_patch.sets_md[i].name)) == 0) {
+ if (first_func_idx == -1)
+ first_func_idx = j;
+ fcount++;
+ }
+ }
+ sets_buf[i].funcs_count = fcount;
+
+ if (in_patch.sets_md[i].name) {
+ relas[relocs_count] = RELSTR_RELA(in_patch.sets_md[i].name);
+ relas[relocs_count].r_offset = s_off + offsetof(struct kpatch_set_metadata, name);
+ relocs_count++;
+ }
+
+ if (first_func_idx != -1) {
+ relas[relocs_count].r_offset = s_off + offsetof(struct kpatch_set_metadata, funcs);
+ relas[relocs_count].r_info = GELF_R_INFO(funcs_sec_sym_idx, R_X86_64_64);
+ relas[relocs_count].r_addend = first_func_idx * sizeof(struct kpatch_func_metadata);
+ relocs_count++;
+ }
+
+ if (in_patch.sets_md[i].pre_patch) {
+ relas[relocs_count] = RELSTR_RELA(in_patch.sets_md[i].pre_patch);
+ relas[relocs_count].r_offset = s_off + offsetof(struct kpatch_set_metadata, pre_patch);
+ relocs_count++;
+ }
+ if (in_patch.sets_md[i].post_patch) {
+ relas[relocs_count] = RELSTR_RELA(in_patch.sets_md[i].post_patch);
+ relas[relocs_count].r_offset = s_off + offsetof(struct kpatch_set_metadata, post_patch);
+ relocs_count++;
+ }
+ if (in_patch.sets_md[i].pre_unpatch) {
+ relas[relocs_count] = RELSTR_RELA(in_patch.sets_md[i].pre_unpatch);
+ relas[relocs_count].r_offset = s_off + offsetof(struct kpatch_set_metadata, pre_unpatch);
+ relocs_count++;
+ }
+ if (in_patch.sets_md[i].post_unpatch) {
+ relas[relocs_count] = RELSTR_RELA(in_patch.sets_md[i].post_unpatch);
+ relas[relocs_count].r_offset = s_off + offsetof(struct kpatch_set_metadata, post_unpatch);
+ relocs_count++;
+ }
+ }
+
+ scn = create_section(".kpatch.sets", SHT_PROGBITS, SHF_ALLOC, 8,
+ sizeof(struct kpatch_set_metadata), sets_buf, total_size);
+
+ if (relocs_count > 0)
+ create_rela_section(".rela.kpatch.sets", scn, relas, relocs_count);
+
+ printf("Created .kpatch.sets (%zu bytes, %d relocations)\n", total_size, relocs_count);
+ return (scn);
+}
+
+static int
+find_symtab_index(const char *sym_name)
+{
+ GElf_Shdr shdr;
+ GElf_Sym sym;
+ const char *name;
+ int i, count;
+
+ gelf_getshdr(in_patch.symtab_scn, &shdr);
+ count = shdr.sh_size / shdr.sh_entsize;
+
+ for (i = 0; i < count; i++) {
+ gelf_getsym(in_patch.symtab_data, i, &sym);
+ name = elf_strptr(in_patch.elf, shdr.sh_link, sym.st_name);
+
+ if (name != NULL && !strcmp(name, sym_name))
+ return (i);
+ }
+
+ return (-1);
+}
+
+static Elf_Scn *
+create_kpatch_relocs(void)
+{
+ struct kpatch_reloc_metadata *relocs_buf;
+ GElf_Rela *relas;
+ Elf_Scn *scn, *symtab_scn;
+ Elf_Data *symtab_data;
+ size_t total_size, r_off;
+ const char *old_file, *local_sym;
+ int i, sym_idx, relocs_count = 0;
+ GElf_Sym sym;
+
+ symtab_scn = elf_getscn(out_patch.elf, index_map_get(elf_ndxscn(in_patch.symtab_scn)));
+ symtab_data = elf_getdata(symtab_scn, NULL);
+
+ total_size = in_patch.relocs_count * sizeof(struct kpatch_reloc_metadata);
+ relocs_buf = calloc(in_patch.relocs_count, sizeof(struct kpatch_reloc_metadata));
+ relas = calloc(in_patch.relocs_count * 2, sizeof(GElf_Rela));
+
+ for (i = 0; i < in_patch.relocs_count; i++) {
+ r_off = i * sizeof(struct kpatch_reloc_metadata);
+
+ local_sym = RELSTR_STR(in_patch.relocs_md[i].local_sym);
+ sym_idx = find_symtab_index(local_sym);
+ if (sym_idx < 0)
+ errx(1, "Could not find reloc local symbol %s in symtab", local_sym);
+
+ gelf_getsym(symtab_data, sym_idx, &sym);
+ sym.st_shndx = SHN_FREEBSD_KPATCH;
+ sym.st_value = i; /* Index in the kpatch_reloc_metadata array */
+ sym.st_info = GELF_ST_INFO(GELF_ST_BIND(sym.st_info), STT_NOTYPE);
+ gelf_update_sym(symtab_data, sym_idx, &sym);
+
+ relocs_buf[i].flags = in_patch.relocs_md[i].flags;
+ if (in_patch.relocs_md[i].flags & PATCH_USING_SYMPOS) {
+ relocs_buf[i].sympos = in_patch.relocs_md[i].uniquifier.sympos;
+ } else {
+ old_file = in_patch.relocs_md[i].uniquifier.real_file ?
+ RELSTR_STR(in_patch.relocs_md[i].uniquifier.real_file) : "";
+ relocs_buf[i].sympos = find_kernel_symbol(
+ RELSTR_STR(in_patch.relocs_md[i].real_sym), old_file);
+ }
+
+ if (in_patch.relocs_md[i].real_sym) {
+ relas[relocs_count] = RELSTR_RELA(in_patch.relocs_md[i].real_sym);
+ relas[relocs_count].r_offset = r_off + offsetof(struct kpatch_reloc_metadata, sym);
+ relocs_count++;
+ }
+ if (in_patch.relocs_md[i].real_obj) {
+ relas[relocs_count] = RELSTR_RELA(in_patch.relocs_md[i].real_obj);
+ relas[relocs_count].r_offset = r_off + offsetof(struct kpatch_reloc_metadata, obj);
+ relocs_count++;
+ }
+ }
+
+ scn = create_section(".kpatch.relocs", SHT_PROGBITS, SHF_ALLOC, 8,
+ sizeof(struct kpatch_reloc_metadata), relocs_buf, total_size);
+
+ if (relocs_count > 0)
+ create_rela_section(".rela.kpatch.relocs", scn, relas, relocs_count);
+
+ printf("Created .kpatch.relocs (%zu bytes, %d relocations)\n", total_size, relocs_count);
+ return (scn);
+}
+
+static Elf_Scn *
+create_kpatch_info(Elf_Scn *sets_scn, Elf_Scn *relocs_scn)
+{
+ struct kpatch_metadata *info_buf;
+ GElf_Rela *relas;
+ Elf_Scn *scn, *symtab_scn;
+ unsigned long sets_sec_sym_idx, relocs_sec_sym_idx;
+ int relocs_count = 0;
+
+ info_buf = calloc(1, sizeof(struct kpatch_metadata));
+ relas = calloc(2, sizeof(GElf_Rela));
+
+ symtab_scn = elf_getscn(out_patch.elf, index_map_get(elf_ndxscn(in_patch.symtab_scn)));
+
+ info_buf->sets_count = in_patch.sets_count;
+ if (sets_scn != NULL && in_patch.sets_count > 0) {
+ sets_sec_sym_idx = add_section_symbol(symtab_scn, elf_ndxscn(sets_scn));
+
+ relas[relocs_count].r_offset = offsetof(struct kpatch_metadata, sets);
+ relas[relocs_count].r_info = GELF_R_INFO(sets_sec_sym_idx, R_X86_64_64);
+ relas[relocs_count].r_addend = 0;
+ relocs_count++;
+ }
+
+ info_buf->relocs_count = in_patch.relocs_count;
+ if (relocs_scn != NULL && in_patch.relocs_count > 0) {
+ relocs_sec_sym_idx = add_section_symbol(symtab_scn, elf_ndxscn(relocs_scn));
+
+ relas[relocs_count].r_offset = offsetof(struct kpatch_metadata, relocs);
+ relas[relocs_count].r_info = GELF_R_INFO(relocs_sec_sym_idx, R_X86_64_64);
+ relas[relocs_count].r_addend = 0;
+ relocs_count++;
+ }
+
+ memcpy(info_buf->build_id, kern.build_id, kern.build_id_len);
+ info_buf->build_id_len = kern.build_id_len;
+
+ scn = create_section(".kpatch.info", SHT_PROGBITS, SHF_ALLOC, 8,
+ sizeof(struct kpatch_metadata), info_buf, sizeof(struct kpatch_metadata));
+
+ if (relocs_count > 0)
+ create_rela_section(".rela.kpatch.info", scn, relas, relocs_count);
+
+ add_symbol(symtab_scn, KPATCH_METADATA, elf_ndxscn(scn), sizeof(struct kpatch_metadata));
+
+ printf("Created .kpatch.info (%zu bytes, symbol %s)\n", sizeof(struct kpatch_metadata), KPATCH_METADATA);
+ return (scn);
+}
+
+static void
+new_patch_sections(void)
+{
+ Elf_Scn *funcs_scn, *sets_scn, *relocs_scn;
+
+ funcs_scn = create_kpatch_funcs();
+ sets_scn = create_kpatch_sets(funcs_scn);
+ relocs_scn = create_kpatch_relocs();
+
+ create_kpatch_info(sets_scn, relocs_scn);
+}
+
+static void
+fix_patch_relocations(void)
+{
+ Elf_Scn *scn_out;
+ GElf_Shdr shdr;
+ Elf_Data *symtab_data_out;
+ GElf_Sym sym;
+ int dirty, i;
+ unsigned new_ndx;
+
+ scn_out = NULL;
+ while ((scn_out = elf_nextscn(out_patch.elf, scn_out)) != NULL) {
+ if (elf_ndxscn(scn_out) > out_patch.last_ndx)
+ continue;
+
+ gelf_getshdr(scn_out, &shdr);
+ dirty = 0;
+
+ if (shdr.sh_link != 0) {
+ shdr.sh_link = index_map_get(shdr.sh_link);
+ dirty = 1;
+ }
+
+ if (shdr.sh_info != 0 && (shdr.sh_type == SHT_REL || shdr.sh_type == SHT_RELA)) {
+ shdr.sh_info = index_map_get(shdr.sh_info);
+ dirty = 1;
+ }
+
+ if (dirty)
+ gelf_update_shdr(scn_out, &shdr);
+ }
+
+ /* Update symbol section indices */
+ scn_out = elf_getscn(out_patch.elf, index_map_get(elf_ndxscn(in_patch.symtab_scn)));
+ symtab_data_out = elf_getdata(scn_out, NULL);
+
+ /*
+ * Fixup the old copied symbols up to in_patch.symtab_count,
+ * leaving intact the new symbols added later
+ */
+ for (i = 0; i < in_patch.symtab_count; i++) {
+ gelf_getsym(symtab_data_out, i, &sym);
+
+ /* Ignore standard/special sections (e.g. UNDEF, ABS, KPATCH) */
+ if (sym.st_shndx > 0 && sym.st_shndx < SHN_LORESERVE) {
+ new_ndx = index_map_get(sym.st_shndx);
+ if (new_ndx != sym.st_shndx) {
+ sym.st_shndx = new_ndx;
+ gelf_update_sym(symtab_data_out, i, &sym);
+ }
+ }
+ }
+
+ /* Update the string table index */
+ elf_setshstrndx(out_patch.elf, index_map_get(in_patch.shstrndx));
+}
+
+static int
+close_patch(void)
+{
+ int ret;
+
+ ret = elf_update(out_patch.elf, ELF_C_WRITE);
+ if (ret < 0)
+ printf("elf_update failed: %s", elf_errmsg(-1));
+
+ // TODO: Free shstrab data
+
+ elf_end(out_patch.elf);
+ elf_end(in_patch.elf);
+ close(out_patch.fd);
+ close(in_patch.fd);
+
+ free(out_patch.idx_map);
+ return (ret);
+}
+
+int
+main(int argc, const char **argv)
+{
+ if (argc != 4)
+ errx(1, "Usage: %s RAW_KMOD OUT_KMOD KERNEL", argv[0]);
+
+ if (elf_version(EV_CURRENT) == EV_NONE)
+ errx(1, "Failed to init libelf: %s", elf_errmsg(-1));
+
+ open_kernel(argv[3]);
+ open_patch(argv[1], argv[2]);
+
+ copy_patch_sections();
+
+ parse_patch_metadata();
+
+ new_patch_sections();
+
+ fix_patch_relocations();
+
+ close_kernel();
+ return (close_patch() < 0);
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Sep 10, 2:02 PM (19 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38631666
Default Alt Text
D59492.id186223.diff (35 KB)
Attached To
Mode
D59492: buildpatch: Add kpatch ELF post-processing tool
Attached
Detach File
Event Timeline
Log In to Comment