From 7eeaa837d3a6f29f9312bf29962214e709663e52 Mon Sep 17 00:00:00 2001
From: Amand Tihon <amand.tihon@alrj.org>
Date: Fri, 22 May 2009 02:46:53 +0200
Subject: [PATCH] Cleaning and refactoring.

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.
---
 {elf => Bold}/BinArray.py  |   1 -
 {elf => Bold}/__init__.py  |   6 +-
 {elf => Bold}/constants.py |   0
 {elf => Bold}/elf.py       | 174 ++++++++++++----------
 Bold/errors.py             |  49 +++++++
 Bold/linker.py             | 198 +++++++++++++++++++++++++
 bold.py                    | 294 ++++++++++++++-----------------------
 7 files changed, 458 insertions(+), 264 deletions(-)
 rename {elf => Bold}/BinArray.py (92%)
 rename {elf => Bold}/__init__.py (79%)
 rename {elf => Bold}/constants.py (100%)
 rename {elf => Bold}/elf.py (89%)
 create mode 100644 Bold/errors.py
 create mode 100644 Bold/linker.py

diff --git a/elf/BinArray.py b/Bold/BinArray.py
similarity index 92%
rename from elf/BinArray.py
rename to Bold/BinArray.py
index ea88cad..84d5725 100644
--- a/elf/BinArray.py
+++ b/Bold/BinArray.py
@@ -5,7 +5,6 @@
 # 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
diff --git a/elf/__init__.py b/Bold/__init__.py
similarity index 79%
rename from elf/__init__.py
rename to Bold/__init__.py
index c25fb45..f090c5a 100644
--- a/elf/__init__.py
+++ b/Bold/__init__.py
@@ -5,9 +5,13 @@
 # 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
diff --git a/elf/constants.py b/Bold/constants.py
similarity index 100%
rename from elf/constants.py
rename to Bold/constants.py
diff --git a/elf/elf.py b/Bold/elf.py
similarity index 89%
rename from elf/elf.py
rename to Bold/elf.py
index d90e81e..065be52 100644
--- a/elf/elf.py
+++ b/Bold/elf.py
@@ -5,7 +5,6 @@
 # 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
@@ -14,6 +13,7 @@
 
 from BinArray import BinArray
 from constants import *
+from errors import *
 import struct
 
 # Helpful decorator
@@ -44,6 +44,8 @@ class Elf64(object):
     if path:
       self.fromfile(path)
 
+  # Functions for relocatables files used as input
+
   def fromfile(self, path):
     f = file(path, "rb")
 
@@ -52,6 +54,16 @@ class Elf64(object):
     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):
@@ -123,7 +135,6 @@ class Elf64(object):
         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
@@ -163,6 +174,8 @@ class Elf64(object):
         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)
@@ -206,7 +219,7 @@ class Elf64_eident(object):
 
   def __init__(self, rawdata=None):
     object.__init__(self)
-    if rawdata:
+    if rawdata is not None:
       self.fromBinArray(rawdata)
 
   def fromBinArray(self, rawdata):
@@ -262,7 +275,7 @@ class Elf64_Ehdr(object):
     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):
@@ -309,7 +322,7 @@ class Elf64_Shdr(object):
   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):
@@ -346,7 +359,7 @@ class Elf64_Sym(object):
   entsize = struct.calcsize(format)
   def __init__(self, rawdata=None):
     object.__init__(self)
-    if rawdata:
+    if rawdata is not None:
       self.fromBinArray(rawdata)
 
   @nested_property
@@ -388,7 +401,7 @@ class Elf64_Rel(object):
   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):
@@ -441,6 +454,10 @@ class Elf64_Dyn(object):
       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 :
 
@@ -467,15 +484,15 @@ def Section(shdr, data=None):
 
 
 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:
@@ -535,9 +552,44 @@ class SSymtab(BaseSection):
 
 
 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)
@@ -555,8 +607,19 @@ class SStrtab(BaseSection):
       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):
@@ -581,8 +644,8 @@ 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
@@ -637,8 +700,6 @@ class Elf64_Phdr(object):
     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,
@@ -648,17 +709,13 @@ class Elf64_Phdr(object):
   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):
@@ -730,46 +787,11 @@ class DataSegment(BaseSegment):
     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():
@@ -782,13 +804,13 @@ class Dynamic(object):
 
   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 
@@ -796,22 +818,21 @@ class Dynamic(object):
       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
@@ -843,3 +864,4 @@ class Interpreter(object):
 
   def layout(self):
     pass
+
diff --git a/Bold/errors.py b/Bold/errors.py
new file mode 100644
index 0000000..98db3e8
--- /dev/null
+++ b/Bold/errors.py
@@ -0,0 +1,49 @@
+# -*- 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
diff --git a/Bold/linker.py b/Bold/linker.py
new file mode 100644
index 0000000..757bb1d
--- /dev/null
+++ b/Bold/linker.py
@@ -0,0 +1,198 @@
+#! /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)
+
diff --git a/bold.py b/bold.py
index 74a8ba2..02f6626 100755
--- a/bold.py
+++ b/bold.py
@@ -5,195 +5,117 @@
 # 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)
 
-- 
2.39.5