smolemit.py 5.82 KB
Newer Older
PoroCYon's avatar
PoroCYon committed
1
2

import sys
3
from collections import OrderedDict
PoroCYon's avatar
PoroCYon committed
4
5
6

from smolshared import *

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def sort_imports(libraries, hashfn):
    #eprintf("in: " + str(libraries))

    # sort libs by name length
    ll = sorted(libraries.items(), key=lambda ls: len(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):
PoroCYon's avatar
PoroCYon committed
24
    outf.write('; vim: set ft=nasm:\n') # be friendly
25

26
27
    if nx:  outf.write('%define USE_NX 1\n')
    if h16: outf.write('%define USE_HASH16 1\n')
28

29
30
31
    hashfn = hash_bsd2 if h16 else hash_djb2
    if det: libraries = sort_imports(libraries, hashfn)

32
33
34
35
36
37
38
39
40
41
42
43
44
45
    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')

PoroCYon's avatar
PoroCYon committed
46
    outf.write('bits 32\n')
47

PoroCYon's avatar
PoroCYon committed
48
49
    shorts = { l: l.split('.', 1)[0].lower().replace('-', '_') for l in libraries }

PoroCYon's avatar
PoroCYon committed
50
    outf.write('%include "header32.asm"\n')
51
    outf.write('dynamic.needed:\n')
PoroCYon's avatar
PoroCYon committed
52
    for library in libraries:
PoroCYon's avatar
PoroCYon committed
53
        outf.write('dd 1;DT_NEEDED\n')
54
        outf.write('dd (_symbols.{} - _strtab)\n'.format(shorts[library]))
55
56
57
58
59
60
    outf.write("""\
dynamic.end:
%ifndef UNSAFE_DYNAMIC
    dd DT_NULL
%endif
""")
PoroCYon's avatar
PoroCYon committed
61

62
    outf.write('[section .rodata.neededlibs]\n')
63
    outf.write('_strtab:\n')
64
    for library, symrels in libraries.items():
65
        outf.write('\t_symbols.{}: db "{}",0\n'.format(shorts[library], library))
PoroCYon's avatar
PoroCYon committed
66

67
68
69
70
71
72
    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():
73
74
        for sym, reloc in symrels:
            # meh
75
            if reloc != 'R_386_PC32' and reloc != 'R_386_GOT32X':
76
77
78
                eprintf('Relocation type ' + reloc + ' of symbol ' + sym + ' unsupported!')
                sys.exit(1)

79
80
            if nx:
                outf.write("\t\t_symbols.{lib}.{name}: dd 0x{hash:x}"\
81
                    .format(lib=shorts[library],name=sym,hash=hashfn(sym)).lstrip('\n'))
82
83
            else:
                outf.write(("""\
PoroCYon's avatar
PoroCYon committed
84
\t\tglobal {name}
85
86
\t\t{name}:""" + ("\n\t\t\tdb 0xE9" if use_jmp_bytes else '') + """
\t\t\tdd 0x{hash:x}
87
""").format(name=sym, hash=hashfn(sym)).lstrip('\n'))
PoroCYon's avatar
PoroCYon committed
88
89

    outf.write('db 0\n')
PoroCYon's avatar
PoroCYon committed
90
91
    outf.write('_symbols.end:\n')

92
93
94
95
96
97
98
99
100
101
102
103
104
    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')

PoroCYon's avatar
PoroCYon committed
105
    outf.write('%include "loader32.asm"\n')
106
107
# end output_x86

PoroCYon's avatar
PoroCYon committed
108

109
def output_amd64(libraries, nx, h16, outf, det):
110
111
112
113
114
115
116
    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')

117
118
119
    hashfn = hash_djb2 #hash_bsd2 if h16 else hash_djb2
    if det: libraries = sort_imports(libraries, hashfn)

PoroCYon's avatar
PoroCYon committed
120
121
    outf.write('; vim: set ft=nasm:\n')
    outf.write('bits 64\n')
122

PoroCYon's avatar
PoroCYon committed
123
124
125
126
127
    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:
128
129
130
131
132
133
134
135
136
137
138
        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
""")
PoroCYon's avatar
PoroCYon committed
139

140
    outf.write('[section .rodata.neededlibs]\n')
141
142

    outf.write('_strtab:\n')
143
144
    for library, symrels in libraries.items():
        outf.write('\t_symbols.{}: db "{}",0\n'.format(shorts[library], library))
145

146
    outf.write('[section .data.smolgot]\n')
147

PoroCYon's avatar
PoroCYon committed
148
    outf.write('_symbols:\n')
149
150
    for library, symrels in libraries.items():
        for sym, reloc in symrels:
151
152
            if reloc not in ['R_X86_64_PLT32', 'R_X86_64_GOTPCRELX', \
                             'R_X86_64_REX_GOTPCRELX', 'R_X86_64_GOTPCREL']:
153
154
155
                eprintf('Relocation type ' + reloc + ' of symbol ' + sym + ' unsupported!')
                sys.exit(1)

156
157
            if reloc in ['R_X86_64_GOTPCRELX', 'R_X86_64_REX_GOTPCRELX', \
                         'R_X86_64_GOTPCREL']:
158
159
160
161
162
                outf.write("""
global {name}
{name}:
""".format(name=sym).lstrip('\n'))

PoroCYon's avatar
PoroCYon committed
163
            outf.write('\t\t_symbols.{lib}.{name}: dq 0x{hash:x}\n'\
164
                       .format(lib=shorts[library],name=sym,hash=hashfn(sym)))
PoroCYon's avatar
PoroCYon committed
165
166
167
168
169

    outf.write('db 0\n')
    outf.write('_symbols.end:\n')

    outf.write('_smolplt:\n')
170
171
172
    for library, symrels in libraries.items():
        for sym, reloc in symrels:
            if reloc == 'R_X86_64_PLT32':
173
                outf.write("""\
PoroCYon's avatar
PoroCYon committed
174
175
176
[section .text.smolplt.{name}]
global {name}
{name}:
177
\tjmp [rel _symbols.{lib}.{name}]
PoroCYon's avatar
PoroCYon committed
178
""".format(lib=shorts[library],name=sym).lstrip('\n'))
PoroCYon's avatar
PoroCYon committed
179

PoroCYon's avatar
PoroCYon committed
180
181
    outf.write('_smolplt.end:\n')
    outf.write('%include "loader64.asm"\n')
182
183
184
# end output_amd64


185
186
187
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)
PoroCYon's avatar
PoroCYon committed
188
189
190
191
    else:
        eprintf("E: cannot emit for arch '" + str(arch) + "'")
        sys.exit(1)