module.c 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. /*
  2. * Port on Texas Instruments TMS320C6x architecture
  3. *
  4. * Copyright (C) 2005, 2009, 2010, 2011 Texas Instruments Incorporated
  5. * Author: Thomas Charleux (thomas.charleux@jaluna.com)
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License version 2 as
  9. * published by the Free Software Foundation.
  10. *
  11. */
  12. #include <linux/moduleloader.h>
  13. #include <linux/elf.h>
  14. #include <linux/vmalloc.h>
  15. #include <linux/kernel.h>
  16. static inline int fixup_pcr(u32 *ip, Elf32_Addr dest, u32 maskbits, int shift)
  17. {
  18. u32 opcode;
  19. long ep = (long)ip & ~31;
  20. long delta = ((long)dest - ep) >> 2;
  21. long mask = (1 << maskbits) - 1;
  22. if ((delta >> (maskbits - 1)) == 0 ||
  23. (delta >> (maskbits - 1)) == -1) {
  24. opcode = *ip;
  25. opcode &= ~(mask << shift);
  26. opcode |= ((delta & mask) << shift);
  27. *ip = opcode;
  28. pr_debug("REL PCR_S%d[%p] dest[%p] opcode[%08x]\n",
  29. maskbits, ip, (void *)dest, opcode);
  30. return 0;
  31. }
  32. pr_err("PCR_S%d reloc %p -> %p out of range!\n",
  33. maskbits, ip, (void *)dest);
  34. return -1;
  35. }
  36. /*
  37. * apply a RELA relocation
  38. */
  39. int apply_relocate_add(Elf32_Shdr *sechdrs,
  40. const char *strtab,
  41. unsigned int symindex,
  42. unsigned int relsec,
  43. struct module *me)
  44. {
  45. Elf32_Rela *rel = (void *) sechdrs[relsec].sh_addr;
  46. Elf_Sym *sym;
  47. u32 *location, opcode;
  48. unsigned int i;
  49. Elf32_Addr v;
  50. Elf_Addr offset = 0;
  51. pr_debug("Applying relocate section %u to %u with offset 0x%x\n",
  52. relsec, sechdrs[relsec].sh_info, offset);
  53. for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
  54. /* This is where to make the change */
  55. location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
  56. + rel[i].r_offset - offset;
  57. /* This is the symbol it is referring to. Note that all
  58. undefined symbols have been resolved. */
  59. sym = (Elf_Sym *)sechdrs[symindex].sh_addr
  60. + ELF32_R_SYM(rel[i].r_info);
  61. /* this is the adjustment to be made */
  62. v = sym->st_value + rel[i].r_addend;
  63. switch (ELF32_R_TYPE(rel[i].r_info)) {
  64. case R_C6000_ABS32:
  65. pr_debug("RELA ABS32: [%p] = 0x%x\n", location, v);
  66. *location = v;
  67. break;
  68. case R_C6000_ABS16:
  69. pr_debug("RELA ABS16: [%p] = 0x%x\n", location, v);
  70. *(u16 *)location = v;
  71. break;
  72. case R_C6000_ABS8:
  73. pr_debug("RELA ABS8: [%p] = 0x%x\n", location, v);
  74. *(u8 *)location = v;
  75. break;
  76. case R_C6000_ABS_L16:
  77. opcode = *location;
  78. opcode &= ~0x7fff80;
  79. opcode |= ((v & 0xffff) << 7);
  80. pr_debug("RELA ABS_L16[%p] v[0x%x] opcode[0x%x]\n",
  81. location, v, opcode);
  82. *location = opcode;
  83. break;
  84. case R_C6000_ABS_H16:
  85. opcode = *location;
  86. opcode &= ~0x7fff80;
  87. opcode |= ((v >> 9) & 0x7fff80);
  88. pr_debug("RELA ABS_H16[%p] v[0x%x] opcode[0x%x]\n",
  89. location, v, opcode);
  90. *location = opcode;
  91. break;
  92. case R_C6000_PCR_S21:
  93. if (fixup_pcr(location, v, 21, 7))
  94. return -ENOEXEC;
  95. break;
  96. case R_C6000_PCR_S12:
  97. if (fixup_pcr(location, v, 12, 16))
  98. return -ENOEXEC;
  99. break;
  100. case R_C6000_PCR_S10:
  101. if (fixup_pcr(location, v, 10, 13))
  102. return -ENOEXEC;
  103. break;
  104. default:
  105. pr_err("module %s: Unknown RELA relocation: %u\n",
  106. me->name, ELF32_R_TYPE(rel[i].r_info));
  107. return -ENOEXEC;
  108. }
  109. }
  110. return 0;
  111. }