smol/src/smolemit.py

192 lines
5.8 KiB
Python

import sys
from collections import OrderedDict
from smolshared import *
def sort_imports(libraries, hashfn):
#eprintf("in: " + str(libraries))
# sort libs by name length, then by name
ll = sorted(libraries.items(), key=lambda ls: (len(ls[0]), ls[0]))
for i in range(len(ll)):
# sort symbols by hash value
ll[i] = (ll[i][0], sorted(ll[i][1], key=lambda sr: hashfn(sr[0])))
#eprintf("out:" + str(dict(ll)))
# insertion order only works with python >=3.6!
if sys.version_info < (3, 6): return OrderedDict(ll)
else: return dict(ll)
def output_x86(libraries, nx, h16, outf, det):
outf.write('; vim: set ft=nasm:\n') # be friendly
if nx: outf.write('%define USE_NX 1\n')
if h16: outf.write('%define USE_HASH16 1\n')
hashfn = hash_bsd2 if h16 else hash_djb2
if det: libraries = sort_imports(libraries, hashfn)
usedrelocs = set({})
for library, symrels in libraries.items():
for sym, reloc in symrels: usedrelocs.add(reloc)
if not(nx) and 'R_386_PC32' in usedrelocs and 'R_386_GOT32X' in usedrelocs:
eprintf("Using a mix of R_386_PC32 and R_386_GOT32X relocations! "+\
"Please change a few C compiler flags and recompile your code.")
exit(1)
use_jmp_bytes = not nx and 'R_386_PC32' in usedrelocs
if use_jmp_bytes:
outf.write('%define USE_JMP_BYTES 1\n')
outf.write('bits 32\n')
shorts = { l: l.split('.', 1)[0].lower().replace('-', '_') for l in libraries }
outf.write('%include "header32.asm"\n')
outf.write('dynamic.needed:\n')
for library in libraries:
outf.write('dd 1;DT_NEEDED\n')
outf.write('dd (_symbols.{} - _strtab)\n'.format(shorts[library]))
outf.write("""\
dynamic.end:
%ifndef UNSAFE_DYNAMIC
dd DT_NULL
%endif
""")
outf.write('[section .rodata.neededlibs]\n')
outf.write('_strtab:\n')
for library, symrels in libraries.items():
outf.write('\t_symbols.{}: db "{}",0\n'.format(shorts[library], library))
outf.write('[section .data.smolgot]\n')
if not nx:
outf.write('[section .text.smolplt]\n')
outf.write('_symbols:\n')
for library, symrels in libraries.items():
for sym, reloc in symrels:
# meh
if reloc != 'R_386_PC32' and reloc != 'R_386_GOT32X':
eprintf('Relocation type ' + reloc + ' of symbol ' + sym + ' unsupported!')
sys.exit(1)
if nx:
outf.write("\t\t_symbols.{lib}.{name}: dd 0x{hash:x}"\
.format(lib=shorts[library],name=sym,hash=hashfn(sym)).lstrip('\n'))
else:
outf.write(("""\
\t\tglobal {name}
\t\t{name}:""" + ("\n\t\t\tdb 0xE9" if use_jmp_bytes else '') + """
\t\t\tdd 0x{hash:x}
""").format(name=sym, hash=hashfn(sym)).lstrip('\n'))
outf.write('db 0\n')
outf.write('_symbols.end:\n')
if nx:
outf.write('_smolplt:\n')
for library, symrels in libraries.items():
for sym, reloc in symrels:
outf.write("""\
[section .text.smolplt.{name}]
global {name}
{name}:
\tjmp [dword _symbols.{lib}.{name}]
""".format(lib=shorts[library],name=sym).lstrip('\n'))
outf.write('_smolplt.end:\n')
outf.write('%include "loader32.asm"\n')
# end output_x86
def output_amd64(libraries, nx, h16, outf, det):
if h16:
eprintf("--hash16 not supported yet for x86_64 outputs.")
exit(1)
if nx: outf.write('%define USE_NX 1\n')
# if h16: outf.write('%define USE_HASH16 1\n')
hashfn = hash_djb2 #hash_bsd2 if h16 else hash_djb2
if det: libraries = sort_imports(libraries, hashfn)
outf.write('; vim: set ft=nasm:\n')
outf.write('bits 64\n')
shorts = { l: l.split('.', 1)[0].lower().replace('-', '_') for l in libraries }
outf.write('%include "header64.asm"\n')
outf.write('dynamic.needed:\n')
for library in libraries:
outf.write(' dq 1;DT_NEEDED\n')
outf.write(' dq (_symbols.{} - _strtab)\n'.format(shorts[library]))
outf.write("""\
dynamic.symtab:
dq DT_SYMTAB ; d_tag
dq 0 ; d_un.d_ptr
dynamic.end:
%ifndef UNSAFE_DYNAMIC
dq DT_NULL
%endif
""")
outf.write('[section .rodata.neededlibs]\n')
outf.write('_strtab:\n')
for library, symrels in libraries.items():
outf.write('\t_symbols.{}: db "{}",0\n'.format(shorts[library], library))
outf.write('[section .data.smolgot]\n')
outf.write('_symbols:\n')
for library, symrels in libraries.items():
for sym, reloc in symrels:
if reloc not in ['R_X86_64_PLT32', 'R_X86_64_GOTPCRELX', \
'R_X86_64_REX_GOTPCRELX', 'R_X86_64_GOTPCREL']:
eprintf('Relocation type ' + reloc + ' of symbol ' + sym + ' unsupported!')
sys.exit(1)
if reloc in ['R_X86_64_GOTPCRELX', 'R_X86_64_REX_GOTPCRELX', \
'R_X86_64_GOTPCREL']:
outf.write("""
global {name}
{name}:
""".format(name=sym).lstrip('\n'))
outf.write('\t\t_symbols.{lib}.{name}: dq 0x{hash:x}\n'\
.format(lib=shorts[library],name=sym,hash=hashfn(sym)))
outf.write('db 0\n')
outf.write('_symbols.end:\n')
outf.write('_smolplt:\n')
for library, symrels in libraries.items():
for sym, reloc in symrels:
if reloc == 'R_X86_64_PLT32':
outf.write("""\
[section .text.smolplt.{name}]
global {name}
{name}:
\tjmp [rel _symbols.{lib}.{name}]
""".format(lib=shorts[library],name=sym).lstrip('\n'))
outf.write('_smolplt.end:\n')
outf.write('%include "loader64.asm"\n')
# end output_amd64
def output(arch, libraries, nx, h16, outf, det):
if arch == 'i386': output_x86(libraries, nx, h16, outf, det)
elif arch == 'x86_64': output_amd64(libraries, nx, h16, outf, det)
else:
eprintf("E: cannot emit for arch '" + str(arch) + "'")
sys.exit(1)