From 7eeaa837d3a6f29f9312bf29962214e709663e52 Mon Sep 17 00:00:00 2001 From: Amand Tihon 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 # # 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 # # 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 # # 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(" +# +# 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 +# +# 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 # # 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 " +__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