Merge branch 'rk_develop-3.10' into rk_develop-3.10-next
[firefly-linux-kernel-4.4.55.git] / arch / arm / kernel / pie.c
index 5dff5d671f1aa2261e2b9bea6b1f5ed0ec13531a..a4b41e9441f79eb45cbb7bc2c2fd6b5f177fbc2e 100644 (file)
 #include <linux/elf.h>
 
 #include <asm/elf.h>
+#include <asm/pie.h>
 
 extern char __pie_rel_dyn_start[];
 extern char __pie_rel_dyn_end[];
 extern char __pie_tail_offset[];
+extern char __pie_reloc_offset[];
 
 struct arm_pie_tail {
        int count;
@@ -23,23 +25,23 @@ struct arm_pie_tail {
 };
 
 int pie_arch_fill_tail(void *tail, void *common_start, void *common_end,
-                       void *overlay_start, void *code_start, void *code_end)
+                       void *overlay_start, void *code_start, void *code_end,
+                       void *rel_start, void *rel_end)
 {
        Elf32_Rel *rel;
        int records;
        int i;
        struct arm_pie_tail *pie_tail = tail;
        int count;
+       void *kern_off;
 
        rel = (Elf32_Rel *) __pie_rel_dyn_start;
-       records = (__pie_rel_dyn_end - __pie_rel_dyn_start) /
-                                               sizeof(*rel);
+       records = (__pie_rel_dyn_end - __pie_rel_dyn_start) / sizeof(*rel);
 
        count = 0;
        for (i = 0; i < records; i++, rel++) {
-               void *kern_off;
                if (ELF32_R_TYPE(rel->r_info) != R_ARM_RELATIVE)
-                       return -ENOEXEC;
+                       break;
 
                /* Adjust offset to match area in kernel */
                kern_off = common_start + rel->r_offset;
@@ -48,10 +50,22 @@ int pie_arch_fill_tail(void *tail, void *common_start, void *common_end,
                        if (tail)
                                pie_tail->offset[count] = rel->r_offset;
                        count++;
-               } else if (kern_off >= code_start && kern_off < code_end) {
+               }
+       }
+
+       rel = (Elf32_Rel *) rel_start;
+       records = (rel_end - rel_start) / sizeof(*rel);
+
+       for (i = 0; i < records; i++, rel++) {
+               if (ELF32_R_TYPE(rel->r_info) != R_ARM_RELATIVE)
+                       break;
+
+               /* Adjust offset to match area in kernel */
+               kern_off = common_start + rel->r_offset;
+
+               if (kern_off >= common_start && kern_off < code_end) {
                        if (tail)
-                               pie_tail->offset[count] = rel->r_offset -
-                                               (code_start - overlay_start);
+                               pie_tail->offset[count] = rel->r_offset;
                        count++;
                }
        }
@@ -72,12 +86,19 @@ int pie_arch_fixup(struct pie_chunk *chunk, void *base, void *tail,
                                                unsigned long offset)
 {
        struct arm_pie_tail *pie_tail = tail;
+       void *reloc;
        int i;
 
        /* Perform relocation fixups for given offset */
        for (i = 0; i < pie_tail->count; i++)
                *((uintptr_t *) (pie_tail->offset[i] + base)) += offset;
 
+       /* Store the PIE offset to tail and recol func */
+       *kern_to_pie(chunk, (uintptr_t *) __pie_tail_offset) = tail - base;
+       reloc = kern_to_pie(chunk,
+                               (void *) fnptr_to_addr(&__pie___pie_relocate));
+       *kern_to_pie(chunk, (uintptr_t *) __pie_reloc_offset) = reloc - base;
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(pie_arch_fixup);