dl.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. /* dl-386.c - arch-dependent part of loadable module support */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2002,2005,2007,2009 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/cpu/types.h>
  24. #include <grub/mm.h>
  25. #include <grub/i18n.h>
  26. /* Dummy __gnu_local_gp. Resolved by linker. */
  27. static char __gnu_local_gp_dummy;
  28. static char _gp_disp_dummy;
  29. /* Check if EHDR is a valid ELF header. */
  30. grub_err_t
  31. grub_arch_dl_check_header (void *ehdr)
  32. {
  33. Elf_Ehdr *e = ehdr;
  34. /* Check the magic numbers. */
  35. #ifdef GRUB_CPU_WORDS_BIGENDIAN
  36. if (e->e_ident[EI_CLASS] != ELFCLASS32
  37. || e->e_ident[EI_DATA] != ELFDATA2MSB
  38. || e->e_machine != EM_MIPS)
  39. #else
  40. if (e->e_ident[EI_CLASS] != ELFCLASS32
  41. || e->e_ident[EI_DATA] != ELFDATA2LSB
  42. || e->e_machine != EM_MIPS)
  43. #endif
  44. return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic"));
  45. return GRUB_ERR_NONE;
  46. }
  47. #pragma GCC diagnostic ignored "-Wcast-align"
  48. grub_err_t
  49. grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
  50. grub_size_t *got)
  51. {
  52. const Elf_Ehdr *e = ehdr;
  53. const Elf_Shdr *s;
  54. /* FIXME: suboptimal. */
  55. grub_size_t gp_size = 0;
  56. unsigned i;
  57. *tramp = 0;
  58. *got = 0;
  59. for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff);
  60. i < e->e_shnum;
  61. i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize))
  62. if (s->sh_type == SHT_REL)
  63. {
  64. const Elf_Rel *rel, *max;
  65. for (rel = (const Elf_Rel *) ((const char *) e + s->sh_offset),
  66. max = rel + s->sh_size / s->sh_entsize;
  67. rel < max;
  68. rel++)
  69. switch (ELF_R_TYPE (rel->r_info))
  70. {
  71. case R_MIPS_GOT16:
  72. case R_MIPS_CALL16:
  73. case R_MIPS_GPREL32:
  74. gp_size += 4;
  75. break;
  76. }
  77. }
  78. if (gp_size > 0x08000)
  79. return grub_error (GRUB_ERR_OUT_OF_RANGE, "__gnu_local_gp is too big\n");
  80. *got = gp_size;
  81. return GRUB_ERR_NONE;
  82. }
  83. /* Relocate symbols. */
  84. grub_err_t
  85. grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
  86. Elf_Shdr *s, grub_dl_segment_t seg)
  87. {
  88. grub_uint32_t gp0;
  89. Elf_Ehdr *e = ehdr;
  90. if (!mod->reginfo)
  91. {
  92. unsigned i;
  93. Elf_Shdr *ri;
  94. /* Find reginfo. */
  95. for (i = 0, ri = (Elf_Shdr *) ((char *) ehdr + e->e_shoff);
  96. i < e->e_shnum;
  97. i++, ri = (Elf_Shdr *) ((char *) ri + e->e_shentsize))
  98. if (ri->sh_type == SHT_MIPS_REGINFO)
  99. break;
  100. if (i == e->e_shnum)
  101. return grub_error (GRUB_ERR_BAD_MODULE, "no reginfo found");
  102. mod->reginfo = (grub_uint32_t *)((char *) ehdr + ri->sh_offset);
  103. }
  104. gp0 = mod->reginfo[5];
  105. Elf_Rel *rel, *max;
  106. for (rel = (Elf_Rel *) ((char *) e + s->sh_offset),
  107. max = (Elf_Rel *) ((char *) rel + s->sh_size);
  108. rel < max;
  109. rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
  110. {
  111. grub_uint8_t *addr;
  112. Elf_Sym *sym;
  113. grub_uint32_t sym_value;
  114. if (seg->size < rel->r_offset)
  115. return grub_error (GRUB_ERR_BAD_MODULE,
  116. "reloc offset is out of the segment");
  117. addr = (grub_uint8_t *) ((char *) seg->addr + rel->r_offset);
  118. sym = (Elf_Sym *) ((char *) mod->symtab
  119. + mod->symsize * ELF_R_SYM (rel->r_info));
  120. sym_value = sym->st_value;
  121. if (s->sh_type == SHT_RELA)
  122. {
  123. sym_value += ((Elf_Rela *) rel)->r_addend;
  124. }
  125. if (sym_value == (grub_addr_t) &__gnu_local_gp_dummy)
  126. sym_value = (grub_addr_t) mod->got;
  127. else if (sym_value == (grub_addr_t) &_gp_disp_dummy)
  128. {
  129. sym_value = (grub_addr_t) mod->got - (grub_addr_t) addr;
  130. if (ELF_R_TYPE (rel->r_info) == R_MIPS_LO16)
  131. /* ABI mandates +4 even if partner lui doesn't
  132. immediately precede addiu. */
  133. sym_value += 4;
  134. }
  135. switch (ELF_R_TYPE (rel->r_info))
  136. {
  137. case R_MIPS_HI16:
  138. {
  139. grub_uint32_t value;
  140. Elf_Rel *rel2;
  141. #ifdef GRUB_CPU_WORDS_BIGENDIAN
  142. addr += 2;
  143. #endif
  144. /* Handle partner lo16 relocation. Lower part is
  145. treated as signed. Hence add 0x8000 to compensate.
  146. */
  147. value = (*(grub_uint16_t *) addr << 16)
  148. + sym_value + 0x8000;
  149. for (rel2 = rel + 1; rel2 < max; rel2++)
  150. if (ELF_R_SYM (rel2->r_info)
  151. == ELF_R_SYM (rel->r_info)
  152. && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16)
  153. {
  154. value += *(grub_int16_t *)
  155. ((char *) seg->addr + rel2->r_offset
  156. #ifdef GRUB_CPU_WORDS_BIGENDIAN
  157. + 2
  158. #endif
  159. );
  160. break;
  161. }
  162. *(grub_uint16_t *) addr = (value >> 16) & 0xffff;
  163. }
  164. break;
  165. case R_MIPS_LO16:
  166. #ifdef GRUB_CPU_WORDS_BIGENDIAN
  167. addr += 2;
  168. #endif
  169. *(grub_uint16_t *) addr += sym_value & 0xffff;
  170. break;
  171. case R_MIPS_32:
  172. *(grub_uint32_t *) addr += sym_value;
  173. break;
  174. case R_MIPS_GPREL32:
  175. *(grub_uint32_t *) addr = sym_value
  176. + *(grub_uint32_t *) addr + gp0 - (grub_uint32_t)mod->got;
  177. break;
  178. case R_MIPS_26:
  179. {
  180. grub_uint32_t value;
  181. grub_uint32_t raw;
  182. raw = (*(grub_uint32_t *) addr) & 0x3ffffff;
  183. value = raw << 2;
  184. value += sym_value;
  185. raw = (value >> 2) & 0x3ffffff;
  186. *(grub_uint32_t *) addr =
  187. raw | ((*(grub_uint32_t *) addr) & 0xfc000000);
  188. }
  189. break;
  190. case R_MIPS_GOT16:
  191. if (ELF_ST_BIND (sym->st_info) == STB_LOCAL)
  192. {
  193. Elf_Rel *rel2;
  194. /* Handle partner lo16 relocation. Lower part is
  195. treated as signed. Hence add 0x8000 to compensate.
  196. */
  197. sym_value += (*(grub_uint16_t *) addr << 16)
  198. + 0x8000;
  199. for (rel2 = rel + 1; rel2 < max; rel2++)
  200. if (ELF_R_SYM (rel2->r_info)
  201. == ELF_R_SYM (rel->r_info)
  202. && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16)
  203. {
  204. sym_value += *(grub_int16_t *)
  205. ((char *) seg->addr + rel2->r_offset
  206. #ifdef GRUB_CPU_WORDS_BIGENDIAN
  207. + 2
  208. #endif
  209. );
  210. break;
  211. }
  212. sym_value &= 0xffff0000;
  213. *(grub_uint16_t *) addr = 0;
  214. }
  215. /* Fallthrough. */
  216. case R_MIPS_CALL16:
  217. {
  218. grub_uint32_t *gpptr = mod->gotptr;
  219. /* FIXME: reuse*/
  220. #ifdef GRUB_CPU_WORDS_BIGENDIAN
  221. addr += 2;
  222. #endif
  223. *gpptr = sym_value + *(grub_uint16_t *) addr;
  224. *(grub_uint16_t *) addr
  225. = sizeof (grub_uint32_t) * (gpptr - (grub_uint32_t *) mod->got);
  226. mod->gotptr = gpptr + 1;
  227. break;
  228. }
  229. case R_MIPS_JALR:
  230. break;
  231. default:
  232. {
  233. return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
  234. N_("relocation 0x%x is not implemented yet"),
  235. ELF_R_TYPE (rel->r_info));
  236. }
  237. break;
  238. }
  239. }
  240. return GRUB_ERR_NONE;
  241. }
  242. void
  243. grub_arch_dl_init_linker (void)
  244. {
  245. grub_dl_register_symbol ("__gnu_local_gp", &__gnu_local_gp_dummy, 0, 0);
  246. grub_dl_register_symbol ("_gp_disp", &_gp_disp_dummy, 0, 0);
  247. }