smol/smold

103 lines
4.0 KiB
Python
Executable File

#!/usr/bin/env python3
import os
from os import path
import sys
import argparse
import shutil
import tempfile
import subprocess
from smol.util import error
from smol.elf import ELFMachine, ELF_DEFAULT_BITS
from smol.parse import decide_arch, find_lib, find_symbol, get_cc_paths, get_needed_syms
from smol.emit import output_table
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-m', '--target',
help='architecture to generate asm code for (default: auto)')
parser.add_argument('-l', '--library', metavar='LIB', action='append',
help='libraries to link against')
parser.add_argument('-L', '--libdir', metavar='DIR', action='append',
help='directories to search libraries in')
parser.add_argument('--smol-opt', metavar='OPT', default=['use_interp'], action='append',
help='optimization flags for smol')
parser.add_argument('--smol-loader-dir', metavar='DIR',
default=path.join(path.dirname(__file__), 'ldr'), help='path to smol loader files')
parser.add_argument('--nasm', default=os.getenv('NASM') or shutil.which('nasm'),
help='which nasm binary to use')
parser.add_argument('--ld', default=os.getenv('LD') or shutil.which('ld'),
help='which ld binary to use')
parser.add_argument('--cc', default=os.getenv('CC') or shutil.which('cc'),
help='which cc binary to use')
parser.add_argument('--scanelf', default=os.getenv('SCANELF') or shutil.which('scanelf'),
help='which scanelf binary to use')
parser.add_argument('--readelf', default=os.getenv('READELF') or shutil.which('readelf'),
help='which readelf binary to use')
parser.add_argument('input', nargs='+', help='input object file(s)')
parser.add_argument('-o', '--output', metavar='OUT', default='smol.out', help='output binary')
args, ld_args = parser.parse_known_args()
for util in ['nasm', 'cc', 'scanelf', 'readelf', 'ld']:
if not getattr(args, util):
parser.error('utility "{u}" could not be found! either install it or pass the path with --{u}'
.format(u=util))
opts = set()
for opt in args.smol_opt[:]:
if opt.startswith('-'):
opts.discard(opt[1:])
continue
opts.add(opt)
args.library = args.library or []
args.libdir = args.libdir or []
if args.target:
arch = args.target.tolower().replace('elf_', '')
if arch not in ELFMachine:
parser.error('unknown architecture: {}'.format(arch))
arch = ELFMachine(arch)
bits = ELF_DEFAULT_BITS[arch]
else:
arch, bits = decide_arch(args.input)
if not arch or not bits:
error('Invalid architecture!')
syms = get_needed_syms(args.readelf, args.input)
paths = get_cc_paths(args.cc)
libdirs = args.libdir + paths['libraries']
libs = []
libnames = args.library
for libname in libnames:
lib = find_lib(libdirs, libname)
if not lib:
error('could not find library: {}'.format(libname))
libs.append(lib)
symbols = {}
for symbol, reloc in syms:
library = find_symbol(args.scanelf, libs, libnames, symbol)
if not library:
error("could not find symbol: {}".format(symbol))
symbols.setdefault(library, [])
symbols[library].append((symbol, reloc))
as_args = ['-D' + opt.upper() for opt in opts]
with tempfile.NamedTemporaryFile('w', suffix='.s') as table, tempfile.NamedTemporaryFile('w', suffix='.o') as tableobj:
output_table(arch, symbols, table)
table.flush()
try:
subprocess.check_call([args.nasm] + as_args + ['-I', args.smol_loader_dir + '/', '-f', 'elf{}'.format(bits), table.name, '-o', tableobj.name])
subprocess.check_call([args.ld, '-T', os.path.join(args.smol_loader_dir, 'link.ld'), '--oformat=binary', '-o', args.output, tableobj.name] + ld_args + args.input)
except subprocess.CalledProcessError:
sys.exit(1)
if __name__ == '__main__':
main()