2 # -*- coding: utf-8 -*-
3 # kate: space-indent on; indent-width 2; mixedindent off; indent-mode python;
5 # Copyright (C) 2009 Amand 'alrj' Tihon <amand.tihon@alrj.org>
7 # This file is part of bold, the Byte Optimized Linker.
9 # You can redistribute this file and/or modify it under the terms of the
10 # GNU Lesser General Public License as published by the Free Software
11 # Foundation, version 2.1.
14 Main entry point for the bold linker.
17 from constants import *
18 from elf import Elf64, Elf64_Phdr, TextSegment, DataSegment, Dynamic, Interpreter
20 from ctypes.util import find_library
22 class BoldLinker(object):
23 """A Linker object takes one or more objects files, optional shared libs,
24 and arranges all this in an executable.
26 Important note: the external functions from the libraries are NOT resolved.
27 This import is left to the user, as it can be done more efficiently by hash.
28 (http://www.linuxdemos.org/contentarticle/how_to_start_4k_introdev_with_ibh)
29 For this, a very useful symbol is exported, : _dt_debug, the address of the
38 self.entry_point = "_start"
41 def add_object(self, filename):
42 """Add a relocatable file as input."""
48 def add_shlib(self, libname):
49 """Add a shared library to link against."""
50 # Note : we use ctypes' find_library to find the real name
51 fullname = find_library(libname)
53 raise LibNotFound(libname)
54 self.shlibs.append(fullname)
57 """Do the actual linking."""
58 # Prepare two segments. One for .text, the other for .data + .bss
59 self.text_segment = TextSegment()
60 # .data will be mapped 0x100000 bytes further
61 self.data_segment = DataSegment(align=0x100000)
62 self.output.add_segment(self.text_segment)
63 self.output.add_segment(self.data_segment)
65 # Adjust the ELF header
66 self.output.header.e_ident.make_default_amd64()
67 self.output.header.e_phoff = self.output.header.size
68 self.output.header.e_type = ET_EXEC
69 # Elf header lies inside .text
70 self.text_segment.add_content(self.output.header)
72 # Create the four Program Headers. They'll be inside .text
73 # The first Program Header defines .text
74 ph_text = Elf64_Phdr()
75 ph_text.p_type = PT_LOAD
76 ph_text.p_align = 0x100000
77 self.output.add_phdr(ph_text)
78 self.text_segment.add_content(ph_text)
80 # Second one defines .data + .bss
81 ph_data = Elf64_Phdr()
82 ph_data.p_type = PT_LOAD
83 ph_data.p_align = 0x100000
84 self.output.add_phdr(ph_data)
85 self.text_segment.add_content(ph_data)
87 # Third one is only there to define the DYNAMIC section
88 ph_dynamic = Elf64_Phdr()
89 ph_dynamic.p_type = PT_DYNAMIC
90 self.output.add_phdr(ph_dynamic)
91 self.text_segment.add_content(ph_dynamic)
93 # Fourth one is for interp
94 ph_interp = Elf64_Phdr()
95 ph_interp.p_type = PT_INTERP
96 self.output.add_phdr(ph_interp)
97 self.text_segment.add_content(ph_interp)
99 # We have all the needed program headers, update ELF header
100 self.output.header.ph_num = len(self.output.phdrs)
102 # Create the actual content for the interpreter section
103 interp = Interpreter()
104 self.text_segment.add_content(interp)
106 # Then the Dynamic section
108 # for all the requested libs, add a reference in the Dynamic table
109 for lib in self.shlibs:
110 dynamic.add_shlib(lib)
111 # Add an empty symtab, symbol resolution is not done.
112 dynamic.add_symtab(0)
113 # And we need a DT_DEBUG
116 # This belongs to .data
117 self.data_segment.add_content(dynamic)
118 # The dynamic table links to a string table for the libs' names.
119 self.text_segment.add_content(dynamic.strtab)
121 # We can now add the interesting sections to the corresponding segments
124 # Only ALLOC sections are worth it.
125 # This might require change in the future
126 if not (sh.sh_flags & SHF_ALLOC):
129 if (sh.sh_flags & SHF_EXECINSTR):
130 self.text_segment.add_content(sh.content)
131 else: # No exec, it's for .data or .bss
132 if (sh.sh_type == SHT_NOBITS):
133 self.data_segment.add_nobits(sh.content)
135 self.data_segment.add_content(sh.content)
137 # Now, everything is at its place.
138 # Knowing the base address, we can determine where everyone will fall
139 self.output.layout(base_vaddr=0x400000)
141 # Knowing the addresses of all the parts, Program Headers can be filled
142 # This will put the correct p_offset, p_vaddr, p_filesz and p_memsz
143 ph_text.update_from_content(self.text_segment)
144 ph_data.update_from_content(self.data_segment)
145 ph_interp.update_from_content(interp)
146 ph_dynamic.update_from_content(dynamic)
149 # Gather the undefined symbols from all input files
150 undefined_symbols = set()
152 undefined_symbols.update(i.undefined_symbols)
154 # Make a dict with all the symbols declared globally.
155 # Key is the symbol name, value is the final virtual address
159 for s in i.global_symbols:
160 if s in global_symbols:
161 raise RedefinedSymbol(s)
162 # Final address is the section's base address + the symbol's offset
163 addr = i.global_symbols[s][0].content.virt_addr
164 addr += i.global_symbols[s][1]
165 global_symbols[s] = addr
167 # Add a few useful symbols
168 global_symbols["_dt_debug"] = dynamic.dt_debug_address
169 global_symbols["_DYNAMIC"] = dynamic.virt_addr
171 # Find out which symbols aren't really defined anywhere
172 undefined_symbols.difference_update(global_symbols)
174 # For now, it's an error. Later, we could try to find them in the shared
176 if len(undefined_symbols):
177 raise UndefinedSymbol(undefined_symbols.pop())
181 # We can now do the actual relocation
183 i.apply_relocation(global_symbols)
185 # And update the ELF header with the entry point
186 if not self.entry_point in global_symbols:
187 raise UndefinedSymbol(self.entry_point)
188 self.output.header.e_entry = global_symbols[self.entry_point]
193 def toBinArray(self):
194 return self.output.toBinArray()
196 def tofile(self, file_object):
197 return self.output.toBinArray().tofile(file_object)