]> git.alrj.org Git - bold.git/blob - Bold/linker.py
757bb1d12fcecb463f8aa28889e7ee97cbd22622
[bold.git] / Bold / linker.py
1 #! /usr/bin/python
2 # -*- coding: utf-8 -*-
3 # kate: space-indent on; indent-width 2; mixedindent off; indent-mode python;
4
5 # Copyright (C) 2009 Amand 'alrj' Tihon <amand.tihon@alrj.org>
6 #
7 # This file is part of bold, the Byte Optimized Linker.
8 #
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.
12
13 """
14 Main entry point for the bold linker.
15 """
16
17 from constants import *
18 from elf import Elf64, Elf64_Phdr, TextSegment, DataSegment, Dynamic, Interpreter
19 from errors import *
20 from ctypes.util import find_library
21
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.
25
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
30   DT_DEBUG's d_ptr.
31   """
32
33   def __init__(self):
34     object.__init__(self)
35
36     self.objs = []
37     self.shlibs = []
38     self.entry_point = "_start"
39     self.output = Elf64()
40
41   def add_object(self, filename):
42     """Add a relocatable file as input."""
43     obj = Elf64(filename)
44     obj.resolve_names()
45     obj.find_symbols()
46     self.objs.append(obj)
47
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)
52     if not fullname:
53       raise LibNotFound(libname)
54     self.shlibs.append(fullname)
55
56   def link(self):
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)
64
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)
71
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)
79
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)
86
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)
92
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)
98
99     # We have all the needed program headers, update ELF header
100     self.output.header.ph_num = len(self.output.phdrs)
101
102     # Create the actual content for the interpreter section
103     interp = Interpreter()
104     self.text_segment.add_content(interp)
105
106     # Then the Dynamic section
107     dynamic = Dynamic()
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
114     dynamic.add_debug()
115
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)
120
121     # We can now add the interesting sections to the corresponding segments
122     for i in self.objs:
123       for sh in i.shdrs:
124         # Only ALLOC sections are worth it.
125         # This might require change in the future
126         if not (sh.sh_flags & SHF_ALLOC):
127           continue
128
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)
134           else:
135             self.data_segment.add_content(sh.content)
136
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)
140
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)
147
148
149     # Gather the undefined symbols from all input files
150     undefined_symbols = set()
151     for i in self.objs:
152       undefined_symbols.update(i.undefined_symbols)
153
154     # Make a dict with all the symbols declared globally.
155     # Key is the symbol name, value is the final virtual address
156     global_symbols = {}
157
158     for i in self.objs:
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
166
167     # Add a few useful symbols
168     global_symbols["_dt_debug"] = dynamic.dt_debug_address
169     global_symbols["_DYNAMIC"] = dynamic.virt_addr
170
171     # Find out which symbols aren't really defined anywhere
172     undefined_symbols.difference_update(global_symbols)
173
174     # For now, it's an error. Later, we could try to find them in the shared
175     # libraries.
176     if len(undefined_symbols):
177       raise UndefinedSymbol(undefined_symbols.pop())
178
179
180
181     # We can now do the actual relocation
182     for i in self.objs:
183       i.apply_relocation(global_symbols)
184
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]
189
190     # DONE !
191
192
193   def toBinArray(self):
194     return self.output.toBinArray()
195
196   def tofile(self, file_object):
197     return self.output.toBinArray().tofile(file_object)
198