]> git.alrj.org Git - bold.git/blob - Bold/elf.py
Add support for actual dynamic linking, with external symbol resolution.
[bold.git] / Bold / elf.py
1 # -*- coding: utf-8 -*-
2
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 from BinArray import BinArray
15 from constants import *
16 from errors import *
17 import struct
18
19 # Helpful decorator
20 def nested_property(c):
21   return property(**c())
22
23 #--------------------------------------------------------------------------
24 #  Elf
25 #--------------------------------------------------------------------------
26
27 class Elf64(object):
28   """Handles an Elf64 object."""
29   interpreter = "/lib64/ld-linux-x86-64.so.2"
30
31   def __init__(self, path=None):
32     object.__init__(self)
33     self.header = Elf64_Ehdr()
34     self.header.owner = self
35     self.shdrs = []
36     self.phdrs = []
37     self.shlibs = []
38     self.sections = {}
39     self.segments = []
40     self.local_symbols = {}
41     self.global_symbols = {}
42     self.undefined_symbols = []
43
44     if path:
45       self.filename = path
46       self.fromfile(path)
47
48   # Functions for relocatables files used as input
49
50   def fromfile(self, path):
51     f = file(path, "rb")
52
53     # Load Elf header
54     data = BinArray()
55     data.fromfile(f, Elf64_Ehdr.size)
56     self.header.fromBinArray(data)
57
58     # This linker only supports relocatable objects
59     if self.header.e_type != ET_REL:
60       raise NotRelocatableObject(path)
61
62     if self.header.e_ident.ei_class != ELFCLASS64:
63       raise UnsupportedObject(path, "Not %s" % ELFCLASS64)
64
65     if self.header.e_machine != EM_X86_64:
66       raise UnsupportedObject(path, "Not %s" % EM_X86_64)
67
68     # Load sections headers
69     f.seek(self.header.e_shoff)
70     for i in range(self.header.e_shnum):
71       data = BinArray()
72       data.fromfile(f, self.header.e_shentsize)
73       h = Elf64_Shdr(i, data)
74       h.owner = self
75       self.shdrs.append(h)
76
77     # Read sections content
78     for sh in self.shdrs:
79       data = BinArray()
80       if sh.sh_type != SHT_NOBITS:
81         f.seek(sh.sh_offset)
82         data.fromfile(f, sh.sh_size)
83       sh.content = data
84
85     f.close()
86
87   def resolve_names(self):
88     # The .shstrtab index is in Elf Header. find the sections names
89     strtab = self.shdrs[self.header.e_shstrndx].content
90
91     for sh in self.shdrs:
92       sh.name = strtab[int(sh.sh_name)]
93       self.sections[sh.name] = sh
94
95       # And resolve names in the section itself
96       sh.resolve_names()
97
98
99   def find_symbols(self):
100     for sh in self.shdrs:
101       if sh.sh_type == SHT_SYMTAB:
102         symtab = sh.content.symtab
103
104         for symbol in symtab:
105           if symbol.st_type == STT_FILE:
106             continue
107           if symbol.st_shndx == SHN_ABS:
108             continue
109           if symbol.st_shndx == SHN_UNDEF:
110             if symbol.name:
111               self.undefined_symbols.append(symbol.name)
112             continue
113
114           target_section = self.shdrs[symbol.st_shndx]
115
116           symbol_name = symbol.name
117           value = symbol.st_value
118           bind = symbol.st_binding
119
120           # We got a name, a target section, and an offset in the section
121           if symbol.st_binding == STB_LOCAL:
122             if symbol.st_type == STT_SECTION:
123               symbol_name = target_section.name
124             self.local_symbols[symbol_name] = (target_section, value)
125           else:
126             self.global_symbols[symbol_name] = (target_section, value)
127
128   def apply_relocation(self, all_global_symbols):
129     # find relocation tables
130     relocations = [sh for sh in self.shdrs if sh.sh_type in [SHT_REL, SHT_RELA]]
131     for sh in relocations:
132       target = sh.target.content
133
134       for reloc in sh.content.relatab:
135         if reloc.symbol.st_shndx == SHN_UNDEF:
136           # This is an extern symbol, find it in all_global_symbols
137           sym_address = all_global_symbols[reloc.symbol.name]
138         else:
139           # source == in which section it is defined
140           source = self.shdrs[reloc.symbol.st_shndx].content
141           sym_address = source.virt_addr + reloc.symbol.st_value
142
143         target_ba = target.data # The actual BinArray that we'll modify
144         pc_address = target.virt_addr + reloc.r_offset
145
146         if reloc.r_type == R_X86_64_64:
147           format = "<Q" # Direct 64 bit address
148           target_value = sym_address + reloc.r_addend
149         elif reloc.r_type == R_X86_64_PC32:
150           format = "<i" # PC relative 32 bit signed
151           target_value = sym_address + reloc.r_addend - pc_address
152         elif reloc.r_type == R_X86_64_32:
153           format = "<I" # Direct 32 bit zero extended
154           target_value = sym_address + reloc.r_addend
155         elif reloc.r_type == R_X86_64_PC16:
156           format = "<h" # 16 bit sign extended pc relative
157           target_value = sym_address + reloc.r_addend - pc_address
158         elif reloc.r_type == R_X86_64_16:
159           format = "<H" # Direct 16 bit zero extended
160           target_value = sym_address + reloc.r_addend
161         elif reloc.r_type == R_X86_64_PC8:
162           format = "b" # 8 bit sign extended pc relative
163           target_value = sym_address + reloc.r_addend - pc_address
164         elif reloc.r_type == R_X86_64_8:
165           format = "b" # Direct 8 bit sign extended
166           target_value = sym_address + reloc.r_addend
167         else:
168           print "Unsupported relocation type: %s" % reloc.r_type
169           exit(1)
170
171         d = BinArray(struct.pack(format, target_value))
172         start = reloc.r_offset
173         end = start + len(d)
174         target_ba[start:end] = d
175
176
177   # Functions for executables files, as output
178
179   def add_phdr(self, phdr):
180     self.phdrs.append(phdr)
181     self.header.e_phnum = len(self.phdrs)
182     phdr.owner = self
183
184   def add_segment(self, segment):
185     self.segments.append(segment)
186
187   def layout(self, base_vaddr):
188     """Do the actual layout for final executable."""
189
190     virt_addr = base_vaddr
191     file_offset = 0
192     self.virt_addr = base_vaddr
193     self.file_offset = file_offset
194     for s in self.segments:
195         virt_addr += s.align
196         s.virt_addr = virt_addr
197         s.file_offset = file_offset
198         s.layout()
199         virt_addr += s.logical_size
200         file_offset += s.physical_size
201
202   def toBinArray(self):
203     ba = BinArray()
204     for s in self.segments:
205       ba.extend(s.toBinArray())
206     return ba
207
208
209 #--------------------------------------------------------------------------
210 #  Elf file header
211 #--------------------------------------------------------------------------
212
213 class Elf64_eident(object):
214   """Detailed representation for the Elf identifier."""
215   format = "16B"
216   size = struct.calcsize(format)
217   physical_size = size
218   logical_size = size
219
220   def __init__(self, rawdata=None):
221     object.__init__(self)
222     if rawdata is not None:
223       self.fromBinArray(rawdata)
224
225   def fromBinArray(self, rawdata):
226     t = struct.unpack(self.format, rawdata)
227     self.ei_magic = rawdata[:4]
228     self.ei_class = ElfClass(rawdata[4])
229     self.ei_data = ElfData(rawdata[5])
230     self.ei_version = ElfVersion(rawdata[6])
231     self.ei_osabi = ElfOsAbi(rawdata[7])
232     self.ei_abiversion = 0
233     self.ei_pad = [0, 0, 0, 0, 0, 0, 0]
234
235   def make_default_amd64(self):
236     self.ei_magic = BinArray([0x7f, 0x45, 0x4c, 0x46])
237     self.ei_class = ELFCLASS64
238     self.ei_data = ELFDATA2LSB
239     self.ei_version = EV_CURRENT
240     self.ei_osabi = ELFOSABI_SYSV
241     self.ei_abiversion = 0
242     self.ei_pad = [0, 0, 0, 0, 0, 0, 0]
243
244   def toBinArray(self):
245     ba = BinArray(self.ei_magic)
246     ba.append(self.ei_class)
247     ba.append(self.ei_data)
248     ba.append(self.ei_version)
249     ba.append(self.ei_osabi)
250     ba.append(self.ei_abiversion)
251     ba.extend(self.ei_pad)
252     return ba
253
254
255 class Elf64_Ehdr(object):
256   """Elf file header"""
257   format = "<16B 2H I 3Q I 6H"
258   size = struct.calcsize(format)
259   physical_size = size
260   logical_size = size
261   
262   def __init__(self, rawdata=None):
263     object.__init__(self)
264     self.e_ident = Elf64_eident()
265     self.e_type = ET_NONE
266     self.e_machine = EM_X86_64
267     self.e_version = EV_CURRENT
268     self.e_entry = 0
269     self.e_phoff = 0
270     self.e_shoff = 0
271     self.e_flags = 0
272     self.e_ehsize = self.size
273     self.e_phentsize = Elf64_Phdr.size
274     self.e_phnum = 0
275     self.e_shentsize = Elf64_Shdr.size
276     self.e_shnum = 0
277     self.e_shstrndx = 0
278     if rawdata is not None:
279       self.fromBinArray(rawdata)
280
281   def fromBinArray(self, rawdata):
282     t = struct.unpack(self.format, rawdata)
283     self.e_ident = Elf64_eident(BinArray(rawdata[:16]))
284     self.e_type = ElfType(t[16])
285     self.e_machine = ElfMachine(t[17])
286     self.e_version = ElfVersion(t[18])
287     self.e_entry = t[19]
288     self.e_phoff = t[20]
289     self.e_shoff = t[21]
290     self.e_flags = t[22]
291     self.e_ehsize = t[23]
292     self.e_phentsize = t[24]
293     self.e_phnum = t[25]
294     self.e_shentsize = t[26]
295     self.e_shnum = t[27]
296     self.e_shstrndx = t[28]
297
298   def toBinArray(self):
299     # Build a list from e_ident and all other fields, to feed struct.pack.
300     values = self.e_ident.toBinArray().tolist()
301     values.extend([self.e_type, self.e_machine, self.e_version, self.e_entry,
302       self.e_phoff, self.e_shoff, self.e_flags, self.e_ehsize, self.e_phentsize,
303       self.e_phnum, self.e_shentsize, self.e_shnum, self.e_shstrndx])
304     res = struct.pack(self.format, *values)
305     return BinArray(res)
306
307   def layout(self):
308     pass
309
310
311 #--------------------------------------------------------------------------
312 #  Elf Sections
313 #--------------------------------------------------------------------------
314
315 class Elf64_Shdr(object):
316   """Elf64 section header."""
317   format = "<2I 4Q 2I 2Q"
318   size = struct.calcsize(format)
319   physical_size = size
320   logical_size = size
321   
322   def __init__(self, index=None, rawdata=None):
323     object.__init__(self)
324     self.index = index
325     if rawdata is not None:
326       self.fromBinArray(rawdata)
327
328   def fromBinArray(self, rawdata):
329     t = struct.unpack(self.format, rawdata)
330     self.sh_name = t[0]
331     self.sh_type = ElfShType(t[1])
332     self.sh_flags = t[2]
333     self.sh_addr = t[3]
334     self.sh_offset = t[4]
335     self.sh_size = t[5]
336     self.sh_link = t[6]
337     self.sh_info = t[7]
338     self.sh_addralign = t[8]
339     self.sh_entsize = t[9]
340
341   def resolve_names(self):
342     self.content.resolve_names(self.owner)
343
344   @nested_property
345   def content():
346     def fget(self):
347       return self._content
348     def fset(self, data):
349       """Use the Section factory to get the subclass corresponding to the
350          session type specified in this header)."""
351       self._content = Section(self, data)
352     return locals()
353
354 # For sections that contain elements of specific types :
355
356 class Elf64_Sym(object):
357   """Symbol Table entry"""
358   format = "<I 2B H 2Q "
359   entsize = struct.calcsize(format)
360   def __init__(self, rawdata=None):
361     object.__init__(self)
362     if rawdata is not None:
363       self.fromBinArray(rawdata)
364
365   @nested_property
366   def st_binding():
367     def fget(self):
368       return ElfSymbolBinding((self.st_info >> 4) & 0x0f)
369     def fset(self, value):
370       self.st_info = (((value & 0x0f) << 4) | (self.st_info & 0x0f))
371     return locals()
372
373   @nested_property
374   def st_type():
375     def fget(self):
376        return ElfSymbolType(self.st_info & 0x0f)
377     def fset(self, value):
378       self.st_info = ((self.st_info & 0xf0) | (value & 0x0f))
379     return locals()
380
381   @nested_property
382   def st_visibility():
383     def fget(self):
384       return ElfSymbolVisibility(self.st_other & 0x03)
385     def fset(self, value):
386       self.st_other = ((self.st_other & 0xfc) | (value & 0x03))
387     return locals()
388
389   def fromBinArray(self, rawdata):
390     t = struct.unpack(self.format, rawdata)
391     self.st_name = t[0] # index in the strtab pointed by sh_link
392     self.st_info = t[1]
393     self.st_other = t[2]
394     self.st_shndx = ElfSectionIndex(t[3])
395     self.st_value = t[4]
396     self.st_size = t[5]
397
398
399 class Elf64_Rel(object):
400   format = "<2Q"
401   def __init__(self, rawdata=None):
402     object.__init__(self)
403     self.r_addend = 0 # No addend in a Rel.
404     if rawdata is not None:
405       self.fromBinArray(rawdata)
406
407   def fromBinArray(sef, rawdata):
408     t = struct.unpack(self.format, rawdata)
409     self.r_offset = t[0]
410     self.r_info = t[1]
411
412   @nested_property
413   def r_sym():
414     def fget(self):
415       return (self.r_info >> 32) & 0xffffffff
416     def fset(self, value):
417       self.r_info = ((value & 0xffffffff) << 32) | (self.r_info & 0xffffffff)
418     return locals()
419
420   @nested_property
421   def r_type():
422     def fget(self):
423       return Amd64Relocation(self.r_info & 0xffffffff)
424     def fset(self, value):
425       self.r_info = (self.r_info & 0xffffffff00000000) | (value & 0xffffffff)
426     return locals()
427
428
429 class Elf64_Rela(Elf64_Rel):
430   format = "<2Q q"
431   def __init__(self, rawdata=None):
432     Elf64_Rel.__init__(self, rawdata)
433
434   def fromBinArray(self, rawdata):
435     t = struct.unpack(self.format, rawdata)
436     self.r_offset = t[0]
437     self.r_info = t[1]
438     self.r_addend = t[2]
439
440
441 class Elf64_Dyn(object):
442   format = "<2Q"
443   size = struct.calcsize(format)
444   def __init__(self, tag, value):
445     object.__init__(self)
446     self.d_tag = tag
447     self.d_val = value
448
449   @nested_property
450   def d_ptr():
451     def fget(self):
452       return self.d_val
453     def fset(self, value):
454       self.d_val = value
455     return locals()
456
457   def toBinArray(self):
458     ba = BinArray()
459     ba.fromstring(struct.pack(self.format, self.d_tag, self.d_val))
460     return ba
461
462 # Sections types :
463
464 def Section(shdr, data=None):
465   """A section factory"""
466   dataclass = {
467     SHT_NULL:           SNull,
468     SHT_PROGBITS:       SProgBits,
469     SHT_SYMTAB:         SSymtab,
470     SHT_STRTAB:         SStrtab,
471     SHT_RELA:           SRela,
472     SHT_HASH:           SHash,
473     SHT_DYNAMIC:        SDynamic,
474     SHT_NOTE:           SNote,
475     SHT_NOBITS:         SNobits,
476     SHT_REL:            SRel,
477     SHT_SHLIB:          SShlib,
478     SHT_DYNSYM:         SDynsym
479   }
480   if shdr.sh_type in dataclass:
481     return dataclass[shdr.sh_type](shdr, data)
482   else:
483     return BaseSection(shdr, data)
484
485
486 class BaseSection(object):
487   def __init__(self, shdr, rawdata=None):
488     object.__init__(self)
489     self.data = None
490     self.header = shdr
491     if rawdata is not None:
492       self.fromBinArray(rawdata)
493
494   def fromBinArray(self, rawdata):
495     self.data = rawdata
496
497   def toBinArray(self):
498     if self.data:
499       return self.data
500     else:
501       return BinArray()
502
503   def resolve_names(self, elf):
504     """Nothing to resolve."""
505     pass
506
507   @nested_property
508   def size():
509     def fget(self):
510       return len(self.data)
511     return locals()
512   physical_size = size
513   logical_size = size
514
515   def layout(self):
516     pass
517
518
519 class SNull(BaseSection):
520   def __init__(self, shdr, data=None):
521     BaseSection.__init__(self, shdr, None)
522
523
524 class SProgBits(BaseSection):
525   def __init__(self, shdr, data=None):
526     BaseSection.__init__(self, shdr, data)
527
528
529 class SSymtab(BaseSection):
530   entsize = struct.calcsize(Elf64_Sym.format)
531   def __init__(self, shdr, data=None):
532     self.symtab = []
533     BaseSection.__init__(self, shdr, data)
534
535   def fromBinArray(self, data):
536     BaseSection.fromBinArray(self, data)
537     nument = len(data) / self.entsize
538     for i in range(nument):
539       start = i * self.entsize
540       end = i * self.entsize + self.entsize
541       self.symtab.append(Elf64_Sym(data[start:end]))
542
543   def resolve_names(self, elf):
544     # For a symtab, the strtab is indicated by sh_link
545     strtab = elf.shdrs[self.header.sh_link].content
546     # Resolve for all symbols in the table
547     for sym in self.symtab:
548       sym.name = strtab[sym.st_name]
549
550   def __getitem__(self, key):
551     return self.symtab[key]
552
553
554 class SStrtab(BaseSection):
555   """This one behaves in two completely different ways.
556   If it's given a section header and data, it will act as read-only, only to
557   be used for name resolution.
558   If it's not given any argument, it can be used to create a new Strtab."""
559   def __init__(self, shdr=None, data=None):
560     self.readonly = (shdr is not None)
561     self.by_index = {}
562     self.by_name = {}
563     self.table = []
564     BaseSection.__init__(self, shdr, data)
565     self.virt_addr = None
566
567   def toBinArray(self):
568     if self.readonly:
569       return BaseSection.toBinArray()
570
571     ba = BinArray()
572     keys = self.by_index.keys()
573     keys.sort()
574     for k in keys:
575       ba.fromstring(self.by_index[k] + "\0")
576     return ba
577
578   @nested_property
579   def size():
580     def fget(self):
581       if len(self.by_index) == 0:
582         return 0
583       return len(self.data)
584     return locals()
585   physical_size = size
586   logical_size = size
587
588   def iteritems(self):
589     return self.by_index.iteritems()
590
591   # Resolution functions
592
593   def fromBinArray(self, data):
594     BaseSection.fromBinArray(self, data)
595     itab = data.tostring().split('\0')
596     i = 0
597     for sname in itab:
598       self.by_index[i] = sname
599       self.by_name[sname] = i
600       i += len(sname) + 1
601
602   def __getitem__(self, key):
603     if isinstance(key, int):
604       # Find string by index
605       if key in self.by_index:
606         # Already computed, return it
607         return self.by_index[key]
608       else:
609         # It references a substring
610         v = self.data[key:].tostring().split('\0')[0]
611         self.by_index[key] = v
612         self.by_name[v] = key
613         return v
614     else:
615       # find index by name
616       if key in self.by_name:
617         return self.by_name[key]
618       else:
619         raise KeyError(key)
620
621   # Executable creation functions
622
623   def append(self, identifier):
624     if len(self.by_name) == 0:
625       offset = 0
626     else:
627       last = max(self.by_index.keys())
628       offset = last + len(self.by_index[last]) + 1 # for the \0
629     self.by_index[offset] = identifier
630     self.by_name[identifier] = offset
631     self.data = self.toBinArray()
632     return offset
633
634   def layout(self):
635     pass
636
637
638 class SRela(BaseSection):
639   entsize = struct.calcsize(Elf64_Rela.format)
640   def __init__(self, shdr, data=None):
641     self.relatab = []
642     BaseSection.__init__(self, shdr, data)
643
644   def fromBinArray(self, data):
645     BaseSection.fromBinArray(self, data)
646     nument = len(data) / self.entsize
647     for i in range(nument):
648       start = i * self.entsize
649       end = i * self.entsize + self.entsize
650       self.relatab.append(Elf64_Rela(data[start:end]))
651
652   def resolve_names(self, elf):
653     """Badly named, this wil resolve to a symtab entry..."""
654     # sh_link leads to the symtab
655     self.symtab = elf.shdrs[self.header.sh_link].content
656     # sh_info links to the section on which the relocation applies
657     self.header.target = elf.shdrs[self.header.sh_info]
658     for r in self.relatab:
659       r.symbol = self.symtab[r.r_sym]
660
661
662
663 class SHash(BaseSection):
664   pass
665
666
667 class SDynamic(BaseSection):
668   pass
669
670
671 class SNote(BaseSection):
672   pass
673
674
675 class SNobits(BaseSection):
676   size = 0
677   physical_size = 0
678
679   @nested_property
680   def logical_size():
681     def fget(self):
682       return self.header.sh_size
683     return locals()
684
685   def toBinArray(self):
686     return BinArray()
687
688
689 class SRel(BaseSection):
690   pass
691
692
693 class SShlib(BaseSection):
694   pass
695
696
697 class SDynsym(SSymtab):
698   pass
699
700
701 class Elf64_Phdr(object):
702   format = "<2I 6Q"
703   size = struct.calcsize(format)
704   physical_size = size
705   logical_size = size
706
707   def __init__(self):
708     object.__init__(self)
709     self.p_type = PT_NULL
710     self.p_flags = PF_X + PF_W + PF_R
711     self.p_offset = 0
712     self.p_vaddr = 0
713     self.p_paddr = 0
714     self.p_filesz = 0
715     self.p_memsz = 0
716     self.p_align = 1
717
718   def toBinArray(self):
719     res = struct.pack(self.format, self.p_type, self.p_flags, self.p_offset,
720       self.p_vaddr, self.p_paddr, self.p_filesz, self.p_memsz, self.p_align)
721     return BinArray(res)
722
723   def layout(self):
724     pass
725
726   def update_from_content(self, content):
727     """ Update ofset, address and sizes.
728     After having applied layout(),the content knows all these values."""
729     self.p_offset = content.file_offset
730     self.p_vaddr = content.virt_addr
731     self.p_filesz = content.physical_size
732     self.p_memsz = content.logical_size
733
734
735 class BaseSegment(object):
736   def __init__(self, align=0):
737     object.__init__(self)
738     self.align = align
739     self.content = []
740
741   def add_content(self, content):
742     self.content.append(content)
743
744   def toBinArray(self):
745     ba = BinArray()
746     for c in self.content:
747       ba.extend(c.toBinArray())
748     return ba
749
750   @nested_property
751   def size():
752     def fget(self):
753       return sum(c.size for c in self.content)
754     return locals()
755   physical_size = size
756   logical_size = size
757
758
759 class TextSegment(BaseSegment):
760   def __init__(self, align=0):
761     BaseSegment.__init__(self, align)
762
763   def layout(self):
764     virt_addr = self.virt_addr
765     file_offset = self.file_offset
766     for i in self.content:
767       i.virt_addr = virt_addr
768       i.file_offset = file_offset
769       i.layout()
770       virt_addr += i.logical_size
771       file_offset += i.physical_size
772
773
774 class DataSegment(BaseSegment):
775   def __init__(self, align=0):
776     BaseSegment.__init__(self, align)
777     self.nobits = []
778
779   def add_nobits(self, content):
780     self.nobits.append(content)
781
782   def layout(self):
783     virt_addr = self.virt_addr
784     file_offset = self.file_offset
785     for i in self.content:
786       i.virt_addr = virt_addr
787       i.file_offset = file_offset
788       i.layout()
789       virt_addr += i.logical_size
790       file_offset += i.physical_size
791     for i in self.nobits:
792       i.virt_addr = virt_addr
793       i.file_offset = 0
794       i.layout()
795       virt_addr += i.logical_size
796
797   @nested_property
798   def logical_size():
799     def fget(self):
800       return self.physical_size + sum(c.logical_size for c in self.nobits)
801     return locals()
802
803
804 class Dynamic(object):
805   def __init__(self):
806     object.__init__(self)
807     self.dyntab = []
808     self.strtab = SStrtab()
809
810   @nested_property
811   def size():
812     def fget(self):
813       # End the table with a DT_NULL without associated value.
814       return (Elf64_Dyn.size * len(self.dyntab) + struct.calcsize("Q"))
815     return locals()
816   physical_size = size
817   logical_size = size
818
819   def add_shlib(self, shlib):
820     offset = self.strtab.append(shlib)
821     self.dyntab.append(Elf64_Dyn(DT_NEEDED, offset))
822
823   def add_symtab(self, vaddr):
824     self.dyntab.append(Elf64_Dyn(DT_SYMTAB, vaddr))
825
826   def add_debug(self):
827     self.dyntab.append(Elf64_Dyn(DT_DEBUG, 0))
828
829   def layout(self):
830     # Adjust the address of the strtab, if 
831     if self.strtab.virt_addr is None:
832       print "Ooops, strtab's address is not known yet. Aborting."
833       exit(1)
834     else:
835       self.dyntab.append(Elf64_Dyn(DT_STRTAB, self.strtab.virt_addr))
836
837   @nested_property
838   def dt_debug_address():
839     def fget(self):
840       for i, d in enumerate(self.dyntab):
841         if d.d_tag == DT_DEBUG:
842           return self.virt_addr + (i*d.size + (d.size/2))
843     return locals()
844
845
846   def toBinArray(self):
847     ba = BinArray()
848     for d in self.dyntab:
849       ba.extend(d.toBinArray())
850     null = struct.pack("<Q", DT_NULL)
851     ba.fromstring(null)
852     return ba
853
854
855 class Interpreter(object):
856   """
857   Pseudo-section containing the null terminated string referencing the
858   interpreter to use.
859
860   @ivar size: Read-only attribute, size of the null terminated string.
861   @ivar logical_size: alias to size
862   @ivar physical_size: alias to size
863   """
864   default_interpreter = "/lib64/ld-linux-x86-64.so.2"
865
866   def __init__(self, interpreter=None):
867     """
868     @param interpreter: The interpreter ot use. Defaults to
869       "/lib64/ld-linux-x86-64.so.2", as per the specs.
870     @type interpreter: string
871     """
872     object.__init__(self)
873     if interpreter:
874       self.interpreter = interpreter
875     else:
876       self.interpreter = self.default_interpreter
877
878   @nested_property
879   def size():
880     def fget(self):
881       # Null terminated
882       return len(self.interpreter) + 1
883     return locals()
884   physical_size = size
885   logical_size = size
886
887   def toBinArray(self):
888     """
889     @return: a L{BinArray} with the content of the pseudo-section.
890     """
891     ba = BinArray(self.interpreter)
892     ba.append(0)
893     return ba
894
895   def layout(self):
896     """
897     Unused.
898     """
899     pass
900