1#!/usr/bin/env python
2#
3# This script converts a textual (YAML) description of an ELF file to
4# an equivalent 'binary' file.
5#
6# The YAML description may have the following top-level keys:
7#
8# 'elf_fillchar': char
9# 	Sets the fill character to 'char'.
10# 'ehdr': EHDR-DESCRIPTOR
11# 	Defines an ELF Ehdr structure.
12# 'phdrtab': list-of(PHDR-DESCRIPTOR)
13# 	Defines the contents of the ELF Program Header table.
14# 	Each `Phdr' descriptor represents one ELF Phdr entry.
15# 'sections': list-of(SECTION-DESCRIPTOR)
16# 	Defines the content of each section in the file.  Each
17# 	`SECTION-DESCRIPTOR' contains information for the
18# 	section `header' and the actual data for the section.
19#
20# The script will compute reasonable defaults for any fields
21# left unspecified in the YAML description.
22#
23# Descriptors EHDR-DESCRIPTOR and PHDR-DESCRIPTOR may be specified
24# as a YAML key-value set.  The key names correspond to the
25# field names of the corresponding ELF structures, e.g., 'e_machine'
26# and 'e_ident' for the Ehdr and 'p_type' and 'p_paddr' for
27# a Phdr entry.
28#
29# Descriptor SECTION-DESCRIPTOR contains the fields in an ELF
30# Shdr structure and an additional member 'sh_data', whose
31# value is the data for the section.
32#
33# Example:
34#
35# <snip>
36# ehdr: !Ehdr
37#   e_ident: !Ident
38#     ei_class: ELFCLASS32
39#     ei_data:  ELFDATA2MSB
40#   e_machine:  EM_PPC
41# phdrtab:
42#  - !Phdr
43#    ph_type: PHT_NULL
44#    ... other program header fields ...
45#  - !Phdr
46#    ... etc. ...
47# sections:
48#  - !Section
49#    sh_name: .dynsym
50#    ... other section header fields ...
51#    sh_data: # ... list of data ...
52#    - !Dyn
53#      d_tag: 0xdeadcode
54#    - !Dyn
55#      d_tag: 0xcafebabe
56#  - !Section
57#    sh_name: .shstrtab
58#    sh_type: SHT_STRTAB
59#    sh_data:
60#    - string1
61#    - string2
62# </snip>
63#
64# :: Handling of strings ::
65#
66# Fields like 'sh_name' (in a section header) are defined to contain
67# an integer index into a specified string table (in this case a
68# section with name '.shstrtab').  Other ELF data structures use a
69# similar convention; names in a '.dynamic' section as stored as
70# indices into a '.dynstr' section.  In the YAML descriptor, such
71# fields may be specified as indices, which are used as-is, or as text
72# strings which are converted to the appropriate string index.
73# For convenience in creating ELF objects with a large number of
74# sections, a section index may be manually specified using a
75# 'sh_index' pseudo field.
76#
77# $Id: elfc 1718 2011-08-12 07:30:43Z jkoshy $
78
79version     = "%prog 1.0"
80usage       = "usage: %prog [options] [input-file]"
81description = """Create an ELF binary from a textual description in """ + \
82	"""'input-file' (or stdin)"""
83
84import optparse, re, struct, sys, types, yaml
85
86class ElfError(Exception):
87	"""An exception signalled during conversion."""
88
89	def __init__(self, node=None, msg=None):
90		"""Initialize an exception object.
91
92		Arguments:
93		node	-- a YAML parse tree node.
94		msg	-- human readable message associated with this
95			   exception.
96		"""
97		if node:
98			self.ee_start = node.start_mark.line + 1
99			self.ee_end = node.end_mark.line + 1
100		else:
101			self.ee_start = self.ee_end = -1
102		self.ee_msg = msg
103
104	def __str__(self):
105		"""Form a printable representation of an exception."""
106
107		if self.ee_start != -1:
108			if self.ee_start == self.ee_end:
109				return "Error: line %d: %s" % (self.ee_start,
110					self.ee_msg)
111			else:
112				return "Error: lines %d--%d: %s" % \
113				       (self.ee_start, self.ee_end,
114					self.ee_msg)
115		else:
116			return "Error: %s" % self.ee_msg
117
118
119#
120# Mappings used by the 'encode()' function
121#
122
123elf_cap_tag = {
124	'CA_SUNW_NULL': 0, 'CA_SUNW_HW_1': 1, 'CA_SUNW_SF_1': 2
125}
126
127elf_d_flags = {
128	'DF_ORIGIN': 0x0001, 'DF_SYMBOLIC': 0x0002, 'DF_TEXTREL': 0x0004,
129	'DF_BIND_NOW': 0x0006, 'DF_STATIC_TLS': 0x0010
130}
131
132elf_d_tag = {
133	# from <sys/elf_common.h>
134	'DT_NULL': 0, 'DT_NEEDED': 1, 'DT_PLTRELSZ': 2, 'DT_PLTGOT': 3,
135	'DT_HASH': 4, 'DT_STRTAB': 5, 'DT_SYMTAB': 6, 'DT_RELA': 7,
136	'DT_RELASZ': 8, 'DT_RELAENT': 9, 'DT_STRSZ': 10, 'DT_SYMENT': 11,
137	'DT_INIT': 12, 'DT_FINI': 13, 'DT_SONAME': 14, 'DT_RPATH': 15,
138	'DT_SYMBOLIC': 16, 'DT_REL': 17, 'DT_RELSZ': 18, 'DT_RELENT': 19,
139	'DT_PLTREL': 20, 'DT_DEBUG': 21, 'DT_TEXTREL': 22, 'DT_JMPREL': 23,
140	'DT_BIND_NOW': 24, 'DT_INIT_ARRAY': 25,'DT_FINI_ARRAY': 26,
141	'DT_INIT_ARRAYSZ': 27, 'DT_FINI_ARRAYSZ': 28, 'DT_RUNPATH': 29,
142	'DT_FLAGS': 30, 'DT_ENCODING': 32, 'DT_PREINIT_ARRAY': 32,
143	'DT_PREINIT_ARRAYSZ': 33, 'DT_LOOS': 0x6000000d, 'DT_HIOS': 0x6ffff000,
144	'DT_LOPROC': 0x70000000, 'DT_HIPROC': 0x7fffffff,
145	'DT_SUNW_AUXILIARY': 0x6000000D, 'DT_SUNW_RTLDINF': 0x6000000E,
146	'DT_SUNW_FILTER': 0x6000000F, 'DT_SUNW_CAP': 0x60000010,
147	# from "usr.bin/elfdump/elfdump.c"
148	'DT_GNU_PRELINKED': 0x6ffffdf5, 'DT_GNU_CONFLICTSZ': 0x6ffffdf6,
149	'DT_GNU_LIBLISTSZ': 0x6ffffdf7, 'DT_SUNW_CHECKSUM': 0x6ffffdf78,
150	'DT_PLTPADSZ': 0x6ffffdf79, 'DT_MOVEENT': 0x6ffffdfa,
151	'DT_MOVESZ': 0x6ffffdfb, 'DT_FEATURE': 0x6ffffdfc,
152	'DT_FEATURE': 0x6ffffdfd, 'DT_POSFLAG_1': 0x6ffffdfe,
153	'DT_SYMINENT': 0x6ffffdff, 'DT_VALRNGHI': 0x6ffffdff, # dup
154	'DT_ADDRRNGLO': 0x6ffffe00, 'DT_GNU_CONFLICT': 0x6ffffef8,
155	'DT_GNU_LIBLIST': 0x6ffffef9, 'DT_SUNW_CONFIG': 0x6ffffefa,
156	'DT_SUNW_DEPAUDIT': 0x6ffffefb, 'DT_SUNW_AUDIT': 0x6ffffefc,
157	'DT_SUNW_PLTPAD': 0x6ffffefd, 'DT_SUNW_MOVETAB': 0x6ffffefe,
158	'DT_SYMINFO': 0x6ffffeff, 'DT_ADDRRNGHI': 0x6ffffeff, # dup
159	'DT_VERSYM': 0x6ffffff0, 'DT_GNU_VERSYM': 0x6ffffff0, # dup
160	'DT_RELACOUNT': 0x6ffffff9, 'DT_RELCOUNT': 0x6ffffffa,
161	'DT_FLAGS_1': 0x6ffffffb, 'DT_VERDEF': 0x6ffffffc,
162	'DT_VERDEFNUM': 0x6ffffffd, 'DT_VERNEED': 0x6ffffffe,
163	'DT_VERNEEDNUM': 0x6fffffff,
164	'DT_IA_64_PLT_RESERVE': 0x70000000, 'DT_SUNW_AUXILIARY': 0x7ffffffd,
165	'DT_SUNW_USED': 0x7ffffffe, 'DT_SUNW_FILTER': 0x7fffffff
166}
167
168elf_dyn_fields = [ 'd_tag', 'd_val', 'd_ptr' ]
169
170elf_ehdr_flags = {			# no known flags
171}
172
173elf_ehdr_type = { 				# e_type
174	'ET_NONE': 0, 'ET_REL': 1, 'ET_EXEC': 2, 'ET_DYN': 3, 'ET_CORE': 4
175}
176
177elf_ehdr_machine = {				# e_machine
178	'EM_NONE': 0, 'EM_M32': 1, 'EM_SPARC': 2, 'EM_386': 3, 'EM_68K': 4,
179	'EM_88K': 5, 'EM_486': 6, 'EM_860': 7, 'EM_MIPS': 8, 'EM_S370': 9,
180	'EM_MIPS_RS3_LE': 10, 'EM_MIPS_RS4_BE': 10, 'EM_PARISC': 15,
181	'EM_VPP500': 17, 'EM_SPARC32PLUS': 18, 'EM_960': 19, 'EM_PPC': 20,
182	'EM_PPC64': 21, 'EM_S390': 22, 'EM_V800': 36, 'EM_FR20': 37,
183	'EM_RH32': 38, 'EM_RCE': 39, 'EM_ARM': 40, 'EM_ALPHA_STD': 41,
184	'EM_SH': 42, 'EM_SPARCV9': 43, 'EM_TRICORE': 44, 'EM_ARC': 45,
185	'EM_H8_300': 46, 'EM_H8_300H': 47, 'EM_H8S': 48, 'EM_H8_500': 49,
186	'EM_IA_64': 50, 'EM_MIPS_X': 51, 'EM_COLDFIRE': 52,
187	'EM_68HC12': 53, 'EM_MMA': 54, 'EM_PCP': 55, 'EM_NCPU': 56,
188	'EM_NDR1': 57, 'EM_STARCORE': 58, 'EM_ME16': 59, 'EM_ST100': 60,
189	'EM_TINYJ': 61, 'EM_X86_64': 62, 'EM_ALPHA': 0x9026
190}
191
192elf_ei_version = {				# e_version
193	'EV_NONE': 0, 'EV_CURRENT': 1
194}
195
196elf_ei_class = {
197	'ELFCLASSNONE': 0, 'ELFCLASS32': 1, 'ELFCLASS64': 2
198}
199
200elf_ei_data = {
201	'ELFDATANONE': 0, 'ELFDATA2LSB': 1, 'ELFDATA2MSB': 2
202}
203
204elf_ei_osabi = {
205	# Official values.
206	'ELFOSABI_NONE': 0,
207	'ELFOSABI_HPUX': 1,
208	'ELFOSABI_NETBSD': 2,
209	'ELFOSABI_GNU': 3,
210	'ELFOSABI_HURD': 4,
211	'ELFOSABI_86OPEN': 5,
212	'ELFOSABI_SOLARIS': 6,
213	'ELFOSABI_AIX': 7,
214	'ELFOSABI_IRIX': 8,
215	'ELFOSABI_FREEBSD': 9,
216	'ELFOSABI_TRU64': 10,
217	'ELFOSABI_MODESTO': 11,
218	'ELFOSABI_OPENBSD': 12,
219	'ELFOSABI_OPENVMS': 13,
220	'ELFOSABI_NSK': 14,
221	'ELFOSABI_ARM': 97,
222	'ELFOSABI_STANDALONE': 255,
223	# Aliases.
224	'ELFOSABI_SYSV': 0,
225	'ELFOSABI_LINUX': 3,
226	'ELFOSABI_MONTEREY': 7
227}
228
229elf_ph_fields = [ 'p_align', 'p_filesz', 'p_flags', 'p_memsz', 'p_offset',
230		  'p_paddr', 'p_type', 'p_vaddr' ]
231
232elf_ph_flags = {
233	'PF_X': 0x1, 'PF_W': 0x2, 'PF_R': 0x4
234}
235
236elf_ph_type = {
237	'PT_NULL': 0, 'PT_LOAD': 1, 'PT_DYNAMIC': 2, 'PT_INTERP': 3,
238	'PT_NOTE': 4, 'PT_SHLIB': 5, 'PT_PHDR': 6, 'PT_TLS': 7,
239	'PT_LOOS': 0x60000000, 'PT_HIOS': 0x6FFFFFFF,
240	'PT_SUNW_UNWIND': 0x6464E550, 'PT_GNU_EHFRAME': 0x6464E550,	# dup
241	'PT_SUNWBSS': 0x6FFFFFFA, 'PT_SUNWSTACK': 0x6FFFFFFB,
242	'PT_SUNWDTRACE': 0x6FFFFFFC, 'PT_SUNWCAP': 0x6FFFFFFD,
243	'PT_LOPROC': 0x70000000, 'PT_HIPROC': 0x7FFFFFFF
244}
245
246elf_sh_type = {
247	'SHT_NULL': 0, 'SHT_PROGBITS': 1, 'SHT_SYMTAB': 2, 'SHT_STRTAB': 3,
248	'SHT_RELA': 4, 'SHT_HASH': 5, 'SHT_DYNAMIC': 6, 'SHT_NOTE': 7,
249	'SHT_NOBITS': 8, 'SHT_REL': 9, 'SHT_SHLIB': 10, 'SHT_DYNSYM': 11,
250	'SHT_INIT_ARRAY': 14, 'SHT_FINI_ARRAY': 15, 'SHT_PREINIT_ARRAY': 16,
251	'SHT_GROUP': 17, 'SHT_SYMTAB_SHNDX': 18, 'SHT_LOOS': 0x60000000,
252	'SHT_HIOS': 0x6fffffff, 'SHT_LOPROC': 0x70000000,
253	'SHT_HIPROC': 0x7fffffff, 'SHT_LOUSER': 0x80000000,
254	'SHT_HIUSER': 0xffffffff,
255	# OS specific types
256	'SHT_SUNW_dof': 0x6FFFFFF4, 'SHT_SUNW_cap': 0x6FFFFFF5,
257	'SHT_SUNW_SIGNATURE': 0x6FFFFFF6,
258	'SHT_SUNW_ANNOTATE': 0x6FFFFFF7, 'SHT_GNU_LIBLIST': 0x6ffffff7, # dup
259	'SHT_SUNW_DEBUGSTR': 0x6FFFFFF8, 'SHT_SUNW_DEBUG': 0x6FFFFFF9,
260	'SHT_SUNW_move': 0x6FFFFFFA, 'SHT_SUNW_COMDAT': 0x6FFFFFFB,
261	'SHT_SUNW_syminfo': 0x6FFFFFFC,
262	'SHT_GNU_verdef': 0x6ffffffd, 'SHT_SUNW_verdef': 0x6ffffffd, # dup
263	'SHT_GNU_verneed': 0x6ffffffe, 'SHT_SUNW_verneed': 0x6ffffffe, # dup
264	'SHT_GNU_versym': 0x6fffffff, 'SHT_SUNW_versym': 0x6fffffff, # dup
265	# Processor specific types
266	'SHT_IA_64_EXT': 0x70000000, 'SHT_IA_64_UNWIND': 0x70000001
267}
268
269elf_sh_flags = {
270	'SHF_WRITE': 0x1, 'SHF_ALLOC': 0x2, 'SHF_EXECINSTR': 0x4,
271	'SHF_MERGE': 0x10, 'SHF_STRINGS': 0x20, 'SHF_INFO_LINK': 0x40,
272	'SHF_LINK_ORDER': 0x80, 'SHF_OS_NONCONFORMING': 0x100,
273	'SHF_GROUP': 0x200, 'SHF_TLS': 0x400, 'SHF_MASKOS': 0x0ff00000,
274	'SHF_MASKPROC': 0xf0000000
275}
276
277elf_st_bindings = {
278	'STB_LOCAL': 0, 'STB_GLOBAL': 1, 'STB_WEAK': 2
279}
280
281elf_st_flags = {
282	'SHF_WRITE': 1, 'SHF_ALLOC': 2, 'SHF_EXECINSTR': 4
283}
284
285elf_st_types = {
286	'STT_NOTYPE': 0, 'STT_OBJECT': 1, 'STT_FUNC': 2, 'STT_SECTION': 3,
287	'STT_FILE': 3
288}
289
290elf_syminfo_flags = {
291	'SYMINFO_FLG_DIRECT': 1,
292	'SYMINFO_FLG_PASSTHRU': 2, 'SYMINFO_FLG_FILTER': 2, # dup
293	'SYMINFO_FLG_COPY': 4, 'SYMINFO_FLG_LAZYLOAD': 8,
294	'SYMINFO_FLG_DIRECTBIND': 0x10, 'SYMINFO_FLG_NOEXTDIRECT': 0x20,
295	'SYMINFO_FLG_AUXILIARY': 0x40
296}
297
298elf_syminfo_boundto_types = {
299	'SYMINFO_BT_SELF': 0xFFFF, 'SYMINFO_BT_PARENT': 0xFFFE,
300	'SYMINFO_BT_NONE': 0xFFFD, 'SYMINFO_BT_EXTERN': 0xFFFC
301}
302
303# Defaults
304
305defaults = {
306	# ElfDyn structures
307	'd_tag': 'DT_NULL',
308	'd_un': '0',
309
310	# fields in an ELf Executable Header
311	'e_ehsize': None,
312	'e_entry': '0',
313	'e_flags': [ '0' ],
314	'e_ident': None,
315	'e_machine': 'EM_NONE',
316	'e_phentsize': None,
317	'e_phnum': None,
318	'e_phoff': None,
319	'e_shentsize': None,
320	'e_shnum': None,
321	'e_shoff': None,
322	'e_shstrndx': None,
323	'e_type': 'ET_NONE',
324	'e_version': 'EV_CURRENT',
325	# e_ident bytes
326	'ei_class': 'ELFCLASS32',
327	'ei_data': 'ELFDATA2LSB',
328	'ei_version': 'EV_CURRENT',
329	'ei_osabi': 'ELFOSABI_NONE',
330	'ei_abiversion': '0',
331	# File-wide defaults
332	'elf_fillchar': '0',
333	# Elf Notes
334	'n_namesz': None,
335	'n_descsz': None,
336	'n_type': '0',
337	'n_data': [ "", "" ],
338	# Phdr
339	'p_align': '1',
340	'p_filesz': '0',
341	'p_memsz': '0',
342	'p_flags': [ '0' ],
343	'p_offset': '0',
344	'p_paddr': '0',
345	'p_type': 'PT_NULL',
346	'p_vaddr': '0',
347	# Shdr
348	'sh_addr': '0',
349	'sh_addralign': None,
350	'sh_data': [],
351	'sh_entsize': '0',
352	'sh_flags': [ '0' ],
353	'sh_info': '0',
354	'sh_index': None,
355	'sh_link': '0',
356	'sh_name': '0',
357	'sh_offset': None,
358	'sh_size': None,
359	'sh_type': 'SHT_NULL',
360	# Verdaux
361	'vda_name': 0,
362	'vda_next': 0,
363	# Verdef
364	'vd_version': 1,
365	'vd_flags': 0,
366	'vd_ndx': 0,
367	'vd_cnt': 0,
368	'vd_hash': 0,
369	'vd_aux': 0,
370	'vd_next': 0,
371	# Vernaux
372	'vna_hash': 0,
373	'vna_flags': 0,
374	'vna_other': 0,
375	'vna_name': 0,
376	'vna_next': 0,
377	# Verneed
378	'vn_version': 1,
379	'vn_cnt': 0,
380	'vn_file': 0,
381	'vn_aux': 0,
382	'vn_next': 0
383}
384
385#
386# Module wide constants.
387#
388
389ELFCLASS32  = elf_ei_class['ELFCLASS32']
390ELFDATA2LSB = elf_ei_data['ELFDATA2LSB']
391SHT_NOBITS = elf_sh_type['SHT_NOBITS']
392SHT_NULL = elf_sh_type['SHT_NULL']
393SHT_STRTAB = elf_sh_type['SHT_STRTAB']
394SHN_LORESERVE= 0xFF00
395SHN_XINDEX = 0xFFFF
396#
397# Helper functions.
398#
399
400def get(d, key, default):
401	"""Retrieve the value of 'key' from YAML dictionary 'd'.
402
403	The return value is guaranteed to be not 'None'.
404	"""
405	v = d.get(key, default)
406	if v is None:
407		v = default
408	return v
409
410def encode(d, key, default, mapping):
411	"""Return the numeric value of d[key] in map 'mapping'."""
412
413	v = get(d, key, default)
414	try:
415		return mapping[v]
416	except KeyError:
417		return int(v)
418
419def encode_flags(flags, m):
420	"""Convert 'flags' to a single numeric value using mapping 'm'."""
421	try:
422		v = long(flags)
423		return v
424	except:
425		pass
426	v = 0L
427	for f in flags:
428		try:
429			t = long(m[f])
430		except KeyError:
431			t = long(f)
432		v |= t
433	return v
434
435def check_dict(d, l, node=None):
436	"""Check a dictionary for unknown keys."""
437	unknown = []
438	for k in d.keys():
439		if k not in l:
440			unknown.append(k)
441	if len(unknown) > 0:
442		raise ElfError(node, "{%s} Unknown key(s) %s" % \
443			       (node.tag, unknown))
444
445#
446# Helper classes.
447#
448
449class ElfStrTab:
450	"""A ELF string table.
451
452	This class manages strings in an ELF string table section.
453	"""
454
455	def __init__(self, strs=None):
456		"""Initialize a string table from a list of strings."""
457		self.offset  = 1	# reserve space for initial null byte
458		self.htab = {}
459		if type(strs) == types.StringType: # one string
460			self.add(strs)
461		elif type(strs) == types.ListType: # list of strings
462			for s in strs:
463				self.add(s)
464
465	def add(self, str):
466		"""Add a string to the string table.
467
468		Returns the offset of the string in the ELF section."""
469		try:
470			return self.lookup(str)
471		except KeyError:
472			self.htab[str] = offset = self.offset
473			self.offset += len(str) + 1 # Keep space for a NUL.
474		return offset
475
476	def bits(self):
477		"""Return the contents of an ELF string table."""
478
479		l = self.htab.items()
480		l.sort(lambda x, y: cmp(x[1],y[1])) # Order by string offset.
481		ls = [""]		# initial NUL
482		for (ss,oo) in l:
483			ls.append(ss)
484		return "\000".join(ls) + "\000" # Add trailing NULs
485
486	def lookup(self, str):
487		"""Return the ELF string table offset for string 'str'."""
488
489		return self.htab[str]
490
491
492class ElfType:
493	"""A base type for ELF type descriptors.
494
495	Derived classes are expected to provide the following attributes:
496
497	'fields' -- a list of 4-typles (name, fn, lsz, msz).
498
499		    'name' is the name of a field in the ELF structure.
500
501		    'fn' is a convertor function, one of the functions
502		    'do_(long,encode,flags)' below.
503
504		    'msz' and 'lsz' provide the appropriate sizes when
505		    generating a binary representation of the type.
506	"""
507
508	fields = None
509	def __init__(self, d, node):
510		"""Initialize an ELF datatype from a YAML description.
511
512		Arguments:
513		d	-- a dictionary containing name/value pairs specified
514			   in the text description.
515		node	-- YAML parser node for this element.
516		"""
517
518		keys = map(lambda t: t[0], self.fields)
519		check_dict(d, keys, node)
520		for f in self.fields:
521			name  = f[0]
522			fn    = f[1]
523			try:
524				v = fn(d, name)
525				setattr(self,f[0],v)
526			except:
527				raise ElfError(node,
528					       'key: "%s" value: "%s" unrecognized.' % \
529							   (name, d[name]))
530		self._n = node		# Save YAML node and associated value
531		self._d = d		# for error reporting.
532
533	def __getitem__(self, attrib):
534		"""Allow an ELF type to be treated like a dictionary."""
535
536		return getattr(self, attrib)
537
538	def bits(self, formatchar, elfclass):
539		"""Convert an ELF type to its file representation."""
540
541		format, args = self.getfields(elfclass)
542		return struct.pack(formatchar + format, *args)
543
544	def formatstring(self, elfclass):
545		"""Return the format string for this type."""
546
547		if elfclass == ELFCLASS32:
548			n = 2
549		else:
550			n = 3
551		return "".join(map (lambda t: t[n], self.fields))
552
553	def content(self, elfclass):
554		"""Return a tuple containing the values for an ELF type."""
555
556		a = []
557		if elfclass == ELFCLASS32:
558			n = 2
559		else:
560			n = 3
561		for t in self.fields:
562			if t[n] != "":
563				a.append(getattr(self, t[0]))
564		return tuple(a)
565
566	def getfields(self, elfclass):
567		"""Describe the binary layout of the type.
568
569		Return a tuple (formatstring, *args) describing the
570		desired binary layout in the manner of the 'struct'
571		python library module.
572		"""
573
574		return (self.formatstring(elfclass),
575				self.content(elfclass))
576
577	def layout(self, offset, elf):
578		"""Perform any layout-time translation for an ELF type."""
579
580		return offset
581
582	def size(self, elfclass):
583		"""Return the size of the type in bytes.
584
585		The size returned is independent of the alignment needs of
586		the type.
587		"""
588
589		format = self.formatstring(elfclass)
590		sz = 0
591		for f in format:
592			if f == "B":
593				sz += 1
594			elif f == "H":
595				sz += 2
596			elif f == "I":
597				sz += 4
598			elif f == "Q":
599				sz += 8
600			elif f == "":
601				pass
602			else:
603				raise TypeError, "Invalid format char '%s'." % f
604		return sz
605
606
607#
608# Translation helper functions.
609#
610
611def do_string(d, n):
612	"""Convert a YAML value to a Python string."""
613
614	v = get(d, n, defaults[n])
615	if v:
616		return str(v)
617	return v
618
619def do_long(d, n):
620	"""Convert a YAML value to a Python 'long'."""
621
622	v = get(d, n, defaults[n])
623	if v:
624		return long(v)
625	return v
626
627def do_copy(d, n):
628	"""Copy a YAML value without conversion."""
629
630	v = get(d, n, defaults[n])
631	return v
632
633def do_encode(xlate):
634	"""Translate a YAML value according to mapping 'xlate'."""
635
636	return lambda d, n, xl=xlate: encode(d, n, defaults[n], xl)
637
638def do_flags(xlate):
639	"""Translate a list of flags according to mapping 'xlate'."""
640
641	return lambda d, n, xl=xlate: encode_flags(get(d, n, defaults[n]), xl)
642
643#
644# Definitions of ELF types.
645#
646
647class ElfCap(ElfType):
648	"""A representation of an ELF Cap structure.
649
650	YAML tag: !Cap
651	"""
652
653	fields = [
654		('c_tag', do_encode(elf_cap_tag), "I", "Q"),
655		('c_un', do_long, "I", "Q")
656		]
657	def __init__(self, cap, node):
658		ElfType.__init__(self, cap, node)
659
660
661class ElfDyn(ElfType):
662	"""A representation of an ELF Dyn structure.
663
664	YAML tag: !Dyn
665	"""
666
667	fields = [
668		('d_tag', do_encode(elf_d_tag), "I", "Q"),
669		('d_un',  do_long, "I", "Q")
670		]
671
672	def __init__(self, d, node):
673		ElfType.__init__(self, d, node)
674
675
676class ElfEhdrIdent(ElfType):
677	"""A representation for the 'ident' field of an ELF Ehdr.
678
679	YAML tag: !Ident
680	"""
681
682	fields = [
683		('ei_class', do_encode(elf_ei_class), "B", "B"),
684		('ei_data', do_encode(elf_ei_data), "B", "B"),
685		('ei_version', do_encode(elf_ei_version), "B", "B"),
686		('ei_osabi', do_encode(elf_ei_osabi), "B", "B"),
687		('ei_abiversion', do_long, "B", "B")
688		]
689
690	def __init__(self, ei, node):
691		ElfType.__init__(self, ei, node)
692
693	def bits(self, format, elfclass):
694		f, args = self.getfields(elfclass)
695		s = "\x7FELF"
696		s += struct.pack(f + 'xxxxxxx', *args)
697		return s
698
699
700class ElfEhdr(ElfType):
701	"""A representation of an ELF Executable Header.
702
703	YAML tag: !Ehdr
704	"""
705
706	fields = [
707		('e_ident', do_copy, "", ""),
708		('e_type', do_encode(elf_ehdr_type), "H", "H"),
709		('e_machine', do_encode(elf_ehdr_machine), "H", "H"),
710		('e_version', do_encode(elf_ei_version), "I", "I"),
711		('e_entry', do_long, "I", "Q"),
712		('e_phoff', do_long, "I", "Q"),
713		('e_shoff', do_long, "I", "Q"),
714		('e_flags', do_flags(elf_ehdr_flags), "I", "I"),
715		('e_ehsize', do_long, "H", "H"),
716		('e_phentsize', do_long, "H", "H"),
717		('e_phnum', do_long, "H", "H"),
718		('e_shentsize', do_long, "H", "H"),
719		('e_shnum', do_long, "H", "H"),
720		('e_shstrndx', do_copy, "H", "H")
721		]
722
723	def __init__(self, eh, node):
724		"""Initialize an Ehdr structure.
725
726		If an 'ident' structure was not specified as part of
727		the YAML description, initialize it explicitly.
728		"""
729
730		ElfType.__init__(self, eh, node)
731		if self.e_ident is None:
732			self.e_ident = ElfEhdrIdent({}, node)
733
734	def layout(self, offset, elf):
735		"""Layout an ELF Ehdr.
736
737		This method will fill in defaults and/or compute
738		values for fields that were not specified in the YAML
739		description.
740		"""
741
742		elfclass = elf.elfclass()
743		if elfclass == ELFCLASS32:
744			e_ehsize = 52
745			e_phentsize = 32
746			e_shentsize = 40
747			alignment = 4
748		else:			# 64 bit sizes
749			e_ehsize = 64
750			e_phentsize = 56
751			e_shentsize = 64
752			alignment = 8
753
754		if self.e_ehsize is None:
755			self.e_ehsize = e_ehsize
756
757		# Compute e_phnum if needed.
758		if self.e_phnum is None:
759			self.e_phnum = len(elf.elf_phdrtab)
760
761		# Compute a value for the e_phentsize field.
762		if self.e_phentsize is None:
763			if self.e_phnum:
764				self.e_phentsize = e_phentsize
765			else:
766				self.e_phentsize = 0
767
768		# Set the e_shentsize field.
769		if self.e_shentsize is None:
770			self.e_shentsize = e_shentsize
771
772		# The program header defaults to just after the ELF header.
773		if self.e_phoff is None:
774			if self.e_phnum > 0:
775				self.e_phoff = \
776			  	    (self.e_ehsize + (alignment - 1)) & \
777				    ~(alignment - 1)
778			else:
779				self.e_phoff = 0
780
781		# compute e_shnum
782		self.nsections = elf.elf_sections.get_shnum()
783		if self.nsections > 0:
784			if self.e_shstrndx is None:
785				self.e_shstrndx = '.shstrtab'
786			if type(self.e_shstrndx) == types.StringType:
787				self.e_shstrndx = \
788						elf.elf_sections.get_index(self.e_shstrndx)
789			elif type(self.e_shstrndx) == types.IntType or \
790				 type(self.e_shstrndx) == types.LongType:
791				pass
792			else:
793				raise ElfError(self._n, "Unparseable e_shstrndx field.")
794			if self.e_shstrndx is None:
795				raise ElfError(self._n,
796					       'Cannot determine section ' + \
797					       'name string table index.')
798		else:
799		    if self.e_shstrndx is None:
800			    self.e_shstrndx = 0
801
802		if self.e_shnum is None:
803			self.e_shnum = self.nsections
804
805		# section data comes after the program header by default.  The
806		# section header table is placed after all section data.
807
808		if self.e_phnum > 0:
809			offset = self.e_phoff + self.e_phnum * self.e_phentsize
810		else:
811			offset = self.e_ehsize
812		offset = elf.elf_sections.layout(offset, elf)
813		if self.e_shoff is None:
814			if self.nsections > 0:
815				self.e_shoff = (offset + (alignment-1)) & \
816				       ~(alignment-1)
817			else:
818				self.e_shoff = 0
819
820		if self.nsections >= SHN_LORESERVE:
821			elf.elf_sections.set_extended_shnum(self.nsections)
822			self.e_shnum = 0
823		if self.e_shstrndx >= SHN_XINDEX:
824			elf.elf_sections.set_extended_shstrndx(self.e_shstrndx)
825			self.e_shstrndx = SHN_XINDEX
826
827	def bits(self, formatchar, elfclass):
828		"""Return the file representation of an Elf Ehdr."""
829
830		s = self.e_ident.bits(formatchar, elfclass)
831		s += ElfType.bits(self, formatchar, elfclass)
832
833		return s
834
835
836class ElfLong:
837	"""Wrapper around a python Int/Long."""
838
839	def __init__(self, v):
840		self._v = long(v)
841
842	def bits(self, formatchar, elfclass):
843		"""Return the file representation for this object.
844
845		Depending on the number of bits needed to represent
846		the number, the returned bits would be either 4 or
847		8 bytes wide.
848		"""
849
850		if self._v > 0xFFFFFFFFL:
851			f = formatchar + "Q"
852		else:
853			f = formatchar + "I"
854		return struct.pack(f, self._v)
855
856
857class ElfMove(ElfType):
858	"""A representation of an Elf Move type.
859
860	YAML tag: !Move
861	"""
862
863	fields = [
864		('m_value', do_long, "I", "I"),
865		('m_info', do_long, "I", "Q"),
866		('m_poffset', do_long, "I", "Q"),
867		('m_repeat', do_long, "H", "H"),
868		('m_stride', do_long, "H", "H")
869		]
870
871	def __init__(self, move, node):
872		ElfType.__init__(self, move, node)
873
874
875class ElfNote(ElfType):
876	"""A representation of an Elf Note type.
877
878	YAML tag: !Note
879
880	The data in the note is held in YAML node named 'n_data' which is
881	a pair of strings, one for the note's name field and one for the
882	description.
883
884	If the fields 'n_namesz' and 'n_descz' aren't specified, they
885	are computed from the contents of 'n_data'.
886	"""
887
888	fields = [
889		('n_namesz', do_long, "I", "I"),
890		('n_descsz', do_long, "I", "I"),
891		('n_type', do_long, "I", "I"),
892		('n_data', do_copy, "", "")
893		]
894
895	def __init__(self, note, node):
896		ElfType.__init__(self, note, node)
897		self._note = note
898
899	def layout(self, offset, elfclass):
900		if len(self.n_data) != 2:
901			raise ElfError(node, "Note data not a pair of strings.")
902
903		for nd in self.n_data:
904			if isinstance(nd, ElfType):
905				nd.layout(offset, elfclass)
906
907		if self.n_namesz is None:
908			self.n_namesz = len(self.n_data[0])
909		if self.n_descsz is None:
910			self.n_descsz = len(self.n_data[1])
911
912	def bits(self, format, elfclass):
913		b = ElfType.bits(self, format, elfclass)
914		nbits = str(self.n_data[0])
915		dbits = str(self.n_data[1])
916		return b + nbits + dbits
917
918
919class ElfPhdr(ElfType):
920	"""A representation of an ELF Program Header Table entry.
921
922	YAML tag: !Phdr
923	"""
924
925	fields = [			# NOTE: class-dependent field ordering
926		('p_align',	do_long),
927		('p_filesz',	do_long),
928		('p_flags' ,	do_flags(elf_ph_flags), ),
929		('p_memsz' ,	do_long),
930		('p_offset',	do_long),
931		('p_paddr' ,	do_long),
932		('p_type'  ,	do_encode(elf_ph_type)),
933		('p_vaddr' ,	do_long)
934		]
935
936	def __init__(self, ph, node):
937		ElfType.__init__(self, ph, node)
938
939	def to_string(self):
940		"""Helper during debugging."""
941
942		s = "Phdr(type:%(p_type)d,flags:%(p_flags)d," \
943			"offset:%(p_offset)ld,vaddr:%(p_vaddr)ld," \
944			"paddr:%(p_paddr)ld,filesz:%(p_filesz)ld," \
945			"memsz:%(p_memsz)ld)" % self
946		return s
947
948	def bits(self, formatchar, elfclass):
949		"""Return the file representation of a Phdr."""
950
951		f = formatchar
952		# Phdr structures are laid out in a class-dependent way
953		if elfclass == ELFCLASS32:
954			f += "IIIIIIII"
955			s = struct.pack(f,
956					self.p_type,
957					self.p_offset,
958					self.p_vaddr,
959					self.p_paddr,
960					self.p_filesz,
961					self.p_memsz,
962					self.p_flags,
963					self.p_align)
964		else:
965			f += "IIQQQQQQ"
966			s = struct.pack(f,
967					self.p_type,
968					self.p_flags,
969					self.p_offset,
970					self.p_vaddr,
971					self.p_paddr,
972					self.p_filesz,
973					self.p_memsz,
974					self.p_align)
975		return s
976
977class ElfRel(ElfType):
978	"""A representation of an ELF Rel type.
979
980	YAML tag: !Rel
981	"""
982
983	fields = [
984		('r_offset', do_long, "I", "Q"),
985		('r_info', do_long, "I", "Q")
986		]
987
988	def __init__(self, rel, node):
989		ElfType.__init__(self, rel, node)
990
991
992class ElfRela(ElfType):
993	"""A representation of an ELF Rela type.
994
995	YAML tag: !Rela
996	"""
997
998	fields = [
999		('r_offset', do_long, "I", "Q"),
1000		('r_info', do_long, "I", "Q"),
1001		('r_addend', do_long, "I", "Q")
1002		]
1003
1004	def __init__(self, rela, node):
1005		ElfType.__init__(self, rela, node)
1006
1007
1008class ElfSection(ElfType):
1009	"""A representation of an ELF Section.
1010
1011	YAML tag: !Section
1012
1013	A section description consists of the fields that make up an ELF
1014	section header entry and an additional field 'sh_data' that
1015	contains the data associated with this section.
1016
1017	'sh_data' may be a YAML string, or a YAML list of items that
1018	comprise the content of the section.
1019	"""
1020
1021	fields = [
1022		('sh_name', do_string,	"I", "I"),
1023		('sh_type', do_encode(elf_sh_type), "I", "I"),
1024		('sh_flags', do_flags(elf_sh_flags), "I", "Q"),
1025		('sh_addr', do_long,	"I", "Q"),
1026		('sh_offset', do_long, "I", "Q"),
1027		('sh_size', do_long,	"I", "Q"),
1028		('sh_link', do_long,	"I", "I"),
1029		('sh_info', do_long,	"I", "I"),
1030		('sh_addralign', do_copy, "I", "Q"),
1031		('sh_entsize', do_long, "I", "Q"),
1032		('sh_data', do_copy, "", ""),
1033		('sh_index', do_long, "", "")
1034		]
1035
1036	def __init__(self, shdr, node):
1037		"""Initialize a section descriptor."""
1038
1039		ElfType.__init__(self, shdr, node)
1040		if type(self.sh_data) != types.ListType:
1041			self.sh_data = list(self.sh_data)
1042		if self.sh_addralign is None:
1043			if self.sh_type == SHT_NULL or self.sh_type == SHT_NOBITS:
1044				self.sh_addralign = 0
1045			else:
1046				self.sh_addralign = 1
1047		else:
1048			if (self.sh_addralign == 0 or \
1049				(self.sh_addralign & (self.sh_addralign - 1)) != 0):
1050				raise ElfError(node,
1051							   "'sh_addralign' not a power of two.")
1052		self._data = None	# 'cache' of translated data
1053		self._strtab = None
1054
1055	def to_string(self):
1056		"""Helper function during debugging."""
1057
1058		return "Section(name:%(sh_name)s,type:%(sh_type)d," \
1059		       "flags:%(sh_flags)x,addr:%(sh_addr)d,"\
1060		       "offset:%(sh_offset)d,size:%(sh_size)d," \
1061		       "link:%(sh_link)d,info:%(sh_info)d," \
1062		       "addralign:%(sh_addralign)d,entsize:%(sh_entsize)d)" % \
1063		       self
1064
1065	def make_strtab(self):
1066		"""Create a string table from section contents."""
1067
1068		self._strtab = ElfStrTab(self.sh_data)
1069
1070	def string_to_index(self, name):
1071		"""Convert 'name' to an offset inside a string table.
1072
1073		Only valid for sections of type SHT_STRTAB."""
1074
1075		if self._strtab:
1076			return self._strtab.lookup(name)
1077		raise ElfError(None, 'Cannot translate "%s" to an index.' % name)
1078
1079	def bits(self, formatchar, elfclass):
1080		raise AssertionError, "Section objects should use " \
1081		      "databits() or headerbits()"
1082
1083	def layout(self, offset, elf):
1084		"""Prepare an ELF section for output."""
1085
1086		if type(self.sh_name) == types.StringType:
1087			# first try convert it to a long
1088			try:
1089				self.sh_name = long(self.sh_name)
1090			except ValueError: # lookup in string table
1091				try:
1092					self.sh_name = \
1093								 elf.section_name_index(self.sh_name)
1094				except KeyError:
1095					raise ElfError(self._n,
1096								   "Section name '%s' not in string table." % \
1097								   self.sh_name)
1098		# give a chance for the contents of a section to xlate strings
1099		for d in self.sh_data:
1100			if isinstance(d, ElfType):
1101				d.layout(offset, elf)
1102		# compute the space used by the section data
1103		self._data = self.databits(elf.formatchar(), elf.elfclass())
1104
1105		align = self.sh_addralign
1106		if align == 0:
1107			align = 1
1108		if self.sh_type == SHT_NULL or self.sh_type == SHT_NOBITS:
1109			isnulltype = 1
1110		else:
1111			isnulltype = 0
1112
1113		offset = (offset + (align - 1)) & ~(align - 1)
1114		if self.sh_size is None:
1115			if isnulltype:
1116				self.sh_size = 0
1117			else:
1118				self.sh_size = len(self._data)
1119		if self.sh_offset is None:
1120			if isnulltype:
1121				self.sh_offset = 0
1122			else:
1123				self.sh_offset = offset
1124		if isnulltype:					# ignore bits for null types
1125			return offset
1126		return offset + len(self._data)
1127
1128	def databits(self, formatchar, elfclass):
1129		"""Return the contents of a section."""
1130
1131		if self._data:
1132			return self._data
1133		# special-case string table handling
1134		if self.sh_type == SHT_STRTAB:
1135			return self._strtab.bits()
1136		# 'normal' section
1137		s = ""
1138		for d in self.sh_data:
1139			if isinstance(d, ElfType):
1140				s += d.bits(formatchar, elfclass)
1141			elif isinstance(d, types.LongType):
1142				s += struct.pack(formatchar + "Q", d)
1143			elif isinstance(d, types.IntType):
1144				s += struct.pack(formatchar + "I", d)
1145			else:
1146				s += str(d)
1147		return s
1148
1149	def headerbits(self, formatchar, elfclass):
1150		"""Return the file representation of the section header."""
1151
1152		return ElfType.bits(self, formatchar, elfclass)
1153
1154
1155class ElfSym(ElfType):
1156	"""A representation for an ELF Symbol type.
1157
1158	YAML tag: !Sym
1159	"""
1160
1161	fields = [			# NOTE: class-dependent layout.
1162		('st_info', do_long, "B", "B"),
1163		('st_name', do_string, "I", "I"),
1164		('st_other', do_long, "B", "B"),
1165		('st_shndx', do_string, "H", "H"),
1166		('st_size', do_long, "I", "Q"),
1167		('st_value', do_long, "I", "Q")
1168		]
1169
1170	def __init__(self, sym, node):
1171		ElfType.__init__(self, sym, node)
1172
1173	def bits(self, format, elfclass):
1174		"""Return the file representation for an ELF Sym."""
1175
1176		if elfclass == ELFCLASS32:
1177			s = struct.pack(format + "IIIBBH",
1178					self.st_name,
1179					self.st_value,
1180					self.st_size,
1181					self.st_info,
1182					self.st_other,
1183					self.st_shndx)
1184		else:
1185			s = struct.pack(format + "IBBHQQ",
1186					self.st_name,
1187					self.st_info,
1188					self.st_other,
1189					self.st_shndx,
1190					self.st_value,
1191					self.st_size)
1192		return s
1193
1194	def layout(self, offset, elf):
1195		"""Perform layout-time conversions for an ELF Sym.
1196
1197		String valued fields are converted to offsets into
1198		string tables.
1199		"""
1200
1201		if type(self.st_shndx) == types.StringType:
1202			self.st_shndx = \
1203			      elf.elf_sections.get_index(self.st_shndx)
1204			if self.st_shndx is None:
1205				raise ElfError(self._n, "Untranslateable 'st_shndx' " + \
1206					"value \"%s\"." % self.st_shndx)
1207
1208		if type(self.st_name) == types.StringType:
1209			try:
1210				strtab = \
1211				       elf.elf_sections[self.st_shndx]._strtab
1212			except IndexError:
1213				raise ElfError(self._n, "'st_shndx' out of range")
1214			if strtab is None:
1215				raise ElfError(self._n, "'st_shndx' not of type STRTAB.")
1216
1217			try:
1218				self.st_name = strtab.lookup(self.st_name)
1219			except KeyError:
1220				raise ElfError(self._n,
1221							   'unknown string "%s"' % self.st_name)
1222		return offset
1223
1224
1225class ElfSyminfo(ElfType):
1226	"""A representation of an ELF Syminfo type.
1227
1228	YAML tag: !Syminfo
1229	"""
1230
1231	fields = [
1232		('si_boundto', do_encode(elf_syminfo_boundto_types), "H", "H"),
1233		('si_flags', do_flags(elf_syminfo_flags), "H", "H")
1234		]
1235
1236	def __init__(self, syminfo, node):
1237		ElfType.__init__(self, syminfo, node)
1238
1239
1240class ElfVerdaux(ElfType):
1241	"""A representation of an ELF Verdaux type."""
1242
1243	fields = [
1244		('vda_name', do_long, "I", "I"),
1245		('vda_next', do_long, "I", "I")
1246		]
1247
1248	def __init__(self, verdaux, node):
1249		ElfType.__init__(self, verdaux, node)
1250
1251
1252class ElfVerdef(ElfType):
1253	"""A representation of an ELF Verdef type."""
1254
1255	fields = [
1256		('vd_version', do_long, "H", "H"),
1257		('vd_flags', do_long, "H", "H"),
1258		('vd_ndx', do_long, "H", "H"),
1259		('vd_cnt', do_long, "H", "H"),
1260		('vd_hash', do_long, "I", "I"),
1261		('vd_aux', do_long, "I", "I"),
1262		('vd_next', do_long, "I", "I")
1263		]
1264
1265	def __init__(self, verdef, node):
1266		ElfType.__init__(self, verdef, node)
1267
1268
1269class ElfVernaux(ElfType):
1270	"""A representation of an ELF Vernaux type."""
1271
1272	fields = [
1273		('vna_hash', do_long, "I", "I"),
1274		('vna_flags', do_long, "H", "H"),
1275		('vna_other', do_long, "H", "H"),
1276		('vna_name', do_long, "I", "I"),
1277		('vna_next', do_long, "I", "I")
1278		]
1279
1280	def __init__(self, vernaux, node):
1281		ElfType.__init__(self, vernaux, node)
1282
1283class ElfVerneed(ElfType):
1284	"""A representation of an ELF Verneed type."""
1285
1286	fields = [
1287		('vn_version', do_long, "H", "H"),
1288		('vn_cnt', do_long, "H", "H"),
1289		('vn_file', do_long, "I", "I"),
1290		('vn_aux', do_long, "I", "I"),
1291		('vn_next', do_long, "I", "I")
1292		]
1293
1294	def __init__(self, verneed, node):
1295		ElfType.__init__(self, verneed, node)
1296
1297
1298#
1299# Aggregates
1300#
1301
1302class ElfPhdrTable:
1303	"""A representation of an ELF Program Header Table.
1304
1305	A program header table is a list of program header entry sections.
1306	"""
1307
1308	def __init__(self, phdr):
1309		"""Initialize a program header table object.
1310
1311		Argument 'phdr' is a list of parsed ElfPhdr objects.
1312		"""
1313
1314		self.pht_data = []
1315		for ph in phdr:
1316			if type(ph) == types.DictType:
1317				ph = ElfPhdr(ph)
1318			elif not isinstance(ph, ElfPhdr):
1319				raise ElfError(ph.node,
1320					       "Program Header Table "
1321					       "contains non header data.")
1322			self.pht_data.append(ph)
1323
1324	def bits(self, formatchar, elfclass):
1325		"""Return the file representation of the Phdr table."""
1326
1327		s = ""
1328		for d in self.pht_data:
1329			s += d.bits(formatchar, elfclass)
1330		return s
1331
1332	def __len__(self):
1333		"""Return the number of program header table entries."""
1334
1335		return len(self.pht_data)
1336
1337	def __iter__(self):
1338		"""Return an iterator for traversing Phdr entries."""
1339
1340		return self.pht_data.__iter__()
1341
1342
1343class ElfSectionList:
1344	"""A list of ELF sections."""
1345
1346	def __init__(self, shlist):
1347		"""Initialize an ELF section list.
1348
1349		Argument 'shlist' is a list of parser ElfSection
1350		objects.
1351		"""
1352
1353		self.shl_sections = shlist
1354		self.shl_sectionnames = []
1355		self.shl_nentries = len(shlist)
1356
1357		for sh in shlist:
1358			if not isinstance(sh, ElfSection):
1359				raise ElfError(None,
1360					       """Section 'sections' contains
1361					       unrecognized data.""")
1362			if sh.sh_index is not None:
1363				if self.shl_nentries <= sh.sh_index:
1364					self.shl_nentries = sh.sh_index + 1
1365			self.shl_sectionnames.append((sh.sh_name, sh.sh_index))
1366			if sh.sh_type == SHT_STRTAB: # a string table
1367				sh.make_strtab()
1368
1369	def __len__(self):
1370		"""Return the number of ELF sections."""
1371
1372		return len(self.shl_sections)
1373
1374	def __iter__(self):
1375		"""Iterate through ELF sections."""
1376
1377		return self.shl_sections.__iter__()
1378
1379	def __getitem__(self, ind):
1380		"""Retrieve the ELF section at index 'ind'."""
1381
1382		try:
1383			return self.shl_sections[ind]
1384		except IndexError:
1385			for sh in self.shl_sections:
1386				if sh.sh_index == ind:
1387					return sh
1388			raise IndexError, "no section at index %d" % ind
1389
1390	def layout(self, offset, elf):
1391		"""Compute the layout for section."""
1392
1393		if len(self.shl_sections) == 0:
1394			return 0
1395		for sh in self.shl_sections: # layout sections
1396			offset = sh.layout(offset, elf)
1397		return offset
1398
1399	def get_index(self, name):
1400		"""Return the section index for section 'name', or 'None'."""
1401
1402		c = 0
1403		for (n,i) in self.shl_sectionnames:
1404			if n == name:
1405				if i is None:
1406					return c
1407				else:
1408					return i
1409			c += 1
1410		return None
1411
1412	def get_shnum(self):
1413		"""Retrieve the number of sections in this container."""
1414
1415		return self.shl_nentries
1416
1417	def set_extended_shnum(self, shnum):
1418		"""Set the extended section number."""
1419
1420		sh = self.shl_sections[0]
1421		sh.sh_size = shnum
1422
1423	def set_extended_shstrndx(self, strndx):
1424		"""Set the extended string table index."""
1425
1426		sh = self.shl_sections[0]
1427		sh.sh_link = strndx
1428
1429class Elf:
1430	"""A representation of an ELF object."""
1431
1432	def __init__(self, yamldict, ehdr, phdrtab, sections):
1433		self._d = yamldict
1434		self._n = None
1435		self.elf_ehdr = ehdr
1436		self.elf_phdrtab = phdrtab
1437		self.elf_sections = sections
1438		self.elf_fillchar = long(get(yamldict, 'elf_fillchar',
1439					     defaults['elf_fillchar']))
1440	def byteorder(self):
1441		"""Return the byteorder for this ELF object."""
1442		return self.elf_ehdr.e_ident.ei_data
1443
1444	def elfclass(self):
1445		"""Return the ELF class for this ELF object."""
1446		return self.elf_ehdr.e_ident.ei_class
1447
1448	def formatchar(self):
1449		"""Return the format character corresponding to the ELF
1450		byteorder."""
1451
1452		if self.byteorder() == ELFCLASS32:
1453			return "<"
1454		else:
1455			return ">"
1456
1457	def layout(self):
1458		"""Compute a file layout for this ELF object and update
1459		internal data structures."""
1460
1461		self.elf_ehdr.layout(0, self)
1462
1463
1464	def section_name_index(self, name):
1465		"""Compute index of section 'name' in the section name string table."""
1466
1467		strndx = self.elf_ehdr.e_shstrndx
1468		if strndx is None:
1469			return None
1470		return self.elf_sections[strndx].string_to_index(name)
1471
1472	def write(self, fn):
1473		"""Write out the file representation of an ELF object.
1474
1475		Argument 'fn' denotes the destination."""
1476
1477		of = file(fn, 'w')
1478
1479		formatchar = self.formatchar()
1480		elfclass = self.elfclass()
1481
1482		# Write out the header
1483		of.write(self.elf_ehdr.bits(formatchar, elfclass))
1484
1485		# Write out the program header table if present
1486		if self.elf_phdrtab:
1487			self.reposition(of, self.elf_ehdr.e_phoff)
1488			for ph in self.elf_phdrtab:
1489				of.write(ph.bits(formatchar, elfclass))
1490		# Write out the sections
1491		if self.elf_sections:
1492			# First the contents of the sections
1493			for sh in self.elf_sections:
1494				if sh.sh_type == SHT_NULL or sh.sh_type == SHT_NOBITS:
1495					continue
1496				self.reposition(of, sh.sh_offset)
1497				of.write(sh.databits(formatchar, elfclass))
1498			# Then the header table
1499			self.reposition(of, self.elf_ehdr.e_shoff)
1500			for sh in self.elf_sections:
1501				if sh.sh_index:
1502					new_offset = sh.sh_index * self.elf_ehdr.e_shentsize + \
1503						self.elf_ehdr.e_shoff
1504					self.reposition(of, new_offset)
1505				of.write(sh.headerbits(formatchar, elfclass))
1506		of.close()
1507
1508	def reposition(self, f, offset):
1509		"""Reposition file `f' to offset `offset', filling gaps with
1510		the configured fill character as needed."""
1511
1512		pos = f.tell()
1513		if offset == pos:
1514			return
1515		if offset < pos or (offset > pos and self.elf_fillchar == 0):
1516			f.seek(offset, 0)
1517			return
1518		s = ("%c" % self.elf_fillchar) * (offset - pos)
1519		f.write(s)
1520
1521
1522#
1523# YAML Parser configuration and helpers.
1524#
1525
1526yaml_tags = [
1527	(u'!Cap', ElfCap),
1528	(u'!Dyn', ElfDyn),
1529	(u'!Ehdr', ElfEhdr),
1530	(u'!Ident', ElfEhdrIdent),
1531	(u'!Move', ElfMove),
1532	(u'!Note', ElfNote),
1533	(u'!Phdr', ElfPhdr),
1534	(u'!Rel', ElfRel),
1535	(u'!Rela', ElfRela),
1536	(u'!Section', ElfSection),
1537	(u'!Sym', ElfSym),
1538	(u'!Syminfo', ElfSyminfo),
1539	(u'!Verdaux', ElfVerdaux),
1540	(u'!Verdef', ElfVerdef),
1541	(u'!Vernaux', ElfVernaux),
1542	(u'!Verneed', ElfVerneed) ]
1543
1544def init_parser():
1545	for t in yaml_tags:
1546		yaml.add_constructor(t[0],	# lamdba: loader, node, class
1547			lambda l, n, c=t[1]: \
1548				c(l.construct_mapping(n, deep=True), n))
1549
1550def make_elf(yd):
1551	"""Convert a YAML description `yd' of an ELF file into an
1552	ELF object."""
1553
1554	try:
1555		eh = yd['ehdr']
1556	except KeyError:
1557		eh = ElfEhdr({}, None)
1558
1559	phdrtab = ElfPhdrTable(get(yd, 'phdrtab', {}))
1560	sectionlist = ElfSectionList(get(yd, 'sections', {}))
1561
1562	return Elf(yd, eh, phdrtab, sectionlist)
1563
1564
1565#
1566# MAIN
1567#
1568
1569if __name__ == '__main__':
1570	parser = optparse.OptionParser(usage=usage, version=version,
1571				       description=description)
1572	parser.add_option("-o", "--output", dest="output",
1573			  help="write output to FILE [default: %default]",
1574			  metavar="FILE", default="a.out")
1575	parser.add_option("-N", "--no-shstrtab", dest="do_shstrtab",
1576			  help="do not create a string table section for "
1577			  "section names if missing", action="store_false",
1578			  metavar="BOOLEAN", default=True)
1579	parser.add_option("-U", "--no-shnundef", dest="do_shnundef",
1580			  help="do not create a section header for index "
1581			  "SHN_UNDEF if missing", action="store_false",
1582			  metavar="BOOLEAN", default=True)
1583
1584	(options, args) = parser.parse_args()
1585
1586	if len(args) > 1:
1587		parser.error("only one input-file must be specified")
1588
1589	try:
1590		if args:
1591			stream = file(args[0], 'r')
1592		else:
1593			stream = sys.stdin
1594	except IOError, x:
1595		parser.error("cannot open stream: %s" % x)
1596
1597	init_parser()
1598
1599	try:
1600		elf = make_elf(yaml.load(stream))
1601		elf.layout()
1602		elf.write(options.output)
1603	except yaml.YAMLError, x:
1604		parser.error("cannot parse stream: %s" % x)
1605	except ElfError, msg:
1606		print msg
1607		sys.exit(1)
1608
1609
1610
1611# Local Variables:
1612# mode: python
1613# tab-width: 4
1614# py-indent-offset: 4
1615# End:
1616