diff options
author | Zong Li <zong@andestech.com> | 2018-03-15 04:50:43 -0400 |
---|---|---|
committer | Palmer Dabbelt <palmer@sifive.com> | 2018-04-02 23:00:54 -0400 |
commit | da975dd4818cf42a181910789c096eb6997ed663 (patch) | |
tree | eb538022010facb754f433de628a6b66bc954c4a | |
parent | b8bde0ef12bd43f013d879973a1900930bfb95ee (diff) |
RISC-V: Support GOT_HI20/CALL_PLT relocation type in kernel module
For CALL_PLT, emit the plt entry only when offset is more than 32-bit.
For PCREL_LO12, it uses the location of corresponding HI20 to
get the address of external symbol. It should check the HI20 type
is the PCREL_HI20 or GOT_HI20, because sometime the location will
have two or more relocation types.
For example:
0: 00000797 auipc a5,0x0
0: R_RISCV_ALIGN *ABS*
0: R_RISCV_GOT_HI20 SYMBOL
4: 0007b783 ld a5,0(a5) # 0 <SYMBOL>
4: R_RISCV_PCREL_LO12_I .L0
4: R_RISCV_RELAX *ABS*
Signed-off-by: Zong Li <zong@andestech.com>
Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
-rw-r--r-- | arch/riscv/kernel/module.c | 62 |
1 files changed, 52 insertions, 10 deletions
diff --git a/arch/riscv/kernel/module.c b/arch/riscv/kernel/module.c index e0f05034fc21..be717bd7cea7 100644 --- a/arch/riscv/kernel/module.c +++ b/arch/riscv/kernel/module.c | |||
@@ -92,6 +92,28 @@ static int apply_r_riscv_pcrel_lo12_s_rela(struct module *me, u32 *location, | |||
92 | return 0; | 92 | return 0; |
93 | } | 93 | } |
94 | 94 | ||
95 | static int apply_r_riscv_got_hi20_rela(struct module *me, u32 *location, | ||
96 | Elf_Addr v) | ||
97 | { | ||
98 | s64 offset = (void *)v - (void *)location; | ||
99 | s32 hi20; | ||
100 | |||
101 | /* Always emit the got entry */ | ||
102 | if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) { | ||
103 | offset = module_emit_got_entry(me, v); | ||
104 | offset = (void *)offset - (void *)location; | ||
105 | } else { | ||
106 | pr_err( | ||
107 | "%s: can not generate the GOT entry for symbol = %016llx from PC = %p\n", | ||
108 | me->name, v, location); | ||
109 | return -EINVAL; | ||
110 | } | ||
111 | |||
112 | hi20 = (offset + 0x800) & 0xfffff000; | ||
113 | *location = (*location & 0xfff) | hi20; | ||
114 | return 0; | ||
115 | } | ||
116 | |||
95 | static int apply_r_riscv_call_plt_rela(struct module *me, u32 *location, | 117 | static int apply_r_riscv_call_plt_rela(struct module *me, u32 *location, |
96 | Elf_Addr v) | 118 | Elf_Addr v) |
97 | { | 119 | { |
@@ -100,10 +122,16 @@ static int apply_r_riscv_call_plt_rela(struct module *me, u32 *location, | |||
100 | u32 hi20, lo12; | 122 | u32 hi20, lo12; |
101 | 123 | ||
102 | if (offset != fill_v) { | 124 | if (offset != fill_v) { |
103 | pr_err( | 125 | /* Only emit the plt entry if offset over 32-bit range */ |
104 | "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", | 126 | if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) { |
105 | me->name, v, location); | 127 | offset = module_emit_plt_entry(me, v); |
106 | return -EINVAL; | 128 | offset = (void *)offset - (void *)location; |
129 | } else { | ||
130 | pr_err( | ||
131 | "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n", | ||
132 | me->name, v, location); | ||
133 | return -EINVAL; | ||
134 | } | ||
107 | } | 135 | } |
108 | 136 | ||
109 | hi20 = (offset + 0x800) & 0xfffff000; | 137 | hi20 = (offset + 0x800) & 0xfffff000; |
@@ -127,6 +155,7 @@ static int (*reloc_handlers_rela[]) (struct module *me, u32 *location, | |||
127 | [R_RISCV_PCREL_HI20] = apply_r_riscv_pcrel_hi20_rela, | 155 | [R_RISCV_PCREL_HI20] = apply_r_riscv_pcrel_hi20_rela, |
128 | [R_RISCV_PCREL_LO12_I] = apply_r_riscv_pcrel_lo12_i_rela, | 156 | [R_RISCV_PCREL_LO12_I] = apply_r_riscv_pcrel_lo12_i_rela, |
129 | [R_RISCV_PCREL_LO12_S] = apply_r_riscv_pcrel_lo12_s_rela, | 157 | [R_RISCV_PCREL_LO12_S] = apply_r_riscv_pcrel_lo12_s_rela, |
158 | [R_RISCV_GOT_HI20] = apply_r_riscv_got_hi20_rela, | ||
130 | [R_RISCV_CALL_PLT] = apply_r_riscv_call_plt_rela, | 159 | [R_RISCV_CALL_PLT] = apply_r_riscv_call_plt_rela, |
131 | [R_RISCV_RELAX] = apply_r_riscv_relax_rela, | 160 | [R_RISCV_RELAX] = apply_r_riscv_relax_rela, |
132 | }; | 161 | }; |
@@ -184,25 +213,38 @@ int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, | |||
184 | u64 hi20_loc = | 213 | u64 hi20_loc = |
185 | sechdrs[sechdrs[relsec].sh_info].sh_addr | 214 | sechdrs[sechdrs[relsec].sh_info].sh_addr |
186 | + rel[j].r_offset; | 215 | + rel[j].r_offset; |
187 | /* Find the corresponding HI20 PC-relative relocation entry */ | 216 | u32 hi20_type = ELF_RISCV_R_TYPE(rel[j].r_info); |
188 | if (hi20_loc == sym->st_value) { | 217 | |
218 | /* Find the corresponding HI20 relocation entry */ | ||
219 | if (hi20_loc == sym->st_value | ||
220 | && (hi20_type == R_RISCV_PCREL_HI20 | ||
221 | || hi20_type == R_RISCV_GOT_HI20)) { | ||
222 | s32 hi20, lo12; | ||
189 | Elf_Sym *hi20_sym = | 223 | Elf_Sym *hi20_sym = |
190 | (Elf_Sym *)sechdrs[symindex].sh_addr | 224 | (Elf_Sym *)sechdrs[symindex].sh_addr |
191 | + ELF_RISCV_R_SYM(rel[j].r_info); | 225 | + ELF_RISCV_R_SYM(rel[j].r_info); |
192 | u64 hi20_sym_val = | 226 | u64 hi20_sym_val = |
193 | hi20_sym->st_value | 227 | hi20_sym->st_value |
194 | + rel[j].r_addend; | 228 | + rel[j].r_addend; |
229 | |||
195 | /* Calculate lo12 */ | 230 | /* Calculate lo12 */ |
196 | s64 offset = hi20_sym_val - hi20_loc; | 231 | u64 offset = hi20_sym_val - hi20_loc; |
197 | s32 hi20 = (offset + 0x800) & 0xfffff000; | 232 | if (IS_ENABLED(CONFIG_MODULE_SECTIONS) |
198 | s32 lo12 = offset - hi20; | 233 | && hi20_type == R_RISCV_GOT_HI20) { |
234 | offset = module_emit_got_entry( | ||
235 | me, hi20_sym_val); | ||
236 | offset = offset - hi20_loc; | ||
237 | } | ||
238 | hi20 = (offset + 0x800) & 0xfffff000; | ||
239 | lo12 = offset - hi20; | ||
199 | v = lo12; | 240 | v = lo12; |
241 | |||
200 | break; | 242 | break; |
201 | } | 243 | } |
202 | } | 244 | } |
203 | if (j == sechdrs[relsec].sh_size / sizeof(*rel)) { | 245 | if (j == sechdrs[relsec].sh_size / sizeof(*rel)) { |
204 | pr_err( | 246 | pr_err( |
205 | "%s: Can not find HI20 PC-relative relocation information\n", | 247 | "%s: Can not find HI20 relocation information\n", |
206 | me->name); | 248 | me->name); |
207 | return -EINVAL; | 249 | return -EINVAL; |
208 | } | 250 | } |