dl.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. /* dl.c - arch-dependent part of loadable module support */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2013 Free Software Foundation, Inc.
  5. *
  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.
  10. *
  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.
  15. *
  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/>.
  18. */
  19. #include <grub/dl.h>
  20. #include <grub/elf.h>
  21. #include <grub/misc.h>
  22. #include <grub/err.h>
  23. #include <grub/mm.h>
  24. #include <grub/i18n.h>
  25. #include <grub/arm/reloc.h>
  26. struct trampoline_arm
  27. {
  28. #define ARM_LOAD_IP 0xe59fc000
  29. #define ARM_BX 0xe12fff1c
  30. #define ARM_MOV_PC 0xe1a0f00c
  31. grub_uint32_t load_ip; /* ldr ip, [pc] */
  32. grub_uint32_t bx; /* bx ip or mov pc, ip*/
  33. grub_uint32_t addr;
  34. };
  35. static grub_uint16_t thumb_template[8] =
  36. {
  37. 0x468c, /* mov ip, r1 */
  38. 0x4903, /* ldr r1, [pc, #12] ; (10 <.text+0x10>) */
  39. /* Exchange R1 and IP in limited Thumb instruction set.
  40. IP gets negated but we compensate it by C code. */
  41. /* R1 IP */
  42. /* -A R1 */
  43. 0x4461, /* add r1, ip */ /* R1-A R1 */
  44. 0x4249, /* negs r1, r1 */ /* A-R1 R1 */
  45. 0x448c, /* add ip, r1 */ /* A-R1 A */
  46. 0x4249, /* negs r1, r1 */ /* R1-A A */
  47. 0x4461, /* add r1, ip */ /* R1 A */
  48. 0x4760 /* bx ip */
  49. };
  50. struct trampoline_thumb
  51. {
  52. grub_uint16_t template[8];
  53. grub_uint32_t neg_addr;
  54. };
  55. #pragma GCC diagnostic ignored "-Wcast-align"
  56. grub_err_t
  57. grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
  58. grub_size_t *got)
  59. {
  60. const Elf_Ehdr *e = ehdr;
  61. const Elf_Shdr *s;
  62. unsigned i;
  63. *tramp = 0;
  64. *got = 0;
  65. for (i = 0, s = (const Elf_Shdr *) ((grub_addr_t) e + e->e_shoff);
  66. i < e->e_shnum;
  67. i++, s = (const Elf_Shdr *) ((grub_addr_t) s + e->e_shentsize))
  68. if (s->sh_type == SHT_REL)
  69. {
  70. const Elf_Rel *rel, *max;
  71. for (rel = (const Elf_Rel *) ((grub_addr_t) e + s->sh_offset),
  72. max = (const Elf_Rel *) ((grub_addr_t) rel + s->sh_size);
  73. rel + 1 <= max;
  74. rel = (const Elf_Rel *) ((grub_addr_t) rel + s->sh_entsize))
  75. switch (ELF_R_TYPE (rel->r_info))
  76. {
  77. case R_ARM_CALL:
  78. case R_ARM_JUMP24:
  79. {
  80. *tramp += sizeof (struct trampoline_arm);
  81. break;
  82. }
  83. case R_ARM_THM_CALL:
  84. case R_ARM_THM_JUMP24:
  85. case R_ARM_THM_JUMP19:
  86. {
  87. *tramp += sizeof (struct trampoline_thumb);
  88. break;
  89. }
  90. }
  91. }
  92. grub_dprintf ("dl", "trampoline size %x\n", *tramp);
  93. return GRUB_ERR_NONE;
  94. }
  95. /*************************************************
  96. * Runtime dynamic linker with helper functions. *
  97. *************************************************/
  98. grub_err_t
  99. grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
  100. Elf_Shdr *s, grub_dl_segment_t seg)
  101. {
  102. Elf_Rel *rel, *max;
  103. for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset),
  104. max = (Elf_Rel *) ((char *) rel + s->sh_size);
  105. rel < max;
  106. rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
  107. {
  108. Elf_Addr *target, sym_addr;
  109. grub_err_t retval;
  110. Elf_Sym *sym;
  111. if (seg->size < rel->r_offset)
  112. return grub_error (GRUB_ERR_BAD_MODULE,
  113. "reloc offset is out of the segment");
  114. target = (void *) ((char *) seg->addr + rel->r_offset);
  115. sym = (Elf_Sym *) ((char *) mod->symtab
  116. + mod->symsize * ELF_R_SYM (rel->r_info));
  117. sym_addr = sym->st_value;
  118. switch (ELF_R_TYPE (rel->r_info))
  119. {
  120. case R_ARM_ABS32:
  121. {
  122. /* Data will be naturally aligned */
  123. retval = grub_arm_reloc_abs32 (target, sym_addr);
  124. if (retval != GRUB_ERR_NONE)
  125. return retval;
  126. }
  127. break;
  128. case R_ARM_CALL:
  129. case R_ARM_JUMP24:
  130. {
  131. grub_int32_t offset;
  132. sym_addr += grub_arm_jump24_get_offset (target);
  133. offset = sym_addr - (grub_uint32_t) target;
  134. if ((sym_addr & 1) || !grub_arm_jump24_check_offset (offset))
  135. {
  136. struct trampoline_arm *tp = mod->trampptr;
  137. mod->trampptr = tp + 1;
  138. tp->load_ip = ARM_LOAD_IP;
  139. tp->bx = (sym_addr & 1) ? ARM_BX : ARM_MOV_PC;
  140. tp->addr = sym_addr + 8;
  141. offset = (grub_uint8_t *) tp - (grub_uint8_t *) target - 8;
  142. }
  143. if (!grub_arm_jump24_check_offset (offset))
  144. return grub_error (GRUB_ERR_BAD_MODULE,
  145. "trampoline out of range");
  146. grub_arm_jump24_set_offset (target, offset);
  147. }
  148. break;
  149. case R_ARM_THM_CALL:
  150. case R_ARM_THM_JUMP24:
  151. {
  152. /* Thumb instructions can be 16-bit aligned */
  153. grub_int32_t offset;
  154. sym_addr += grub_arm_thm_call_get_offset ((grub_uint16_t *) target);
  155. grub_dprintf ("dl", " sym_addr = 0x%08x\n", sym_addr);
  156. if (ELF_ST_TYPE (sym->st_info) != STT_FUNC)
  157. sym_addr |= 1;
  158. offset = sym_addr - (grub_uint32_t) target;
  159. grub_dprintf("dl", " BL*: target=%p, sym_addr=0x%08x, offset=%d\n",
  160. target, sym_addr, offset);
  161. if (!(sym_addr & 1) || (offset < -0x200000 || offset >= 0x200000))
  162. {
  163. struct trampoline_thumb *tp = mod->trampptr;
  164. mod->trampptr = tp + 1;
  165. grub_memcpy (tp->template, thumb_template, sizeof (tp->template));
  166. tp->neg_addr = -sym_addr - 4;
  167. offset = ((grub_uint8_t *) tp - (grub_uint8_t *) target - 4) | 1;
  168. }
  169. if (offset < -0x200000 || offset >= 0x200000)
  170. return grub_error (GRUB_ERR_BAD_MODULE,
  171. "trampoline out of range");
  172. grub_dprintf ("dl", " relative destination = %p\n",
  173. (char *) target + offset);
  174. retval = grub_arm_thm_call_set_offset ((grub_uint16_t *) target, offset);
  175. if (retval != GRUB_ERR_NONE)
  176. return retval;
  177. }
  178. break;
  179. /* Happens when compiled with -march=armv4. Since currently we need
  180. at least armv5, keep bx as-is.
  181. */
  182. case R_ARM_V4BX:
  183. break;
  184. case R_ARM_THM_MOVW_ABS_NC:
  185. case R_ARM_THM_MOVT_ABS:
  186. {
  187. grub_uint32_t offset;
  188. offset = grub_arm_thm_movw_movt_get_value((grub_uint16_t *) target);
  189. offset += sym_addr;
  190. if (ELF_R_TYPE (rel->r_info) == R_ARM_THM_MOVT_ABS)
  191. offset >>= 16;
  192. else
  193. offset &= 0xffff;
  194. grub_arm_thm_movw_movt_set_value((grub_uint16_t *) target, offset);
  195. }
  196. break;
  197. case R_ARM_THM_JUMP19:
  198. {
  199. /* Thumb instructions can be 16-bit aligned */
  200. grub_int32_t offset;
  201. sym_addr += grub_arm_thm_jump19_get_offset ((grub_uint16_t *) target);
  202. if (ELF_ST_TYPE (sym->st_info) != STT_FUNC)
  203. sym_addr |= 1;
  204. offset = sym_addr - (grub_uint32_t) target;
  205. if (!grub_arm_thm_jump19_check_offset (offset)
  206. || !(sym_addr & 1))
  207. {
  208. struct trampoline_thumb *tp = mod->trampptr;
  209. mod->trampptr = tp + 1;
  210. grub_memcpy (tp->template, thumb_template, sizeof (tp->template));
  211. tp->neg_addr = -sym_addr - 4;
  212. offset = ((grub_uint8_t *) tp - (grub_uint8_t *) target - 4) | 1;
  213. }
  214. if (!grub_arm_thm_jump19_check_offset (offset))
  215. return grub_error (GRUB_ERR_BAD_MODULE,
  216. "trampoline out of range");
  217. grub_arm_thm_jump19_set_offset ((grub_uint16_t *) target, offset);
  218. }
  219. break;
  220. default:
  221. return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
  222. N_("relocation 0x%x is not implemented yet"),
  223. ELF_R_TYPE (rel->r_info));
  224. }
  225. }
  226. return GRUB_ERR_NONE;
  227. }
  228. /*
  229. * Check if EHDR is a valid ELF header.
  230. */
  231. grub_err_t
  232. grub_arch_dl_check_header (void *ehdr)
  233. {
  234. Elf_Ehdr *e = ehdr;
  235. /* Check the magic numbers. */
  236. if (e->e_ident[EI_CLASS] != ELFCLASS32
  237. || e->e_ident[EI_DATA] != ELFDATA2LSB || e->e_machine != EM_ARM)
  238. return grub_error (GRUB_ERR_BAD_OS,
  239. N_("invalid arch-dependent ELF magic"));
  240. return GRUB_ERR_NONE;
  241. }