1 # -*- 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.
8 # Heavily inspired by elf.h from the GNU C Library.
10 # You can redistribute this file and/or modify it under the terms of the
11 # GNU Lesser General Public License as published by the Free Software
12 # Foundation, version 2.1.
15 from BinArray import BinArray
16 from constants import *
20 def nested_property(c):
21 return property(**c())
23 #--------------------------------------------------------------------------
25 #--------------------------------------------------------------------------
28 """Handles an Elf64 object."""
29 interpreter = "/lib64/ld-linux-x86-64.so.2"
31 def __init__(self, path=None):
33 self.header = Elf64_Ehdr()
34 self.header.owner = self
40 self.local_symbols = {}
41 self.global_symbols = {}
42 self.undefined_symbols = []
47 def fromfile(self, path):
52 data.fromfile(f, Elf64_Ehdr.size)
53 self.header.fromBinArray(data)
55 # Load sections headers
56 f.seek(self.header.e_shoff)
57 for i in range(self.header.e_shnum):
59 data.fromfile(f, self.header.e_shentsize)
60 h = Elf64_Shdr(i, data)
64 # Read sections content
67 if sh.sh_type != SHT_NOBITS:
69 data.fromfile(f, sh.sh_size)
74 def resolve_names(self):
75 # The .shstrtab index is in Elf Header. find the sections names
76 strtab = self.shdrs[self.header.e_shstrndx].content
79 sh.name = strtab[int(sh.sh_name)]
80 self.sections[sh.name] = sh
82 # And resolve names in the section itself
86 def find_symbols(self):
88 if sh.sh_type == SHT_SYMTAB:
89 symtab = sh.content.symtab
92 if symbol.st_type == STT_FILE:
94 if symbol.st_shndx == SHN_ABS:
96 if symbol.st_shndx == SHN_UNDEF:
98 self.undefined_symbols.append(symbol.name)
101 target_section = self.shdrs[symbol.st_shndx]
103 symbol_name = symbol.name
104 value = symbol.st_value
105 bind = symbol.st_binding
107 # We got a name, a target section, and an offset in the section
108 if symbol.st_binding == STB_LOCAL:
109 if symbol.st_type == STT_SECTION:
110 symbol_name = target_section.name
111 self.local_symbols[symbol_name] = (target_section, value)
113 self.global_symbols[symbol_name] = (target_section, value)
115 def apply_relocation(self, all_global_symbols):
116 # find relocation tables
117 relocations = [sh for sh in self.shdrs if sh.sh_type in [SHT_REL, SHT_RELA]]
118 for sh in relocations:
119 target = sh.target.content
121 for reloc in sh.content.relatab:
123 if reloc.symbol.st_shndx == SHN_UNDEF:
124 # This is an extern symbol, find it in all_global_symbols
125 sym_address = all_global_symbols[reloc.symbol.name]
126 print "0x%x" % sym_address
128 # source == in which section it is defined
129 source = self.shdrs[reloc.symbol.st_shndx].content
130 sym_address = source.virt_addr + reloc.symbol.st_value
132 target_ba = target.data # The actual BinArray that we'll modify
133 pc_address = target.virt_addr + reloc.r_offset
135 if reloc.r_type == R_X86_64_64:
136 format = "<Q" # Direct 64 bit address
137 target_value = sym_address + reloc.r_addend
138 elif reloc.r_type == R_X86_64_PC32:
139 format = "<i" # PC relative 32 bit signed
140 target_value = sym_address + reloc.r_addend - pc_address
141 elif reloc.r_type == R_X86_64_32:
142 format = "<I" # Direct 32 bit zero extended
143 target_value = sym_address + reloc.r_addend
144 elif reloc.r_type == R_X86_64_PC16:
145 format = "<h" # 16 bit sign extended pc relative
146 target_value = sym_address + reloc.r_addend - pc_address
147 elif reloc.r_type == R_X86_64_16:
148 format = "<H" # Direct 16 bit zero extended
149 target_value = sym_address + reloc.r_addend
150 elif reloc.r_type == R_X86_64_PC8:
151 format = "b" # 8 bit sign extended pc relative
152 target_value = sym_address + reloc.r_addend - pc_address
153 elif reloc.r_type == R_X86_64_8:
154 format = "b" # Direct 8 bit sign extended
155 target_value = sym_address + reloc.r_addend
157 print "Unsupported relocation type: %s" % reloc.r_type
160 d = BinArray(struct.pack(format, target_value))
161 start = reloc.r_offset
163 target_ba[start:end] = d
166 def add_phdr(self, phdr):
167 self.phdrs.append(phdr)
168 self.header.e_phnum = len(self.phdrs)
171 def add_segment(self, segment):
172 self.segments.append(segment)
174 def layout(self, base_vaddr):
175 """Do the actual layout for final executable."""
177 virt_addr = base_vaddr
179 self.virt_addr = base_vaddr
180 self.file_offset = file_offset
181 for s in self.segments:
183 s.virt_addr = virt_addr
184 s.file_offset = file_offset
186 virt_addr += s.logical_size
187 file_offset += s.physical_size
189 def toBinArray(self):
191 for s in self.segments:
192 ba.extend(s.toBinArray())
196 #--------------------------------------------------------------------------
198 #--------------------------------------------------------------------------
200 class Elf64_eident(object):
201 """Detailed representation for the Elf identifier."""
203 size = struct.calcsize(format)
207 def __init__(self, rawdata=None):
208 object.__init__(self)
210 self.fromBinArray(rawdata)
212 def fromBinArray(self, rawdata):
213 t = struct.unpack(self.format, rawdata)
214 self.ei_magic = rawdata[:4]
215 self.ei_class = ElfClass(rawdata[4])
216 self.ei_data = ElfData(rawdata[5])
217 self.ei_version = ElfVersion(rawdata[6])
218 self.ei_osabi = ElfOsAbi(rawdata[7])
219 self.ei_abiversion = 0
220 self.ei_pad = [0, 0, 0, 0, 0, 0, 0]
222 def make_default_amd64(self):
223 self.ei_magic = BinArray([0x7f, 0x45, 0x4c, 0x46])
224 self.ei_class = ELFCLASS64
225 self.ei_data = ELFDATA2LSB
226 self.ei_version = EV_CURRENT
227 self.ei_osabi = ELFOSABI_SYSV
228 self.ei_abiversion = 0
229 self.ei_pad = [0, 0, 0, 0, 0, 0, 0]
231 def toBinArray(self):
232 ba = BinArray(self.ei_magic)
233 ba.append(self.ei_class)
234 ba.append(self.ei_data)
235 ba.append(self.ei_version)
236 ba.append(self.ei_osabi)
237 ba.append(self.ei_abiversion)
238 ba.extend(self.ei_pad)
242 class Elf64_Ehdr(object):
243 """Elf file header"""
244 format = "<16B 2H I 3Q I 6H"
245 size = struct.calcsize(format)
249 def __init__(self, rawdata=None):
250 object.__init__(self)
251 self.e_ident = Elf64_eident()
252 self.e_type = ET_NONE
253 self.e_machine = EM_X86_64
254 self.e_version = EV_CURRENT
259 self.e_ehsize = self.size
260 self.e_phentsize = Elf64_Phdr.size
262 self.e_shentsize = Elf64_Shdr.size
266 self.fromBinArray(rawdata)
268 def fromBinArray(self, rawdata):
269 t = struct.unpack(self.format, rawdata)
270 self.e_ident = Elf64_eident(BinArray(rawdata[:16]))
271 self.e_type = ElfType(t[16])
272 self.e_machine = ElfMachine(t[17])
273 self.e_version = ElfVersion(t[18])
278 self.e_ehsize = t[23]
279 self.e_phentsize = t[24]
281 self.e_shentsize = t[26]
283 self.e_shstrndx = t[28]
285 def toBinArray(self):
286 # Build a list from e_ident and all other fields, to feed struct.pack.
287 values = self.e_ident.toBinArray().tolist()
288 values.extend([self.e_type, self.e_machine, self.e_version, self.e_entry,
289 self.e_phoff, self.e_shoff, self.e_flags, self.e_ehsize, self.e_phentsize,
290 self.e_phnum, self.e_shentsize, self.e_shnum, self.e_shstrndx])
291 res = struct.pack(self.format, *values)
298 #--------------------------------------------------------------------------
300 #--------------------------------------------------------------------------
302 class Elf64_Shdr(object):
303 """Elf64 section header."""
304 format = "<2I 4Q 2I 2Q"
305 size = struct.calcsize(format)
309 def __init__(self, index=None, rawdata=None):
310 object.__init__(self)
313 self.fromBinArray(rawdata)
315 def fromBinArray(self, rawdata):
316 t = struct.unpack(self.format, rawdata)
318 self.sh_type = ElfShType(t[1])
321 self.sh_offset = t[4]
325 self.sh_addralign = t[8]
326 self.sh_entsize = t[9]
328 def resolve_names(self):
329 self.content.resolve_names(self.owner)
335 def fset(self, data):
336 """Use the Section factory to get the subclass corresponding to the
337 session type specified in this header)."""
338 self._content = Section(self, data)
341 # For sections that contain elements of specific types :
343 class Elf64_Sym(object):
344 """Symbol Table entry"""
345 format = "<I 2B H 2Q "
346 entsize = struct.calcsize(format)
347 def __init__(self, rawdata=None):
348 object.__init__(self)
350 self.fromBinArray(rawdata)
355 return ElfSymbolBinding((self.st_info >> 4) & 0x0f)
356 def fset(self, value):
357 self.st_info = (((value & 0x0f) << 4) | (self.st_info & 0x0f))
363 return ElfSymbolType(self.st_info & 0x0f)
364 def fset(self, value):
365 self.st_info = ((self.st_info & 0xf0) | (value & 0x0f))
371 return ElfSymbolVisibility(self.st_other & 0x03)
372 def fset(self, value):
373 self.st_other = ((self.st_other & 0xfc) | (value & 0x03))
376 def fromBinArray(self, rawdata):
377 t = struct.unpack(self.format, rawdata)
378 self.st_name = t[0] # index in the strtab pointed by sh_link
381 self.st_shndx = ElfSectionIndex(t[3])
386 class Elf64_Rel(object):
388 def __init__(self, rawdata=None):
389 object.__init__(self)
390 self.r_addend = 0 # No addend in a Rel.
392 self.fromBinArray(rawdata)
394 def fromBinArray(sef, rawdata):
395 t = struct.unpack(self.format, rawdata)
402 return (self.r_info >> 32) & 0xffffffff
403 def fset(self, value):
404 self.r_info = ((value & 0xffffffff) << 32) | (self.r_info & 0xffffffff)
410 return Amd64Relocation(self.r_info & 0xffffffff)
411 def fset(self, value):
412 self.r_info = (self.r_info & 0xffffffff00000000) | (value & 0xffffffff)
416 class Elf64_Rela(Elf64_Rel):
418 def __init__(self, rawdata=None):
419 Elf64_Rel.__init__(self, rawdata)
421 def fromBinArray(self, rawdata):
422 t = struct.unpack(self.format, rawdata)
428 class Elf64_Dyn(object):
430 size = struct.calcsize(format)
431 def __init__(self, tag, value):
432 object.__init__(self)
440 def fset(self, value):
447 def Section(shdr, data=None):
448 """A section factory"""
451 SHT_PROGBITS: SProgBits,
456 SHT_DYNAMIC: SDynamic,
463 if shdr.sh_type in dataclass:
464 return dataclass[shdr.sh_type](shdr, data)
466 return BaseSection(shdr, data)
469 class BaseSection(object):
470 def __init__(self, shdr, data=None):
471 object.__init__(self)
475 self.fromBinArray(data)
477 def fromBinArray(self, data):
480 def toBinArray(self):
486 def resolve_names(self, elf):
487 """Nothing to resolve."""
493 return len(self.data)
502 class SNull(BaseSection):
503 def __init__(self, shdr, data=None):
504 BaseSection.__init__(self, shdr, None)
507 class SProgBits(BaseSection):
508 def __init__(self, shdr, data=None):
509 BaseSection.__init__(self, shdr, data)
512 class SSymtab(BaseSection):
513 entsize = struct.calcsize(Elf64_Sym.format)
514 def __init__(self, shdr, data=None):
516 BaseSection.__init__(self, shdr, data)
518 def fromBinArray(self, data):
519 BaseSection.fromBinArray(self, data)
520 nument = len(data) / self.entsize
521 for i in range(nument):
522 start = i * self.entsize
523 end = i * self.entsize + self.entsize
524 self.symtab.append(Elf64_Sym(data[start:end]))
526 def resolve_names(self, elf):
527 # For a symtab, the strtab is indicated by sh_link
528 strtab = elf.shdrs[self.header.sh_link].content
529 # Resolve for all symbols in the table
530 for sym in self.symtab:
531 sym.name = strtab[sym.st_name]
533 def __getitem__(self, key):
534 return self.symtab[key]
537 class SStrtab(BaseSection):
538 def __init__(self, shdr, data=None):
540 BaseSection.__init__(self, shdr, data)
542 def fromBinArray(self, data):
543 BaseSection.fromBinArray(self, data)
544 itab = data.tostring().split('\0')
547 self.strtab[i] = sname
550 def __getitem__(self, key):
551 if key in self.strtab:
552 return self.strtab[key]
554 v = self.data[key:].tostring().split('\0')[0]
559 return self.strtab.iteritems()
562 class SRela(BaseSection):
563 entsize = struct.calcsize(Elf64_Rela.format)
564 def __init__(self, shdr, data=None):
566 BaseSection.__init__(self, shdr, data)
568 def fromBinArray(self, data):
569 BaseSection.fromBinArray(self, data)
570 nument = len(data) / self.entsize
571 for i in range(nument):
572 start = i * self.entsize
573 end = i * self.entsize + self.entsize
574 self.relatab.append(Elf64_Rela(data[start:end]))
576 def resolve_names(self, elf):
577 """Badly named, this wil resolve to a symtab entry..."""
578 # sh_link leads to the symtab
579 self.symtab = elf.shdrs[self.header.sh_link].content
580 # sh_info links to the section on which the relocation applies
581 self.header.target = elf.shdrs[self.header.sh_info]
582 for r in self.relatab:
583 r.symbol = self.symtab[r.r_sym]
587 class SHash(BaseSection):
591 class SDynamic(BaseSection):
595 class SNote(BaseSection):
599 class SNobits(BaseSection):
606 return self.header.sh_size
609 def toBinArray(self):
612 class SRel(BaseSection):
616 class SShlib(BaseSection):
620 class SDynsym(SSymtab):
624 class Elf64_Phdr(object):
626 size = struct.calcsize(format)
631 object.__init__(self)
632 self.p_type = PT_NULL
633 self.p_flags = PF_X + PF_W + PF_R
643 def toBinArray(self):
644 res = struct.pack(self.format, self.p_type, self.p_flags, self.p_offset,
645 self.p_vaddr, self.p_paddr, self.p_filesz, self.p_memsz, self.p_align)
651 #def add_content(self, content):
652 # self.content.append(content)
654 #def add_empty_content(self, content):
655 # self.nobits.append(content)
660 # return sum(s.sh_size for s in self.content)
664 class BaseSegment(object):
665 def __init__(self, align=0):
666 object.__init__(self)
670 def add_content(self, content):
671 self.content.append(content)
673 def toBinArray(self):
675 for c in self.content:
676 ba.extend(c.toBinArray())
682 return sum(c.size for c in self.content)
688 class TextSegment(BaseSegment):
689 def __init__(self, align=0):
690 BaseSegment.__init__(self, align)
693 virt_addr = self.virt_addr
694 file_offset = self.file_offset
695 for i in self.content:
696 i.virt_addr = virt_addr
697 i.file_offset = file_offset
699 virt_addr += i.logical_size
700 file_offset += i.physical_size
703 class DataSegment(BaseSegment):
704 def __init__(self, align=0):
705 BaseSegment.__init__(self, align)
708 def add_nobits(self, content):
709 self.nobits.append(content)
712 virt_addr = self.virt_addr
713 file_offset = self.file_offset
714 for i in self.content:
715 i.virt_addr = virt_addr
716 i.file_offset = file_offset
718 virt_addr += i.logical_size
719 file_offset += i.physical_size
720 for i in self.nobits:
721 i.virt_addr = virt_addr
724 virt_addr += i.logical_size
729 return self.physical_size + sum(c.logical_size for c in self.nobits)
734 class PStrtab(object):
736 object.__init__(self)
738 self.virt_addr = None
740 def append(self, string):
742 offset = self.table[-1][0]
743 offset += len(self.table[-1][1])
746 new_str = string + '\0'
747 self.table.append((offset, new_str))
753 return (self.table[-1][0] + len(self.table[-1][1]))
758 def toBinArray(self):
760 for s in (i[1] for i in self.table):
768 class Dynamic(object):
770 object.__init__(self)
772 self.strtab = PStrtab()
777 # End the table with a DT_NULL without associated value.
778 return (Elf64_Dyn.size * len(self.dyntab) + struct.calcsize("Q"))
783 def add_shlib(self, shlib):
784 offset = self.strtab.append(shlib)
785 self.dyntab.append((DT_NEEDED, offset))
787 def add_symtab(self, vaddr):
788 self.dyntab.append((DT_SYMTAB, vaddr))
791 self.dyntab.append((DT_DEBUG, 0))
794 # Adjust the address of the strtab, if
795 if self.strtab.virt_addr is None:
796 print "Ooops, strtab's address is not known yet. Aborting."
799 self.dyntab.append((DT_STRTAB, self.strtab.virt_addr))
802 def dt_debug_address():
804 for i, d in enumerate(self.dyntab):
806 return self.virt_addr + (i*16 + 8)
810 def toBinArray(self):
812 for i in self.dyntab:
813 s = struct.pack("<2Q", i[0], i[1])
815 null = struct.pack("<Q", DT_NULL)
820 class Interpreter(object):
821 default_interpreter = "/lib64/ld-linux-x86-64.so.2"
823 def __init__(self, interpreter=None):
824 object.__init__(self)
826 self.interpreter = interpreter
828 self.interpreter = self.default_interpreter
834 return len(self.interpreter) + 1
839 def toBinArray(self):
840 ba = BinArray(self.interpreter)