1 /* dl.c - arch-dependent part of loadable module support */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2018 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
22 #include <grub/misc.h>
25 #include <grub/i18n.h>
28 * Instructions and instruction encoding are documented in the RISC-V
29 * specification. This file is based on version 2.2:
31 * https://github.com/riscv/riscv-isa-manual/blob/master/release/riscv-spec-v2.2.pdf
33 #define LDR 0x58000050
37 * Check if EHDR is a valid ELF header.
40 grub_arch_dl_check_header (void *ehdr
)
44 /* Check the magic numbers. */
45 if (e
->e_ident
[EI_DATA
] != ELFDATA2LSB
|| e
->e_machine
!= EM_RISCV
)
46 return grub_error (GRUB_ERR_BAD_OS
,
47 N_("invalid arch-dependent ELF magic"));
52 #pragma GCC diagnostic ignored "-Wcast-align"
54 /* Relocate symbols. */
56 grub_arch_dl_relocate_symbols (grub_dl_t mod
, void *ehdr
,
57 Elf_Shdr
*s
, grub_dl_segment_t seg
)
61 for (rel
= (Elf_Rel
*) ((char *) ehdr
+ s
->sh_offset
),
62 max
= (Elf_Rel
*) ((char *) rel
+ s
->sh_size
);
64 rel
= (Elf_Rel
*) ((char *) rel
+ s
->sh_entsize
))
70 if (rel
->r_offset
>= seg
->size
)
71 return grub_error (GRUB_ERR_BAD_MODULE
,
72 "reloc offset is out of the segment");
74 sym
= (Elf_Sym
*) ((char *) mod
->symtab
75 + mod
->symsize
* ELF_R_SYM (rel
->r_info
));
77 sym_addr
= sym
->st_value
;
78 if (s
->sh_type
== SHT_RELA
)
79 sym_addr
+= ((Elf_Rela
*) rel
)->r_addend
;
81 place
= (void *) ((grub_addr_t
) seg
->addr
+ rel
->r_offset
);
83 switch (ELF_R_TYPE (rel
->r_info
))
87 grub_uint32_t
*abs_place
= place
;
89 grub_dprintf ("dl", " reloc_abs32 %p => 0x%016llx\n",
90 place
, (unsigned long long) sym_addr
);
92 *abs_place
= (grub_uint32_t
) sym_addr
;
97 grub_size_t
*abs_place
= place
;
99 grub_dprintf ("dl", " reloc_abs64 %p => 0x%016llx\n",
100 place
, (unsigned long long) sym_addr
);
102 *abs_place
= (grub_size_t
) sym_addr
;
108 grub_uint8_t
*abs_place
= place
;
110 *abs_place
+= (grub_uint8_t
) sym_addr
;
115 grub_uint16_t
*abs_place
= place
;
117 *abs_place
+= (grub_uint16_t
) sym_addr
;
122 grub_uint32_t
*abs_place
= place
;
124 *abs_place
+= (grub_uint32_t
) sym_addr
;
129 grub_size_t
*abs_place
= place
;
131 *abs_place
+= (grub_size_t
) sym_addr
;
137 grub_uint8_t
*abs_place
= place
;
139 *abs_place
-= (grub_uint8_t
) sym_addr
;
144 grub_uint16_t
*abs_place
= place
;
146 *abs_place
-= (grub_uint16_t
) sym_addr
;
151 grub_uint32_t
*abs_place
= place
;
153 *abs_place
-= (grub_uint32_t
) sym_addr
;
158 grub_size_t
*abs_place
= place
;
160 *abs_place
-= (grub_size_t
) sym_addr
;
166 grub_uint32_t
*abs_place
= place
;
167 grub_ssize_t off
= sym_addr
- (grub_addr_t
) place
;
168 grub_uint32_t imm12
= (off
& 0x1000) << (31 - 12);
169 grub_uint32_t imm11
= (off
& 0x800) >> (11 - 7);
170 grub_uint32_t imm10_5
= (off
& 0x7e0) << (30 - 10);
171 grub_uint32_t imm4_1
= (off
& 0x1e) << (11 - 4);
172 *abs_place
= (*abs_place
& 0x1fff07f)
173 | imm12
| imm11
| imm10_5
| imm4_1
;
179 grub_uint32_t
*abs_place
= place
;
180 grub_ssize_t off
= sym_addr
- (grub_addr_t
) place
;
181 grub_uint32_t imm20
= (off
& 0x100000) << (31 - 20);
182 grub_uint32_t imm19_12
= (off
& 0xff000);
183 grub_uint32_t imm11
= (off
& 0x800) << (20 - 11);
184 grub_uint32_t imm10_1
= (off
& 0x7fe) << (30 - 10);
185 *abs_place
= (*abs_place
& 0xfff)
186 | imm20
| imm19_12
| imm11
| imm10_1
;
192 grub_uint32_t
*abs_place
= place
;
193 grub_ssize_t off
= sym_addr
- (grub_addr_t
) place
;
194 grub_uint32_t hi20
, lo12
;
196 if (off
!= (grub_int32_t
) off
)
197 return grub_error (GRUB_ERR_BAD_MODULE
, "relocation overflow");
199 hi20
= (off
+ 0x800) & 0xfffff000;
200 lo12
= (off
- hi20
) & 0xfff;
201 abs_place
[0] = (abs_place
[0] & 0xfff) | hi20
;
202 abs_place
[1] = (abs_place
[1] & 0xfffff) | (lo12
<< 20);
206 case R_RISCV_RVC_BRANCH
:
208 grub_uint16_t
*abs_place
= place
;
209 grub_ssize_t off
= sym_addr
- (grub_addr_t
) place
;
210 grub_uint16_t imm8
= (off
& 0x100) << (12 - 8);
211 grub_uint16_t imm7_6
= (off
& 0xc0) >> (6 - 5);
212 grub_uint16_t imm5
= (off
& 0x20) >> (5 - 2);
213 grub_uint16_t imm4_3
= (off
& 0x18) << (12 - 5);
214 grub_uint16_t imm2_1
= (off
& 0x6) << (12 - 10);
215 *abs_place
= (*abs_place
& 0xe383)
216 | imm8
| imm7_6
| imm5
| imm4_3
| imm2_1
;
220 case R_RISCV_RVC_JUMP
:
222 grub_uint16_t
*abs_place
= place
;
223 grub_ssize_t off
= sym_addr
- (grub_addr_t
) place
;
224 grub_uint16_t imm11
= (off
& 0x800) << (12 - 11);
225 grub_uint16_t imm10
= (off
& 0x400) >> (10 - 8);
226 grub_uint16_t imm9_8
= (off
& 0x300) << (12 - 11);
227 grub_uint16_t imm7
= (off
& 0x80) >> (7 - 6);
228 grub_uint16_t imm6
= (off
& 0x40) << (12 - 11);
229 grub_uint16_t imm5
= (off
& 0x20) >> (5 - 2);
230 grub_uint16_t imm4
= (off
& 0x10) << (12 - 5);
231 grub_uint16_t imm3_1
= (off
& 0xe) << (12 - 10);
232 *abs_place
= ((*abs_place
& 0xe003)
233 | imm11
| imm10
| imm9_8
| imm7
| imm6
234 | imm5
| imm4
| imm3_1
);
238 case R_RISCV_PCREL_HI20
:
240 grub_uint32_t
*abs_place
= place
;
241 grub_ssize_t off
= sym_addr
- (grub_addr_t
) place
;
244 if (off
!= (grub_int32_t
)off
)
245 return grub_error (GRUB_ERR_BAD_MODULE
, "relocation overflow");
247 hi20
= (off
+ 0x800) & 0xfffff000;
248 *abs_place
= (*abs_place
& 0xfff) | hi20
;
252 case R_RISCV_PCREL_LO12_I
:
253 case R_RISCV_PCREL_LO12_S
:
255 grub_uint32_t
*t32
= place
;
257 /* Search backwards for matching HI20 reloc. */
258 for (rel2
= (Elf_Rela
*) ((char *) rel
- s
->sh_entsize
);
259 (unsigned long)rel2
>= ((unsigned long)ehdr
+ s
->sh_offset
);
260 rel2
= (Elf_Rela
*) ((char *) rel2
- s
->sh_entsize
))
263 Elf_Addr rel2_offset
;
264 Elf_Addr rel2_sym_addr
;
266 grub_ssize_t rel2_off
;
270 rel2_offset
= rel2
->r_offset
;
271 rel2_info
= rel2
->r_info
;
272 rel2_loc
= (grub_addr_t
) seg
->addr
+ rel2_offset
;
274 if (ELF_R_TYPE (rel2_info
) == R_RISCV_PCREL_HI20
275 && rel2_loc
== sym_addr
)
277 sym2
= (Elf_Sym
*) ((char *) mod
->symtab
278 + mod
->symsize
* ELF_R_SYM (rel2
->r_info
));
279 rel2_sym_addr
= sym2
->st_value
;
280 if (s
->sh_type
== SHT_RELA
)
281 rel2_sym_addr
+= ((Elf_Rela
*) rel2
)->r_addend
;
283 rel2_off
= rel2_sym_addr
- rel2_loc
;
284 off
= rel2_off
- ((rel2_off
+ 0x800) & 0xfffff000);
286 if (ELF_R_TYPE (rel
->r_info
) == R_RISCV_PCREL_LO12_I
)
287 *t32
= (*t32
& 0xfffff) | (off
& 0xfff) << 20;
290 grub_uint32_t imm11_5
= (off
& 0xfe0) << (31 - 11);
291 grub_uint32_t imm4_0
= (off
& 0x1f) << (11 - 4);
292 *t32
= (*t32
& 0x1fff07f) | imm11_5
| imm4_0
;
297 if ((unsigned long)rel2
< ((unsigned long)ehdr
+ s
->sh_offset
))
298 return grub_error (GRUB_ERR_BAD_MODULE
, "cannot find matching HI20 relocation");
304 grub_uint32_t
*abs_place
= place
;
305 *abs_place
= (*abs_place
& 0xfff) |
306 (((grub_int32_t
) sym_addr
+ 0x800) & 0xfffff000);
312 grub_uint32_t
*abs_place
= place
;
313 grub_int32_t lo12
= (grub_int32_t
) sym_addr
-
314 (((grub_int32_t
) sym_addr
+ 0x800) & 0xfffff000);
315 *abs_place
= (*abs_place
& 0xfffff) | ((lo12
& 0xfff) << 20);
321 grub_uint32_t
*abs_place
= place
;
322 grub_int32_t lo12
= (grub_int32_t
) sym_addr
-
323 (((grub_int32_t
) sym_addr
+ 0x800) & 0xfffff000);
324 grub_uint32_t imm11_5
= (lo12
& 0xfe0) << (31 - 11);
325 grub_uint32_t imm4_0
= (lo12
& 0x1f) << (11 - 4);
326 *abs_place
= (*abs_place
& 0x1fff07f) | imm11_5
| imm4_0
;
333 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
334 N_("relocation 0x%x is not implemented yet"),
335 ELF_R_TYPE (rel
->r_info
));
339 return GRUB_ERR_NONE
;