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