aot_reloc_xtensa.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /*
  2. * Copyright (C) 2019 Intel Corporation. All rights reserved.
  3. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. */
  5. #include "aot_reloc.h"
  6. #define R_XTENSA_32 1 /* Direct 32 bit */
  7. #define R_XTENSA_SLOT0_OP 20 /* PC relative */
  8. /* clang-format off */
  9. /* for soft-float */
  10. void __floatsidf(void);
  11. void __divdf3(void);
  12. void __ltdf2(void);
  13. /* for mul32 */
  14. void __mulsi3(void);
  15. void __muldi3(void);
  16. void __modsi3(void);
  17. void __divdi3(void);
  18. void __udivdi3(void);
  19. void __unorddf2(void);
  20. void __adddf3(void);
  21. void __eqdf2(void);
  22. void __muldf3(void);
  23. void __gedf2(void);
  24. void __ledf2(void);
  25. void __fixunsdfsi(void);
  26. void __floatunsidf(void);
  27. void __subdf3(void);
  28. void __nedf2(void);
  29. void __fixdfsi(void);
  30. void __moddi3(void);
  31. void __extendsfdf2(void);
  32. void __truncdfsf2(void);
  33. void __gtdf2(void);
  34. void __umoddi3(void);
  35. void __floatdidf(void);
  36. void __divsf3(void);
  37. void __fixdfdi(void);
  38. void __floatundidf(void);
  39. void __fixsfdi(void);
  40. void __fixunssfdi(void);
  41. void __fixunsdfdi(void);
  42. void __floatdisf(void);
  43. void __floatundisf(void);
  44. static SymbolMap target_sym_map[] = {
  45. REG_COMMON_SYMBOLS
  46. /* API's for soft-float */
  47. /* TODO: only register these symbols when Floating-Point Coprocessor
  48. * Option is not enabled */
  49. REG_SYM(__floatsidf),
  50. REG_SYM(__divdf3),
  51. REG_SYM(__ltdf2),
  52. /* API's for 32-bit integer multiply */
  53. /* TODO: only register these symbols when 32-bit Integer Multiply Option
  54. * is not enabled */
  55. REG_SYM(__mulsi3),
  56. REG_SYM(__muldi3),
  57. REG_SYM(__modsi3),
  58. REG_SYM(__divdi3),
  59. REG_SYM(__udivdi3),
  60. REG_SYM(__unorddf2),
  61. REG_SYM(__adddf3),
  62. REG_SYM(__eqdf2),
  63. REG_SYM(__muldf3),
  64. REG_SYM(__gedf2),
  65. REG_SYM(__ledf2),
  66. REG_SYM(__fixunsdfsi),
  67. REG_SYM(__floatunsidf),
  68. REG_SYM(__subdf3),
  69. REG_SYM(__nedf2),
  70. REG_SYM(__fixdfsi),
  71. REG_SYM(__moddi3),
  72. REG_SYM(__extendsfdf2),
  73. REG_SYM(__truncdfsf2),
  74. REG_SYM(__gtdf2),
  75. REG_SYM(__umoddi3),
  76. REG_SYM(__floatdidf),
  77. REG_SYM(__divsf3),
  78. REG_SYM(__fixdfdi),
  79. REG_SYM(__floatundidf),
  80. REG_SYM(__fixsfdi),
  81. REG_SYM(__fixunssfdi),
  82. REG_SYM(__fixunsdfdi),
  83. REG_SYM(__floatdisf),
  84. REG_SYM(__floatundisf),
  85. };
  86. /* clang-format on */
  87. static void
  88. set_error_buf(char *error_buf, uint32 error_buf_size, const char *string)
  89. {
  90. if (error_buf != NULL)
  91. snprintf(error_buf, error_buf_size, "%s", string);
  92. }
  93. SymbolMap *
  94. get_target_symbol_map(uint32 *sym_num)
  95. {
  96. *sym_num = sizeof(target_sym_map) / sizeof(SymbolMap);
  97. return target_sym_map;
  98. }
  99. void
  100. get_current_target(char *target_buf, uint32 target_buf_size)
  101. {
  102. snprintf(target_buf, target_buf_size, "xtensa");
  103. }
  104. static uint32
  105. get_plt_item_size(void)
  106. {
  107. return 0;
  108. }
  109. void
  110. init_plt_table(uint8 *plt)
  111. {
  112. (void)plt;
  113. }
  114. uint32
  115. get_plt_table_size()
  116. {
  117. return get_plt_item_size() * (sizeof(target_sym_map) / sizeof(SymbolMap));
  118. }
  119. static bool
  120. check_reloc_offset(uint32 target_section_size, uint64 reloc_offset,
  121. uint32 reloc_data_size, char *error_buf,
  122. uint32 error_buf_size)
  123. {
  124. if (!(reloc_offset < (uint64)target_section_size
  125. && reloc_offset + reloc_data_size <= (uint64)target_section_size)) {
  126. set_error_buf(error_buf, error_buf_size,
  127. "AOT module load failed: invalid relocation offset.");
  128. return false;
  129. }
  130. return true;
  131. }
  132. /*
  133. * CPU like esp32 can read and write data through the instruction bus, but only
  134. * in a word aligned manner; non-word-aligned access will cause a CPU exception.
  135. * This function uses a world aligned manner to write 16bit value to instruction
  136. * address.
  137. */
  138. static void
  139. put_imm16_to_addr(int16 imm16, int16 *addr)
  140. {
  141. int8 bytes[8];
  142. int32 *addr_aligned1, *addr_aligned2;
  143. addr_aligned1 = (int32 *)((intptr_t)addr & ~3);
  144. if ((intptr_t)addr % 4 != 3) {
  145. *(int32 *)bytes = *addr_aligned1;
  146. *(int16 *)(bytes + ((intptr_t)addr % 4)) = imm16;
  147. *addr_aligned1 = *(int32 *)bytes;
  148. }
  149. else {
  150. addr_aligned2 = (int32 *)(((intptr_t)addr + 3) & ~3);
  151. *(int32 *)bytes = *addr_aligned1;
  152. *(int32 *)(bytes + 4) = *addr_aligned2;
  153. *(int16 *)(bytes + 3) = imm16;
  154. memcpy(addr_aligned1, bytes, 8);
  155. }
  156. }
  157. static union {
  158. int a;
  159. char b;
  160. } __ue = { .a = 1 };
  161. #define is_little_endian() (__ue.b == 1)
  162. #if !defined(__packed)
  163. /*
  164. * Note: This version check is a bit relaxed.
  165. * The __packed__ attribute has been there since gcc 2 era.
  166. */
  167. #if __GNUC__ >= 3
  168. #define __packed __attribute__((__packed__))
  169. #endif
  170. #endif
  171. typedef union {
  172. struct l32r_le {
  173. int8 other;
  174. int16 imm16;
  175. } __packed l;
  176. struct l32r_be {
  177. int16 imm16;
  178. int8 other;
  179. } __packed b;
  180. } l32r_insn_t;
  181. bool
  182. apply_relocation(AOTModule *module, uint8 *target_section_addr,
  183. uint32 target_section_size, uint64 reloc_offset,
  184. int64 reloc_addend, uint32 reloc_type, void *symbol_addr,
  185. int32 symbol_index, char *error_buf, uint32 error_buf_size)
  186. {
  187. switch (reloc_type) {
  188. case R_XTENSA_32:
  189. {
  190. uint8 *insn_addr = target_section_addr + reloc_offset;
  191. #if (WASM_MEM_DUAL_BUS_MIRROR != 0)
  192. insn_addr = os_get_dbus_mirror((void *)insn_addr);
  193. bh_assert(insn_addr != NULL);
  194. #endif
  195. int32 initial_addend;
  196. /* (S + A) */
  197. if ((intptr_t)insn_addr & 3) {
  198. set_error_buf(error_buf, error_buf_size,
  199. "AOT module load failed: "
  200. "instruction address unaligned.");
  201. return false;
  202. }
  203. CHECK_RELOC_OFFSET(4);
  204. initial_addend = *(int32 *)insn_addr;
  205. *(uintptr_t *)insn_addr = (uintptr_t)symbol_addr + initial_addend
  206. + (intptr_t)reloc_addend;
  207. break;
  208. }
  209. case R_XTENSA_SLOT0_OP:
  210. {
  211. uint8 *insn_addr = target_section_addr + reloc_offset;
  212. /* Currently only l32r instruction generates R_XTENSA_SLOT0_OP
  213. * relocation */
  214. l32r_insn_t *l32r_insn = (l32r_insn_t *)insn_addr;
  215. uint8 *reloc_addr;
  216. int32 relative_offset /*, initial_addend */;
  217. int16 imm16;
  218. CHECK_RELOC_OFFSET(3); /* size of l32r instruction */
  219. /*
  220. imm16 = is_little_endian() ?
  221. l32r_insn->l.imm16 : l32r_insn->b.imm16;
  222. initial_addend = (int32)imm16 << 2;
  223. */
  224. reloc_addr =
  225. (uint8 *)((uintptr_t)symbol_addr + (intptr_t)reloc_addend);
  226. if ((intptr_t)reloc_addr & 3) {
  227. set_error_buf(error_buf, error_buf_size,
  228. "AOT module load failed: "
  229. "relocation address unaligned.");
  230. return false;
  231. }
  232. relative_offset =
  233. (int32)((intptr_t)reloc_addr
  234. - (((intptr_t)insn_addr + 3) & ~(intptr_t)3));
  235. /* relative_offset += initial_addend; */
  236. /* check relative offset boundary */
  237. if (relative_offset < -256 * BH_KB || relative_offset > -4) {
  238. set_error_buf(error_buf, error_buf_size,
  239. "AOT module load failed: "
  240. "target address out of range.\n"
  241. "Try using `wamrc --size-level=0` to generate "
  242. ".literal island.");
  243. return false;
  244. }
  245. #if (WASM_MEM_DUAL_BUS_MIRROR != 0)
  246. insn_addr = os_get_dbus_mirror((void *)insn_addr);
  247. bh_assert(insn_addr != NULL);
  248. l32r_insn = (l32r_insn_t *)insn_addr;
  249. #endif
  250. imm16 = (int16)(relative_offset >> 2);
  251. /* write back the imm16 to the l32r instruction */
  252. /* GCC >= 9 complains if we have a pointer that could be
  253. * unaligned. This can happen because the struct is packed.
  254. * These pragma are to suppress the warnings because the
  255. * function put_imm16_to_addr already handles unaligned
  256. * pointers correctly. */
  257. #if __GNUC__ >= 9
  258. #pragma GCC diagnostic push
  259. #pragma GCC diagnostic ignored "-Waddress-of-packed-member"
  260. #endif
  261. if (is_little_endian())
  262. put_imm16_to_addr(imm16, &l32r_insn->l.imm16);
  263. else
  264. put_imm16_to_addr(imm16, &l32r_insn->b.imm16);
  265. #if __GNUC__ >= 9
  266. #pragma GCC diagnostic pop
  267. #endif
  268. break;
  269. }
  270. default:
  271. if (error_buf != NULL)
  272. snprintf(error_buf, error_buf_size,
  273. "Load relocation section failed: "
  274. "invalid relocation type %d.",
  275. (int)reloc_type);
  276. return false;
  277. }
  278. return true;
  279. }