{simple,shoddy,smart} minsize-oriented linker
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

232 lines
7.3 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. import sys
  2. from collections import OrderedDict
  3. from .shared import *
  4. fetch_width_from_bits = { 8: 'byte', 16: 'word', 32: 'dword', 64: 'qword' }
  5. def get_min_check_width(libraries, hashfn):
  6. minv = 8 # can't go lower
  7. for k, v in libraries.items():
  8. for sym in v:
  9. hv = hashfn(sym)
  10. #eprintf("hash: 0x%08x of %s"%(hv,sym))
  11. if (hv & 0xffffffff) == 0:
  12. # this should (hopefully) NEVER happen
  13. error("E: Aiee, all-zero hash for sym '%s'!" % sym)
  14. elif (hv & 0xFFFF) == 0:
  15. #eprintf("32-bit hash")
  16. minv = max(minv, 32) # need at least 32 bits
  17. elif (hv & 0xFF) == 0:
  18. #eprintf("16-bit hash")
  19. minv = max(minv, 16) # need at least 16 bits
  20. return minv
  21. def sort_imports(libraries, hashfn):
  22. #eprintf("in: " + str(libraries))
  23. # DON'T DO THIS: weak symbol stuff etc.
  24. ## sort libs by name length, then by name
  25. #ll = sorted(libraries.items(), key=lambda ls: (len(ls[0]), ls[0]))
  26. #for i in range(len(ll)):
  27. # # sort symbols by hash value
  28. # ll[i] = (ll[i][0], sorted(ll[i][1], key=lambda sr: hashfn(sr[0])))
  29. #eprintf("out:" + str(dict(ll)))
  30. # insertion order only works with python >=3.6!
  31. #if sys.version_info < (3, 6): return OrderedDict(ll)
  32. #else: return dict(ll)
  33. for k, v in libraries.items():
  34. libraries[k] = OrderedDict(sorted(v.items(), key=lambda sr: hashfn(sr[0])))
  35. return libraries
  36. def output_x86(libraries, nx, hashid, outf, det):
  37. outf.write('; vim: set ft=nasm:\n') # be friendly
  38. defff = define_for_hash[hashid]
  39. if defff is not None:
  40. outf.write('%define {} 1\n'.format(defff))
  41. if nx:
  42. outf.write('%define USE_NX 1\n')
  43. hashfn = get_hash_fn(hashid)
  44. if det: libraries = sort_imports(libraries, hashfn)
  45. outf.write('%%define HASH_END_TYP %s\n' %
  46. fetch_width_from_bits[get_min_check_width(libraries, hashfn)])
  47. usedrelocs = set()
  48. for library, symrels in libraries.items():
  49. for sym, reloc in symrels.items():
  50. usedrelocs.add(reloc)
  51. if not(nx) and 'R_386_PC32' in usedrelocs and 'R_386_GOT32X' in usedrelocs:
  52. error("Using a mix of R_386_PC32 and R_386_GOT32X relocations! "+\
  53. "Please change a few C compiler flags and recompile your code.")
  54. use_jmp_bytes = not nx and 'R_386_PC32' in usedrelocs
  55. if use_jmp_bytes:
  56. outf.write('%define USE_JMP_BYTES 1\n')
  57. outf.write('bits 32\n')
  58. shorts = { l: l.split('.', 1)[0].lower().replace('-', '_') for l in libraries }
  59. outf.write('%include "header32.asm"\n')
  60. outf.write('dynamic.needed:\n')
  61. for library in libraries:
  62. outf.write('dd 1;DT_NEEDED\n')
  63. outf.write('dd (_symbols.{} - _strtab)\n'.format(shorts[library]))
  64. outf.write("""\
  65. dynamic.end:
  66. %ifndef UNSAFE_DYNAMIC
  67. dd DT_NULL
  68. %endif
  69. """)
  70. outf.write('[section .rodata.neededlibs]\n')
  71. outf.write('global _strtab\n')
  72. outf.write('_strtab:\n')
  73. for library, symrels in libraries.items():
  74. outf.write('\t_symbols.{}: db "{}",0\n'.format(shorts[library], library))
  75. outf.write('[section .data.smolgot]\n')
  76. if not nx:
  77. outf.write('[section .text.smolplt]\n')
  78. outf.write('global _symbols\n')
  79. outf.write('_symbols:\n')
  80. for library, symrels in libraries.items():
  81. for sym, reloc in symrels.items():
  82. # meh
  83. if reloc != 'R_386_PC32' and reloc != 'R_386_GOT32X':
  84. eprintf('Relocation type %s of symbol %s unsupported!' % (reloc, sym))
  85. sys.exit(1)
  86. if nx:
  87. outf.write("\t\t_symbols.{lib}.{name}: dd 0x{hash:x}"\
  88. .format(lib=shorts[library],name=sym,hash=hashfn(sym)).lstrip('\n'))
  89. else:
  90. outf.write(("""\
  91. \t\tglobal {name}
  92. \t\t{name}:""" + ("\n\t\t\tdb 0xE9" if use_jmp_bytes else '') + """
  93. \t\t\tdd 0x{hash:x}
  94. """).format(name=sym, hash=hashfn(sym)).lstrip('\n'))
  95. outf.write('db 0\n')
  96. outf.write('_symbols.end:\n')
  97. if nx:
  98. outf.write('global _smolplt\n')
  99. outf.write('_smolplt:\n')
  100. for library, symrels in libraries.items():
  101. for sym, reloc in symrels.items():
  102. outf.write("""\
  103. [section .text.smolplt.{name}]
  104. global {name}
  105. {name}:
  106. \tjmp [dword _symbols.{lib}.{name}]
  107. """.format(lib=shorts[library],name=sym).lstrip('\n'))
  108. outf.write('_smolplt.end:\n')
  109. outf.write('%include "loader32.asm"\n')
  110. # end output_x86
  111. def output_amd64(libraries, nx, hashid, outf, det):
  112. if hashid == HASH_BSD2:
  113. error("--hash16 not supported yet for x86_64 outputs.")
  114. outf.write('; vim: set ft=nasm:\n')
  115. outf.write('bits 64\n')
  116. defff = define_for_hash[hashid]
  117. if defff is not None:
  118. outf.write('%define {} 1\n'.format(defff))
  119. if nx:
  120. outf.write('%define USE_NX 1\n')
  121. hashfn = get_hash_fn(hashid)
  122. if det: libraries = sort_imports(libraries, hashfn)
  123. outf.write('%%define HASH_END_TYP %s\n' %
  124. fetch_width_from_bits[get_min_check_width(libraries, hashfn)])
  125. shorts = { l: l.split('.', 1)[0].lower().replace('-', '_') for l in libraries }
  126. outf.write('%include "header64.asm"\n')
  127. outf.write('dynamic.needed:\n')
  128. for library in libraries:
  129. outf.write(' dq 1;DT_NEEDED\n')
  130. outf.write(' dq (_symbols.{} - _strtab)\n'.format(shorts[library]))
  131. outf.write("""\
  132. dynamic.symtab:
  133. dq DT_SYMTAB ; d_tag
  134. dq 0 ; d_un.d_ptr
  135. dynamic.end:
  136. %ifndef UNSAFE_DYNAMIC
  137. dq DT_NULL
  138. %endif
  139. """)
  140. outf.write('[section .rodata.neededlibs]\n')
  141. outf.write('global _strtab\n')
  142. outf.write('_strtab:\n')
  143. for library, symrels in libraries.items():
  144. outf.write('\t_symbols.{}: db "{}",0\n'.format(shorts[library], library))
  145. outf.write('[section .data.smolgot]\n')
  146. outf.write('global _symbols\n')
  147. outf.write('_symbols:\n')
  148. for library, symrels in libraries.items():
  149. for sym, reloc in symrels.items():
  150. if reloc not in ['R_X86_64_PLT32', 'R_X86_64_GOTPCRELX', \
  151. 'R_X86_64_REX_GOTPCRELX', 'R_X86_64_GOTPCREL']:
  152. error('Relocation type %s of symbol %s unsupported!' % (reloc, sym))
  153. if reloc in ['R_X86_64_GOTPCRELX', 'R_X86_64_REX_GOTPCRELX', \
  154. 'R_X86_64_GOTPCREL']:
  155. outf.write("""
  156. global {name}
  157. {name}:
  158. """.format(name=sym).lstrip('\n'))
  159. outf.write('\t\t_symbols.{lib}.{name}: dq 0x{hash:x}\n'\
  160. .format(lib=shorts[library],name=sym,hash=hashfn(sym)))
  161. outf.write('db 0\n')
  162. outf.write('_symbols.end:\n')
  163. outf.write('global _smolplt\n')
  164. outf.write('_smolplt:\n')
  165. for library, symrels in libraries.items():
  166. for sym, reloc in symrels.items():
  167. if reloc == 'R_X86_64_PLT32':
  168. outf.write("""\
  169. [section .text.smolplt.{name}]
  170. global {name}
  171. {name}:
  172. \tjmp [rel _symbols.{lib}.{name}]
  173. """.format(lib=shorts[library],name=sym).lstrip('\n'))
  174. outf.write('_smolplt.end:\n')
  175. outf.write('%include "loader64.asm"\n')
  176. # end output_amd64
  177. def output(arch, libraries, nx, hashid, outf, det):
  178. if arch == 'i386': output_x86(libraries, nx, hashid, outf, det)
  179. elif arch == 'x86_64': output_amd64(libraries, nx, hashid, outf, det)
  180. else:
  181. error("E: cannot emit for arch '%s'" % str(arch))