ARM: PIE: Add support for updating PIE relocations
[firefly-linux-kernel-4.4.55.git] / arch / arm / kernel / pie.c
1 /*
2  * Copyright 2013 Texas Instruments, Inc.
3  *      Russ Dill <russ.dill@ti.com>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU General Public License,
7  * version 2, as published by the Free Software Foundation.
8  */
9
10 #include <linux/kernel.h>
11 #include <linux/pie.h>
12 #include <linux/elf.h>
13
14 #include <asm/elf.h>
15 #include <asm/pie.h>
16
17 extern char __pie_rel_dyn_start[];
18 extern char __pie_rel_dyn_end[];
19 extern char __pie_tail_offset[];
20 extern char __pie_reloc_offset[];
21
22 struct arm_pie_tail {
23         int count;
24         uintptr_t offset[0];
25 };
26
27 int pie_arch_fill_tail(void *tail, void *common_start, void *common_end,
28                         void *overlay_start, void *code_start, void *code_end)
29 {
30         Elf32_Rel *rel;
31         int records;
32         int i;
33         struct arm_pie_tail *pie_tail = tail;
34         int count;
35
36         rel = (Elf32_Rel *) __pie_rel_dyn_start;
37         records = (__pie_rel_dyn_end - __pie_rel_dyn_start) /
38                                                 sizeof(*rel);
39
40         count = 0;
41         for (i = 0; i < records; i++, rel++) {
42                 void *kern_off;
43                 if (ELF32_R_TYPE(rel->r_info) != R_ARM_RELATIVE)
44                         return -ENOEXEC;
45
46                 /* Adjust offset to match area in kernel */
47                 kern_off = common_start + rel->r_offset;
48
49                 if (kern_off >= common_start && kern_off < code_end) {
50                         if (tail)
51                                 pie_tail->offset[count] = rel->r_offset;
52                         count++;
53                 } else if (kern_off >= code_start && kern_off < code_end) {
54                         if (tail)
55                                 pie_tail->offset[count] = rel->r_offset -
56                                                 (code_start - overlay_start);
57                         count++;
58                 }
59         }
60
61         if (tail)
62                 pie_tail->count = count;
63
64         return count * sizeof(uintptr_t) + sizeof(*pie_tail);
65 }
66 EXPORT_SYMBOL_GPL(pie_arch_fill_tail);
67
68 /*
69  * R_ARM_RELATIVE: B(S) + A
70  * B(S) - Addressing origin of the output segment defining the symbol S.
71  * A - Addend for the relocation.
72  */
73 int pie_arch_fixup(struct pie_chunk *chunk, void *base, void *tail,
74                                                 unsigned long offset)
75 {
76         struct arm_pie_tail *pie_tail = tail;
77         void *reloc;
78         int i;
79
80         /* Perform relocation fixups for given offset */
81         for (i = 0; i < pie_tail->count; i++)
82                 *((uintptr_t *) (pie_tail->offset[i] + base)) += offset;
83
84         /* Store the PIE offset to tail and recol func */
85         *kern_to_pie(chunk, (uintptr_t *) __pie_tail_offset) = tail - base;
86         reloc = kern_to_pie(chunk,
87                                 (void *) fnptr_to_addr(&__pie___pie_relocate));
88         *kern_to_pie(chunk, (uintptr_t *) __pie_reloc_offset) = reloc - base;
89
90         return 0;
91 }
92 EXPORT_SYMBOL_GPL(pie_arch_fixup);