-#! /usr/bin/python
# -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 2; mixedindent off; indent-mode python;
# 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.
+# GNU General Public License as published by the Free Software Foundation,
+# either version 3 of the License or (at your option) any later version.
"""
Main entry point for the bold linker.
from ctypes.util import find_library
import struct
+
def hash_name(name):
- """Caculate the hash of the function name."""
+ """Caculate the hash of the function name.
+ @param name: the string to hash
+ @return: 32 bits hash value.
+ """
h = 0
for c in name:
h = ((h * 0x21) ^ ord(c)) & 0xffffffff
return h
+
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):
self.output = Elf64()
self.global_symbols = {}
self.undefined_symbols = set()
+ self.common_symbols = set()
+
def add_object(self, filename):
- """Add a relocatable file as input."""
+ """Add a relocatable file as input.
+ @param filename: path to relocatable object file to add
+ """
obj = Elf64(filename)
obj.resolve_names()
obj.find_symbols()
self.objs.append(obj)
+
def build_symbols_tables(self):
"""Find out the globally available symbols, as well as the globally
undefined ones (which should be found in external libraries."""
- # Gather the "extern" symbols from each input files.
+ # Gather the "extern" and common symbols from each input files.
for i in self.objs:
self.undefined_symbols.update(i.undefined_symbols)
+ self.common_symbols.update(i.common_symbols)
# Make a dict with all the symbols declared globally.
# Key is the symbol name, value will later be set to the final
# Find out which symbols aren't really defined anywhere
self.undefined_symbols.difference_update(self.global_symbols)
+ # A symbol declared as COMMON in one object may very well have been
+ # defined in another. In this case, it will be present in the
+ # global_symbols.
+ # Take a copy because we can't change the set's size inside the loop
+ for i in self.common_symbols.copy():
+ if i[0] in self.global_symbols:
+ self.common_symbols.remove(i)
+
- def build_external(self, with_jump=False, align_jump=True):
+ def build_external(self, with_jump=False, align_jump=False):
"""
Generate a fake relocatable object, for dynamic linking.
+ This object is then automatically added in the list of ebjects to link.
+ TODO: This part is extremely non-portable.
"""
# Find out all the undefined symbols. They're the one we'll need to resolve
fo.shdrs.append(data_shdr)
fo.sections['.data'] = data_shdr
+ # .bss will contain pointers to resolved external functions, as well as
+ # the COMMON symbols (from C tentative declaration).
+ bss_size = len(symbols) * 8
+ for s_name, s_size, s_alignment in self.common_symbols:
+ padding = (s_alignment - (bss_size % s_alignment))
+ bss_size += padding + s_size
+
bss_shdr = Elf64_Shdr()
bss_shdr.sh_type = SHT_NOBITS
bss_shdr.sh_flags = (SHF_WRITE | SHF_ALLOC)
- bss_shdr.sh_size = len(symbols) * 8
+ bss_shdr.sh_size = bss_size
bss_shdr.content = BinArray("")
fo.shdrs.append(bss_shdr)
fo.sections['.bss'] = bss_shdr
fo.global_symbols['_bold__functions_hash'] = (data_shdr, 0)
fo.global_symbols['_bold__functions_pointers'] = (bss_shdr, 0)
+ # The COMMON symbols. Assign an offset in .bss, declare as global.
+ bss_common_offset = len(symbols) * 8
+ for s_name, s_size, s_alignment in self.common_symbols:
+ padding = (s_alignment - (bss_common_offset % s_alignment))
+ bss_common_offset += padding
+ fo.global_symbols[s_name] = (bss_shdr, bss_common_offset)
+ bss_common_offset += s_size
+
+
for n, i in enumerate(symbols):
# The hash is always in .data
h = "_bold__hash_%s" % i
class dummy: pass
rela_shdr = Elf64_Shdr()
rela_shdr.sh_type = SHT_RELA
- # rela_shdr.sh_info = fo.shdrs.index(text_shdr)
rela_shdr.target = text_shdr
rela_shdr.sh_flags = 0
rela_shdr._content = dummy() # We only need a container for relatab...
reloc.symbol = dummy()
reloc.symbol.st_shndx = SHN_UNDEF
reloc.symbol.name = "_bold__%s" % i
- # reloc.symbol.st_value = 0
relatab.append(reloc)
fo.shdrs.append(rela_shdr)
fo.sections['.rela.text'] = rela_shdr
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.global_symbols["_dt_debug"] = dynamic.dt_debug_address
self.global_symbols["_DYNAMIC"] = dynamic.virt_addr
- # For now, it's an error. Later, we could try to find them in the shared
- # libraries.
- #if len(self.undefined_symbols):
- # raise UndefinedSymbol(self.undefined_symbols.pop())
-
-
-
# We can now do the actual relocation
for i in self.objs:
i.apply_relocation(self.global_symbols)
def toBinArray(self):
return self.output.toBinArray()
+
def tofile(self, file_object):
return self.output.toBinArray().tofile(file_object)