objtool: Provide elf_write_{insn,reloc}()
authorPeter Zijlstra <peterz@infradead.org>
Fri, 12 Jun 2020 13:43:00 +0000 (15:43 +0200)
committerPeter Zijlstra <peterz@infradead.org>
Thu, 18 Jun 2020 15:36:33 +0000 (17:36 +0200)
This provides infrastructure to rewrite instructions; this is
immediately useful for helping out with KCOV-vs-noinstr, but will
also come in handy for a bunch of variable sized jump-label patches
that are still on ice.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
tools/objtool/elf.c
tools/objtool/elf.h

index bc6723a502c3e15888bd82780d8398379c87c9ce..26d11d8219418dd56d95c6025802d78b3c53fc52 100644 (file)
@@ -529,8 +529,9 @@ static int read_relas(struct elf *elf)
                        rela->addend = rela->rela.r_addend;
                        rela->offset = rela->rela.r_offset;
                        symndx = GELF_R_SYM(rela->rela.r_info);
-                       rela->sym = find_symbol_by_index(elf, symndx);
                        rela->sec = sec;
+                       rela->idx = i;
+                       rela->sym = find_symbol_by_index(elf, symndx);
                        if (!rela->sym) {
                                WARN("can't find rela entry symbol %d for %s",
                                     symndx, sec->name);
@@ -784,6 +785,43 @@ int elf_rebuild_rela_section(struct elf *elf, struct section *sec)
        return 0;
 }
 
+int elf_write_insn(struct elf *elf, struct section *sec,
+                  unsigned long offset, unsigned int len,
+                  const char *insn)
+{
+       Elf_Data *data = sec->data;
+
+       if (data->d_type != ELF_T_BYTE || data->d_off) {
+               WARN("write to unexpected data for section: %s", sec->name);
+               return -1;
+       }
+
+       memcpy(data->d_buf + offset, insn, len);
+       elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY);
+
+       elf->changed = true;
+
+       return 0;
+}
+
+int elf_write_rela(struct elf *elf, struct rela *rela)
+{
+       struct section *sec = rela->sec;
+
+       rela->rela.r_info = GELF_R_INFO(rela->sym->idx, rela->type);
+       rela->rela.r_addend = rela->addend;
+       rela->rela.r_offset = rela->offset;
+
+       if (!gelf_update_rela(sec->data, rela->idx, &rela->rela)) {
+               WARN_ELF("gelf_update_rela");
+               return -1;
+       }
+
+       elf->changed = true;
+
+       return 0;
+}
+
 int elf_write(struct elf *elf)
 {
        struct section *sec;
index aa9c64da379fdb18e293281f05a921901334b277..7324e772583ee76cc53b26acf190e3d7722a3bc6 100644 (file)
@@ -64,9 +64,10 @@ struct rela {
        GElf_Rela rela;
        struct section *sec;
        struct symbol *sym;
-       unsigned int type;
        unsigned long offset;
+       unsigned int type;
        int addend;
+       int idx;
        bool jump_table_start;
 };
 
@@ -119,6 +120,10 @@ struct elf *elf_open_read(const char *name, int flags);
 struct section *elf_create_section(struct elf *elf, const char *name, size_t entsize, int nr);
 struct section *elf_create_rela_section(struct elf *elf, struct section *base);
 void elf_add_rela(struct elf *elf, struct rela *rela);
+int elf_write_insn(struct elf *elf, struct section *sec,
+                  unsigned long offset, unsigned int len,
+                  const char *insn);
+int elf_write_rela(struct elf *elf, struct rela *rela);
 int elf_write(struct elf *elf);
 void elf_close(struct elf *elf);