Moved the link work to a Linker class.
Added exception handling in main.
Make the Dynamic section use SStrtab and Elf64_Dyn types instead of its own types.
Move all errors in a dedicated file.
Check for undefined symbols.
Check that input files are relocatable objects and of a supported type.
Bits of source cleaning.
# Copyright (C) 2009 Amand 'alrj' Tihon <amand.tihon@alrj.org>
#
# This file is part of bold, the Byte Optimized Linker.
-# Heavily inspired by elf.h from the GNU C Library.
#
# You can redistribute this file and/or modify it under the terms of the
# GNU Lesser General Public License as published by the Free Software
# Copyright (C) 2009 Amand 'alrj' Tihon <amand.tihon@alrj.org>
#
# This file is part of bold, the Byte Optimized Linker.
-# Heavily inspired by elf.h from the GNU C Library.
#
# You can redistribute this file and/or modify it under the terms of the
# GNU Lesser General Public License as published by the Free Software
# Foundation, version 2.1.
+"""
+ Empty file in place.
+ Utilities lies in here,
+ Ready to be used.
+"""
\ No newline at end of file
# Copyright (C) 2009 Amand 'alrj' Tihon <amand.tihon@alrj.org>
#
# This file is part of bold, the Byte Optimized Linker.
-# Heavily inspired by elf.h from the GNU C Library.
#
# You can redistribute this file and/or modify it under the terms of the
# GNU Lesser General Public License as published by the Free Software
from BinArray import BinArray
from constants import *
+from errors import *
import struct
# Helpful decorator
if path:
self.fromfile(path)
+ # Functions for relocatables files used as input
+
def fromfile(self, path):
f = file(path, "rb")
data.fromfile(f, Elf64_Ehdr.size)
self.header.fromBinArray(data)
+ # This linker only supports relocatable objects
+ if self.header.e_type != ET_REL:
+ raise NotRelocatableObject(path)
+
+ if self.header.e_ident.ei_class != ELFCLASS64:
+ raise UnsupportedObject(path, "Not %s" % ELFCLASS64)
+
+ if self.header.e_machine != EM_X86_64:
+ raise UnsupportedObject(path, "Not %s" % EM_X86_64)
+
# Load sections headers
f.seek(self.header.e_shoff)
for i in range(self.header.e_shnum):
if reloc.symbol.st_shndx == SHN_UNDEF:
# This is an extern symbol, find it in all_global_symbols
sym_address = all_global_symbols[reloc.symbol.name]
- print "0x%x" % sym_address
else:
# source == in which section it is defined
source = self.shdrs[reloc.symbol.st_shndx].content
target_ba[start:end] = d
+ # Functions for executables files, as output
+
def add_phdr(self, phdr):
self.phdrs.append(phdr)
self.header.e_phnum = len(self.phdrs)
def __init__(self, rawdata=None):
object.__init__(self)
- if rawdata:
+ if rawdata is not None:
self.fromBinArray(rawdata)
def fromBinArray(self, rawdata):
self.e_shentsize = Elf64_Shdr.size
self.e_shnum = 0
self.e_shstrndx = 0
- if rawdata:
+ if rawdata is not None:
self.fromBinArray(rawdata)
def fromBinArray(self, rawdata):
def __init__(self, index=None, rawdata=None):
object.__init__(self)
self.index = index
- if rawdata:
+ if rawdata is not None:
self.fromBinArray(rawdata)
def fromBinArray(self, rawdata):
entsize = struct.calcsize(format)
def __init__(self, rawdata=None):
object.__init__(self)
- if rawdata:
+ if rawdata is not None:
self.fromBinArray(rawdata)
@nested_property
def __init__(self, rawdata=None):
object.__init__(self)
self.r_addend = 0 # No addend in a Rel.
- if rawdata:
+ if rawdata is not None:
self.fromBinArray(rawdata)
def fromBinArray(sef, rawdata):
self.d_val = value
return locals()
+ def toBinArray(self):
+ ba = BinArray()
+ ba.fromstring(struct.pack(self.format, self.d_tag, self.d_val))
+ return ba
# Sections types :
class BaseSection(object):
- def __init__(self, shdr, data=None):
+ def __init__(self, shdr, rawdata=None):
object.__init__(self)
self.data = None
self.header = shdr
- if data:
- self.fromBinArray(data)
+ if rawdata is not None:
+ self.fromBinArray(rawdata)
- def fromBinArray(self, data):
- self.data = data
+ def fromBinArray(self, rawdata):
+ self.data = rawdata
def toBinArray(self):
if self.data:
class SStrtab(BaseSection):
- def __init__(self, shdr, data=None):
+ """This one behaves in two completely different ways.
+ If it's given a section header and data, it will act as read-only, only to
+ be used for name resolution.
+ If it's not given any argument, it can be used to create a new Strtab."""
+ def __init__(self, shdr=None, data=None):
+ self.readonly = (shdr is not None)
self.strtab = {}
+ self.table = []
BaseSection.__init__(self, shdr, data)
+ self.virt_addr = None
+
+ def toBinArray(self):
+ if self.readonly:
+ return BaseSection.toBinArray()
+
+ ba = BinArray()
+ keys = self.strtab.keys()
+ keys.sort()
+ for k in keys:
+ ba.fromstring(self.strtab[k] + "\0")
+ return ba
+
+ @nested_property
+ def size():
+ def fget(self):
+ if self.readonly:
+ return len(data)
+ if len(self.strtab) == 0:
+ return 0
+ return sum((len(x)+1 for x in self.strtab.values()))
+ return locals()
+ physical_size = size
+ logical_size = size
+
+ def iteritems(self):
+ return self.strtab.iteritems()
+
+ # Resolution functions
def fromBinArray(self, data):
BaseSection.fromBinArray(self, data)
self.strtab[key] = v
return v
- def iteritems(self):
- return self.strtab.iteritems()
+ # Executable creation functions
+
+ def append(self, string):
+ if len(self.strtab) == 0:
+ offset = 0
+ else:
+ last = max(self.strtab.keys())
+ offset = last + len(self.strtab[last]) + 1 # for the \0
+ self.strtab[offset] = string
+ return offset
+
+ def layout(self):
+ pass
class SRela(BaseSection):
self.header.target = elf.shdrs[self.header.sh_info]
for r in self.relatab:
r.symbol = self.symtab[r.r_sym]
-
-
+
+
class SHash(BaseSection):
pass
self.p_filesz = 0
self.p_memsz = 0
self.p_align = 1
- #self.content = []
- #self.nobits = []
def toBinArray(self):
res = struct.pack(self.format, self.p_type, self.p_flags, self.p_offset,
def layout(self):
pass
- #def add_content(self, content):
- # self.content.append(content)
-
- #def add_empty_content(self, content):
- # self.nobits.append(content)
-
- #@nested_property
- #def content_size():
- # def fget(self):
- # return sum(s.sh_size for s in self.content)
- # return locals()
+ def update_from_content(self, content):
+ """ Update ofset, address and sizes.
+ After having applied layout(),the content knows all these values."""
+ self.p_offset = content.file_offset
+ self.p_vaddr = content.virt_addr
+ self.p_filesz = content.physical_size
+ self.p_memsz = content.logical_size
class BaseSegment(object):
return locals()
-
-class PStrtab(object):
- def __init__(self):
- object.__init__(self)
- self.table = []
- self.virt_addr = None
-
- def append(self, string):
- if len(self.table):
- offset = self.table[-1][0]
- offset += len(self.table[-1][1])
- else:
- offset = 0
- new_str = string + '\0'
- self.table.append((offset, new_str))
- return offset
-
- @nested_property
- def size():
- def fget(self):
- return (self.table[-1][0] + len(self.table[-1][1]))
- return locals()
- physical_size = size
- logical_size = size
-
- def toBinArray(self):
- ba = BinArray()
- for s in (i[1] for i in self.table):
- ba.fromstring(s)
- return ba
-
- def layout(self):
- pass
-
-
class Dynamic(object):
def __init__(self):
object.__init__(self)
self.dyntab = []
- self.strtab = PStrtab()
+ self.strtab = SStrtab()
@nested_property
def size():
def add_shlib(self, shlib):
offset = self.strtab.append(shlib)
- self.dyntab.append((DT_NEEDED, offset))
+ self.dyntab.append(Elf64_Dyn(DT_NEEDED, offset))
def add_symtab(self, vaddr):
- self.dyntab.append((DT_SYMTAB, vaddr))
+ self.dyntab.append(Elf64_Dyn(DT_SYMTAB, vaddr))
def add_debug(self):
- self.dyntab.append((DT_DEBUG, 0))
+ self.dyntab.append(Elf64_Dyn(DT_DEBUG, 0))
def layout(self):
# Adjust the address of the strtab, if
print "Ooops, strtab's address is not known yet. Aborting."
exit(1)
else:
- self.dyntab.append((DT_STRTAB, self.strtab.virt_addr))
+ self.dyntab.append(Elf64_Dyn(DT_STRTAB, self.strtab.virt_addr))
@nested_property
def dt_debug_address():
def fget(self):
for i, d in enumerate(self.dyntab):
- if d[0] == DT_DEBUG:
- return self.virt_addr + (i*16 + 8)
+ if d.d_tag == DT_DEBUG:
+ return self.virt_addr + (i*d.size + (d.size/2))
return locals()
-
+
def toBinArray(self):
ba = BinArray()
- for i in self.dyntab:
- s = struct.pack("<2Q", i[0], i[1])
- ba.fromstring(s)
+ for d in self.dyntab:
+ ba.extend(d.toBinArray())
null = struct.pack("<Q", DT_NULL)
ba.fromstring(null)
return ba
def layout(self):
pass
+
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# kate: space-indent on; indent-width 2; mixedindent off; indent-mode python;
+
+# Copyright (C) 2009 Amand 'alrj' Tihon <amand.tihon@alrj.org>
+#
+# This file is part of bold, the Byte Optimized Linker.
+#
+# You can redistribute this file and/or modify it under the terms of the
+# GNU Lesser General Public License as published by the Free Software
+# Foundation, version 2.1.
+
+"""Define all the exceptions."""
+
+class NotRelocatableObject(Exception):
+ """Raised when an input file is not a relocatable ELF object."""
+ def __init__(self, path):
+ self.path = path
+ def __str__(self):
+ return "File '%s' is not a relocatable object file" % self.path
+
+class UnsupportedObject(Exception):
+ """Raised when an input file is not of a supported arch."""
+ def __init__(self, path, reason):
+ self.path = path
+ self.reason = reason
+ def __str__(self):
+ return "File '%s' is not supported: %s" % (self.path, self.reason)
+
+class LibNotFound(Exception):
+ """Raised if a shared library could not be found."""
+ def __init__(self, libname):
+ self.libname = libname
+ def __str__(self):
+ return "Cannot find shared library for '%s'" % self.libname
+
+class UndefinedSymbol(Exception):
+ """Raised if a symbol is referenced but not declared."""
+ def __init__(self, symbol_name):
+ self.symbol = symbol_name
+ def __str__(self):
+ return "Undefined reference to '%s'" % self.symbol
+
+class RedefinedSymbol(Exception):
+ """Raised if a symbol is referenced but not declared."""
+ def __init__(self, symbol_name):
+ self.symbol = symbol_name
+ def __str__(self):
+ return "Symbol '%s' is declared twice" % self.symbol
--- /dev/null
+#! /usr/bin/python
+# -*- coding: utf-8 -*-
+# kate: space-indent on; indent-width 2; mixedindent off; indent-mode python;
+
+# Copyright (C) 2009 Amand 'alrj' Tihon <amand.tihon@alrj.org>
+#
+# This file is part of bold, the Byte Optimized Linker.
+#
+# You can redistribute this file and/or modify it under the terms of the
+# GNU Lesser General Public License as published by the Free Software
+# Foundation, version 2.1.
+
+"""
+Main entry point for the bold linker.
+"""
+
+from constants import *
+from elf import Elf64, Elf64_Phdr, TextSegment, DataSegment, Dynamic, Interpreter
+from errors import *
+from ctypes.util import find_library
+
+class BoldLinker(object):
+ """A Linker object takes one or more objects files, optional shared libs,
+ and arranges all this in an executable.
+
+ Important note: the external functions from the libraries are NOT resolved.
+ This import is left to the user, as it can be done more efficiently by hash.
+ (http://www.linuxdemos.org/contentarticle/how_to_start_4k_introdev_with_ibh)
+ For this, a very useful symbol is exported, : _dt_debug, the address of the
+ DT_DEBUG's d_ptr.
+ """
+
+ def __init__(self):
+ object.__init__(self)
+
+ self.objs = []
+ self.shlibs = []
+ self.entry_point = "_start"
+ self.output = Elf64()
+
+ def add_object(self, filename):
+ """Add a relocatable file as input."""
+ obj = Elf64(filename)
+ obj.resolve_names()
+ obj.find_symbols()
+ self.objs.append(obj)
+
+ def add_shlib(self, libname):
+ """Add a shared library to link against."""
+ # Note : we use ctypes' find_library to find the real name
+ fullname = find_library(libname)
+ if not fullname:
+ raise LibNotFound(libname)
+ self.shlibs.append(fullname)
+
+ def link(self):
+ """Do the actual linking."""
+ # Prepare two segments. One for .text, the other for .data + .bss
+ self.text_segment = TextSegment()
+ # .data will be mapped 0x100000 bytes further
+ self.data_segment = DataSegment(align=0x100000)
+ self.output.add_segment(self.text_segment)
+ self.output.add_segment(self.data_segment)
+
+ # Adjust the ELF header
+ self.output.header.e_ident.make_default_amd64()
+ self.output.header.e_phoff = self.output.header.size
+ self.output.header.e_type = ET_EXEC
+ # Elf header lies inside .text
+ self.text_segment.add_content(self.output.header)
+
+ # Create the four Program Headers. They'll be inside .text
+ # The first Program Header defines .text
+ ph_text = Elf64_Phdr()
+ ph_text.p_type = PT_LOAD
+ ph_text.p_align = 0x100000
+ self.output.add_phdr(ph_text)
+ self.text_segment.add_content(ph_text)
+
+ # Second one defines .data + .bss
+ ph_data = Elf64_Phdr()
+ ph_data.p_type = PT_LOAD
+ ph_data.p_align = 0x100000
+ self.output.add_phdr(ph_data)
+ self.text_segment.add_content(ph_data)
+
+ # Third one is only there to define the DYNAMIC section
+ ph_dynamic = Elf64_Phdr()
+ ph_dynamic.p_type = PT_DYNAMIC
+ self.output.add_phdr(ph_dynamic)
+ self.text_segment.add_content(ph_dynamic)
+
+ # Fourth one is for interp
+ ph_interp = Elf64_Phdr()
+ ph_interp.p_type = PT_INTERP
+ self.output.add_phdr(ph_interp)
+ self.text_segment.add_content(ph_interp)
+
+ # We have all the needed program headers, update ELF header
+ self.output.header.ph_num = len(self.output.phdrs)
+
+ # Create the actual content for the interpreter section
+ interp = Interpreter()
+ self.text_segment.add_content(interp)
+
+ # Then the Dynamic section
+ dynamic = Dynamic()
+ # for all the requested libs, add a reference in the Dynamic table
+ for lib in self.shlibs:
+ dynamic.add_shlib(lib)
+ # Add an empty symtab, symbol resolution is not done.
+ dynamic.add_symtab(0)
+ # And we need a DT_DEBUG
+ dynamic.add_debug()
+
+ # This belongs to .data
+ self.data_segment.add_content(dynamic)
+ # The dynamic table links to a string table for the libs' names.
+ self.text_segment.add_content(dynamic.strtab)
+
+ # We can now add the interesting sections to the corresponding segments
+ for i in self.objs:
+ for sh in i.shdrs:
+ # Only ALLOC sections are worth it.
+ # This might require change in the future
+ if not (sh.sh_flags & SHF_ALLOC):
+ continue
+
+ if (sh.sh_flags & SHF_EXECINSTR):
+ self.text_segment.add_content(sh.content)
+ else: # No exec, it's for .data or .bss
+ if (sh.sh_type == SHT_NOBITS):
+ self.data_segment.add_nobits(sh.content)
+ else:
+ self.data_segment.add_content(sh.content)
+
+ # Now, everything is at its place.
+ # Knowing the base address, we can determine where everyone will fall
+ self.output.layout(base_vaddr=0x400000)
+
+ # Knowing the addresses of all the parts, Program Headers can be filled
+ # This will put the correct p_offset, p_vaddr, p_filesz and p_memsz
+ ph_text.update_from_content(self.text_segment)
+ ph_data.update_from_content(self.data_segment)
+ ph_interp.update_from_content(interp)
+ ph_dynamic.update_from_content(dynamic)
+
+
+ # Gather the undefined symbols from all input files
+ undefined_symbols = set()
+ for i in self.objs:
+ undefined_symbols.update(i.undefined_symbols)
+
+ # Make a dict with all the symbols declared globally.
+ # Key is the symbol name, value is the final virtual address
+ global_symbols = {}
+
+ for i in self.objs:
+ for s in i.global_symbols:
+ if s in global_symbols:
+ raise RedefinedSymbol(s)
+ # Final address is the section's base address + the symbol's offset
+ addr = i.global_symbols[s][0].content.virt_addr
+ addr += i.global_symbols[s][1]
+ global_symbols[s] = addr
+
+ # Add a few useful symbols
+ global_symbols["_dt_debug"] = dynamic.dt_debug_address
+ global_symbols["_DYNAMIC"] = dynamic.virt_addr
+
+ # Find out which symbols aren't really defined anywhere
+ undefined_symbols.difference_update(global_symbols)
+
+ # For now, it's an error. Later, we could try to find them in the shared
+ # libraries.
+ if len(undefined_symbols):
+ raise UndefinedSymbol(undefined_symbols.pop())
+
+
+
+ # We can now do the actual relocation
+ for i in self.objs:
+ i.apply_relocation(global_symbols)
+
+ # And update the ELF header with the entry point
+ if not self.entry_point in global_symbols:
+ raise UndefinedSymbol(self.entry_point)
+ self.output.header.e_entry = global_symbols[self.entry_point]
+
+ # DONE !
+
+
+ def toBinArray(self):
+ return self.output.toBinArray()
+
+ def tofile(self, file_object):
+ return self.output.toBinArray().tofile(file_object)
+
# Copyright (C) 2009 Amand 'alrj' Tihon <amand.tihon@alrj.org>
#
# This file is part of bold, the Byte Optimized Linker.
-# Heavily inspired by elf.h from the GNU C Library.
#
# You can redistribute this file and/or modify it under the terms of the
# GNU Lesser General Public License as published by the Free Software
# Foundation, version 2.1.
-from elf.BinArray import BinArray
-from elf.constants import *
-from elf.elf import Elf64, Elf64_Phdr, TextSegment, DataSegment, Dynamic, Interpreter
-import struct, sys
-
-infiles = [Elf64(n) for n in sys.argv[1:]]
-for i in infiles:
- i.resolve_names()
- i.find_symbols()
-
-#h = infile.header
-#print "Class: %s" % h.e_ident.ei_class
-#print "Data: %s" % h.e_ident.ei_data
-#print "Version: %s" % h.e_ident.ei_version
-#print "OS/ABI: %s" % h.e_ident.ei_osabi
-#print "ABI Version: %s" % h.e_ident.ei_abiversion
-#print "Type: %s" % h.e_type
-#print "Machine: %s" % h.e_machine
-#print "Version: %s" % h.e_version
-#print "Entry point address: 0x%x" % h.e_entry
-#print "Start of program headers: %i (bytes into file)" % h.e_phoff
-#print "Start of section headers: %i (bytes into file)" % h.e_shoff
-#print "Flags: 0x%x" % h.e_flags
-#print "Size of this header: %i (bytes)" % h.e_ehsize
-#print "Size of program headers: %i (bytes)" % h.e_phentsize
-#print "Number of program headers: %i" % h.e_phnum
-#print "Size of section headers: %i (bytes)" % h.e_shentsize
-#print "Number of section headers: %i" % h.e_shnum
-
-#print "Section header string table index: %s" % h.e_shstrndx
-
-#print
-
-#print "Section Headers:"
-#for sh in infile.shdrs:
- #print "[%2i] %-16s %-16s %016x %08x" % (sh.index, sh.name, sh.sh_type,
- #sh.sh_addr, sh.sh_offset)
- #print " %016x %016x %-5s %4i %4i %4i" % (sh.sh_size, sh.sh_entsize,
- #sh.sh_flags, sh.sh_link, sh.sh_info, sh.sh_addralign)
-#print
-
-#for sh in infile.shdrs :
- #if sh.sh_type == SHT_STRTAB:
- ##print "Section %i is a string table with entries :" % sh.index
- ##for i, name in sh.content.iteritems():
- ## print "%4i %s" % (i, name)
- #print
- #elif sh.sh_type == SHT_SYMTAB:
- #print "Section %i is a symbol table with entries :" % sh.index
- #print " Num: Value Size Type Bind Vis Ndx Name"
- #for i, sym in enumerate(sh.content.symtab):
- #print "%6i: %016x %5s %-7s %-6s %-7s %4s %s" % (i,
- #sym.st_value, sym.st_size, sym.st_type, sym.st_binding,
- #sym.st_visibility, sym.st_shndx, sym.name)
- #print
- #elif sh.sh_type == SHT_RELA:
- #print "Section %s is a RELA that applies to %s:" % (sh.name, sh.target.name)
- #print " Offset Info Type Sym. Value Sym. Name + Addend"
- #for i in sh.content.relatab:
- #print "%012x %012x %-16s %016x %s%s + %x" % (i.r_offset, i.r_info,
- #i.r_type, i.symbol.st_value, i.symbol.name,
- #sh.owner.shdrs[i.symbol.st_shndx].name,
- #i.r_addend)
- #print
-
-
-
-outfile = Elf64()
-
-text_segment = TextSegment()
-data_segment = DataSegment(align=0x100000)
-
-outfile.add_segment(text_segment)
-outfile.add_segment(data_segment)
-
-
-outfile.header.e_ident.make_default_amd64()
-outfile.header.e_phoff = outfile.header.size
-outfile.header.e_type = ET_EXEC
-text_segment.add_content(outfile.header)
-
-ph_text = Elf64_Phdr()
-ph_text.p_type = PT_LOAD
-ph_text.p_align = 0x100000
-outfile.add_phdr(ph_text)
-text_segment.add_content(ph_text)
-
-ph_data = Elf64_Phdr()
-ph_data.p_type = PT_LOAD
-ph_data.p_align = 0x100000
-outfile.add_phdr(ph_data)
-text_segment.add_content(ph_data)
-
-ph_dynamic = Elf64_Phdr()
-ph_dynamic.p_type = PT_DYNAMIC
-outfile.add_phdr(ph_dynamic)
-text_segment.add_content(ph_dynamic)
-
-ph_interp = Elf64_Phdr()
-ph_interp.p_type = PT_INTERP
-outfile.add_phdr(ph_interp)
-text_segment.add_content(ph_interp)
-
-interp = Interpreter()
-text_segment.add_content(interp)
-
-dynamic = Dynamic()
-dynamic.add_shlib("libGL.so.1")
-dynamic.add_shlib("libSDL-1.2.so.0")
-dynamic.add_symtab(0)
-dynamic.add_debug()
-data_segment.add_content(dynamic)
-text_segment.add_content(dynamic.strtab)
-
-
-# Find interresting sections in input file
-for i in infiles:
- for sh in i.shdrs:
- if (sh.sh_flags & SHF_ALLOC):
- if (sh.sh_flags & SHF_EXECINSTR):
- text_segment.add_content(sh.content)
- else: # No exec, it's for .data
- if (sh.sh_type == SHT_NOBITS):
- data_segment.add_nobits(sh.content)
- else:
- data_segment.add_content(sh.content)
-
-
-outfile.layout(base_vaddr=0x400000)
-
-
-# Set addresses, sizes, etc. where known
-outfile.header.e_phnum = len(outfile.phdrs)
-outfile.header.e_phoff = outfile.phdrs[0].file_offset
-
-ph_text.p_offset = text_segment.file_offset
-ph_text.p_vaddr = text_segment.virt_addr
-ph_text.p_filesz = text_segment.physical_size
-ph_text.p_memsz = text_segment.logical_size
-
-ph_data.p_offset = data_segment.file_offset
-ph_data.p_vaddr = data_segment.virt_addr
-ph_data.p_filesz = data_segment.physical_size
-ph_data.p_memsz = data_segment.logical_size
-
-ph_interp.p_offset = interp.file_offset
-ph_interp.p_vaddr = interp.virt_addr
-ph_interp.p_filesz = interp.physical_size
-ph_interp.p_memsz = interp.logical_size
-
-ph_dynamic.p_offset = dynamic.file_offset
-ph_dynamic.p_vaddr = dynamic.virt_addr
-ph_dynamic.p_filesz = dynamic.physical_size
-ph_dynamic.p_memsz = dynamic.logical_size
-
-for i in infiles:
- outfile.undefined_symbols.extend(i.undefined_symbols)
-
-dt_dbg = dynamic.dt_debug_address
-outfile.global_symbols["_dt_debug"] = dt_dbg
-outfile.global_symbols["_DYNAMIC"] = dynamic.virt_addr
-
-# Take all globally declared symbols, and put them in outfile's dict
-for i in infiles:
- for s in i.global_symbols:
- section_addr = i.global_symbols[s][0].content.virt_addr
- addr = section_addr + i.global_symbols[s][1]
- if s in outfile.global_symbols:
- print "Symbol '%s' defined more than once."
- exit(1)
- outfile.global_symbols[s] = addr
-
-for i in infiles:
- i.apply_relocation(outfile.global_symbols)
-
-_start = outfile.global_symbols["_start"]
-outfile.header.e_entry = _start
-
-# outfile.apply_global_relocation()
-
-f = open("prout", "wb")
-outfile.toBinArray().tofile(f)
-f.close()
-
-
+#from bold.constants import *
+#from bold.elf import Elf64, Elf64_Phdr, TextSegment, DataSegment, Dynamic, Interpreter
+
+__author__ = "Amand Tihon <amand.tihon@alrj.org>"
+__version__ = "0.0.1"
+
+
+from Bold.linker import BoldLinker
+from Bold.errors import *
+from optparse import OptionParser
+import os, sys
+
+class BoldOptionParser(OptionParser):
+ """Bold option parser."""
+ global __version__
+ _usage_message = "%prog [options] file..."
+ _version_message = "%%prog version %s" % __version__
+ _description_message = """A limited ELF linker for x86_64. It is
+intended to create very small executables with the least possible overhead."""
+
+ def __init__(self):
+ OptionParser.__init__(self, usage=self._usage_message,
+ version=self._version_message, description=self._description_message,
+ add_help_option=True, prog="bold")
+
+ self.set_defaults(entry="_start", outfile="a.out")
+
+ self.add_option("-e", "--entry", action="store", dest="entry",
+ metavar="SYMBOL", help="Set the entry point (default: _start)")
+ self.add_option("-l", "--library", action="append", dest="shlibs",
+ metavar="LIBNAME", help="Search for library LIBNAME")
+ self.add_option("-o", "--output", action="store", dest="outfile",
+ metavar="FILE", help="Set output file name (default: a.out)")
+
+
+def main():
+ parser = BoldOptionParser()
+ options, args = parser.parse_args()
+
+ linker = BoldLinker()
+
+ if options.shlibs:
+ for shlib in options.shlibs:
+ try:
+ linker.add_shlib(shlib)
+ except LibNotFound, e:
+ print >>sys.stderr, e
+ return 1
+
+ if not args:
+ print >>sys.stderr, "No input files"
+ return 1
+
+ for infile in args:
+ try:
+ linker.add_object(infile)
+ except UnsupportedObject, e:
+ print >>sys.stderr, e
+ return 1
+ except IOError, e:
+ print >>sys.stderr, e
+ return 1
+
+ linker.entry_point = options.entry
+
+ try:
+ linker.link()
+ except UndefinedSymbol, e:
+ print >>sys.stderr, e
+ return 1
+ except RedefinedSymbol, e:
+ print >>sys.stderr, e
+ return 1
+
+ # Remove the file if it was present
+ try:
+ os.unlink(options.outfile)
+ except os.error, e:
+ if e.errno == 2: # No such file
+ pass
+
+ try:
+ o = open(options.outfile, "wb")
+ except IOError, e:
+ print >>sys.stderr, e
+ return 1
+
+ linker.tofile(o)
+ o.close()
+
+ try:
+ os.chmod(options.outfile, 0755)
+ except IOError, e:
+ print >>sys.stderr, e
+ return 1
+
+ return 0
+
+
+if __name__ == "__main__":
+ try:
+ rcode = main()
+ except Exception, e:
+ raise
+ print >>sys.stderr, "Unhandled error:", e
+ rcode = 1
+
+ exit(rcode)