Initial commit

This commit is contained in:
Bahadir Balban
2008-01-13 13:53:52 +00:00
commit e2b791a3d8
789 changed files with 95825 additions and 0 deletions

0
tools/pyelf/__init__.py Normal file
View File

327
tools/pyelf/aistruct.py Normal file
View File

@@ -0,0 +1,327 @@
import mmap
import struct
import os
class GFile(object):
def __init__(self, filename, size):
handle = open(filename, 'rb+')
self.mapping = mmap.mmap(handle.fileno(), size)
def set_byte_ordering(self, ordering):
self.byte_ordering = ordering
def get_byte_ordering(self):
return self.byte_ordering
def create_new(filename, size):
assert size < 10240 # This is only for testing, really
handle = open(filename, 'wb')
handle.write('\0' * size)
handle.close()
return GFile(filename, size)
create_new = staticmethod(create_new)
def existing(filename):
assert os.path.exists(filename)
size = os.path.getsize(filename)
return GFile(filename, size)
existing = staticmethod(existing)
class BitPoker(object):
""" """
SIZE_MAP = {1: 'B',
2: 'H',
4: 'I',
8: 'Q'}
def set_mmapfile(self, mmapfile, offset_bytes = 0):
self.mmapfile = mmapfile
self.master_offset_bytes = offset_bytes
def set_byte_ordering(self, byte_ordering):
self.byte_ordering = byte_ordering
self.struct_byte_ordering = {'lsb': '<', 'msb': '>'}[byte_ordering]
def get_byte_ordering(self):
return self.byte_ordering
def read_value(self, numbytes, offset_bytes):
# Seek to the right spot using absolute (whence=0) positioning
self.mmapfile.seek(offset_bytes + self.master_offset_bytes, 0)
data = self.mmapfile.read(numbytes)
return struct.unpack('%s%s' % (self.struct_byte_ordering,
self.SIZE_MAP[numbytes]), data)
def write_value_sized(self, value, numbytes, offset_bytes):
data = struct.pack('%s%s' % (self.struct_byte_ordering,
self.SIZE_MAP[numbytes]), value)
self.mmapfile.seek(offset_bytes + self.master_offset_bytes, 0)
self.mmapfile.write(data)
def read_value_sized(self, numbytes, offset_bytes):
self.mmapfile.seek(offset_bytes + self.master_offset_bytes, 0)
raw_data = self.mmapfile.read(numbytes)
data = struct.unpack('%s%s' % (self.struct_byte_ordering,
self.SIZE_MAP[numbytes]), raw_data)
return data[0]
def read_c_string(self, offset_bytes):
self.mmapfile.seek(offset_bytes + self.master_offset_bytes.get(), 0)
result = ''
c = self.mmapfile.read_byte()
while c != '\0':
result += c
c = self.mmapfile.read_byte()
return result
def new_with_gfile(gfile, offset):
poker = BitPoker()
poker.set_mmapfile(gfile.mapping, offset)
poker.set_byte_ordering(gfile.get_byte_ordering())
return poker
new_with_gfile = staticmethod(new_with_gfile)
def new_with_poker(origpoker, offset):
poker = BitPoker()
poker.set_mmapfile(origpoker.mmapfile, origpoker.master_offset_bytes + offset)
poker.set_byte_ordering(origpoker.get_byte_ordering())
return poker
new_with_poker = staticmethod(new_with_poker)
class AIStruct(object):
SIZE32 = 32
SIZE64 = 64
class AIElement(object):
_natural_size = None
def __init__(self, word_size_in_bits, offset, names, format):
self.offset = offset #offset into memory-mapped file.
self.set_target_word_size(word_size_in_bits)
self.value = 0
self.names = names
self.format = format
def set_target_word_size(self, target_word_size):
self.target_word_size = target_word_size
def get_size_bits(self):
return self._natural_size
def set_args(self):
pass
def set(self, value):
if type(value) is str and len(value) == 1:
value = ord(value)
self.value = value
def get(self):
return self.value
def write(self, bitpoker):
bitpoker.write_value_sized(self.value, self.get_size_bits() / 8,
self.offset)
def read(self, bitpoker):
self.value = bitpoker.read_value_sized(self.get_size_bits() / 8, self.offset)
def __cmp__(self, value):
return cmp(self.value, value)
def __add__(self, other):
if isinstance(other, AIStruct.AIElement):
return self.value + other.value
else:
return self.value + other
def __mul__(self, other):
if isinstance(other, AIStruct.AIElement):
return self.value * other.value
else:
return self.value * other
def __str__(self):
if self.format:
if type(self.format) == type(""):
return self.format % self.value
else:
return self.format(self.value)
if self.names:
return self.names.get(self.value, str(self.value))
else:
return str(self.value)
class WORD(AIElement):
def get_size_bits(self):
return self.target_word_size
class INT8(AIElement):
_natural_size = 8
UINT8 = INT8
class INT16(AIElement):
_natural_size = 16
UINT16 = INT16
class INT32(AIElement):
_natural_size = 32
UINT32 = INT32
class INT64(AIElement):
_natural_size = 64
UINT64 = INT64
class BITFIELD(AIElement):
class AttributeBasedProperty(object):
def __init__(self, bitfield_inst):
self.bitfield_inst = bitfield_inst
def get_length_and_offset_for_key(self, key):
offset = 0
length = 0
for name, sizes_dict in self.bitfield_inst.components:
if name == key:
length = sizes_dict[self.bitfield_inst.target_word_size]
break
offset += sizes_dict[self.bitfield_inst.target_word_size]
if length == 0:
raise AttributeError(key)
return length, offset
def __getitem__(self, key):
length, offset = self.get_length_and_offset_for_key(key)
# We have the position of the desired key in value.
# Retrieve it by right-shifting by "offset" and
# then masking away anything > length.
result = self.bitfield_inst.value >> offset
result &= ( (1<<length) - 1)
if self.bitfield_inst.post_get is not None:
return self.bitfield_inst.post_get(key, result)
else:
return result
def __setitem__(self, key, new_value):
if self.bitfield_inst.pre_set is not None:
new_value = self.bitfield_inst.pre_set(key, new_value)
length, offset = self.get_length_and_offset_for_key(key)
assert new_value < (1 << length)
# Generate the result in three stages
# ... insert the new value:
result = new_value << offset
# ... | the stuff above the value:
result |= ( self.bitfield_inst.value >> (offset + length) << (offset + length) )
# ... | the stuff below the value.
result |= ( self.bitfield_inst.value & ( (1 << offset) - 1 ) )
self.bitfield_inst.value = result
def __init__(self, *args, **kwargs):
super(AIStruct.BITFIELD, self).__init__(*args, **kwargs)
self.components = []
self.attribute_based_property = AIStruct.BITFIELD.AttributeBasedProperty(self)
def set_args(self, components=None, post_get=None, pre_set=None):
if components is not None:
self.components = components
self.post_get = post_get
self.pre_set = pre_set
def get_size_bits(self):
size_bits = 0
for comp_name, comp_sizes in self.components:
size_bits += comp_sizes[self.target_word_size]
return size_bits
def get(self):
return self.attribute_based_property
def set(self, value):
raise AttributeError("set() shouldn't be called here!")
def __init__(self, word_size_in_bits):
self.word_size_in_bits = word_size_in_bits
self.thestruct = []
def _setup_attributes(self, allprops):
# So Python sucks and you can only set properties on class objects,
# not class instance objects. Hence this crud...
class AI(object):
pass
for key in allprops:
setattr(AI, key, allprops[key])
self.ai = AI()
def _setup_one(self, etypename, ename, args, offset, names, format):
elementclass = getattr(self, etypename)
elementinst = elementclass(self.word_size_in_bits, offset, names, format)
elementinst.set_args(**args)
self.thestruct.append(elementinst)
def get_item(obj):
return elementinst
def set_item(obj, value):
return elementinst.set(value)
newprop = {'%s' % (ename): property(get_item, set_item)}
return (elementinst.get_size_bits() / 8, newprop)
def _setup_multiprop(self, ename, times):
def mp_get(obj):
return [getattr(self.ai, '%s_%s' % (ename, counter)) for counter in range(1, times+1)]
def mp_set(obj, value):
for num, item in enumerate(value):
setattr(self.ai, '%s_%s' % (ename, num + 1), item)
return {'%s' % (ename): property(mp_get, mp_set)}
def setup(self, *elements):
offset = 0
allprops = {}
for element in elements:
etypename, ename = element[:2]
args = {}
if len(element) == 3:
args = element[2]
times = args.get('times', None)
names = args.get('names', None)
format = args.get('format', None)
if names is not None:
del args['names']
if format is not None:
del args['format']
if times is not None:
del args['times']
for time in range(times):
size, propdict = self._setup_one(etypename, '%s_%s' % (ename, time + 1), args, offset, names, format)
offset += size
allprops.update(propdict)
allprops.update(self._setup_multiprop(ename, times))
else:
size, propdict = self._setup_one(etypename, ename, args, offset, names, format)
offset += size
allprops.update(propdict)
self._setup_attributes(allprops)
def struct_size(self):
size = 0
for element in self.thestruct:
size += element.get_size_bits()
return size / 8
def set_poker(self, poker):
self.poker = poker
def write(self):
for element in self.thestruct:
element.write(self.poker)
def read_from_poker(self, poker):
self.set_poker(poker)
for element in self.thestruct:
element.read(self.poker)
def write_new(self, filename):
"""convenience function to create a new file and store the KCP in it. """
newfile = GFile.create_new(filename, self.struct_size())
poker = BitPoker('lsb') #FIXME
poker.set_mmapfile(newfile.mapping, offset_bytes = 0)
self.set_poker(poker)
self.write()

BIN
tools/pyelf/aistruct.pyc Normal file

Binary file not shown.

84
tools/pyelf/burn.py Normal file
View File

@@ -0,0 +1,84 @@
from aistruct import AIStruct
import elf, sys
from optparse import OptionParser
class AfterBurner(AIStruct):
def __init__(self, *args, **kwargs):
AIStruct.__init__(self, AIStruct.SIZE32)
self.setup(
('UINT32', 'addr')
)
def __str__(self):
return "0x%x" % self.ai.addr.get()
def arch_perms(rwx):
# ia32 doesn't support read-noexec
if rwx & (1 << 2):
rwx |= 1
return rwx
def align_up(value, align):
mod = value % align
if mod != 0:
value += (align - mod)
return value
def gen_pheaders(elf):
old_rwx = 0
old_offset = 0
old_addr = 0
old_bits = 0
old_size = 0
new_addr = 0
new_offset = 0
new_size = 0
for section in [section for section in elf.sheaders if section.allocable()]:
# Test - can we add this section to the current program header?
new = 0
rwx = arch_perms(section.get_perms())
addr = section.ai.sh_addr.get()
offset = section.ai.sh_offset.get()
al = section.ai.sh_addralign.get()
size = section.ai.sh_size.get()
if old_rwx != rwx:
new = 1
if addr != align_up(old_size + old_addr, al):
new = 2
if offset != align_up(old_size + old_offset, al):
new = 3
if new != 0:
#print hex(new_offset), hex(new_addr), hex(new_size)
new_size = size
new_addr = addr
new_offset = offset
else:
new_size = (addr + size) - new_addr
old_rwx = rwx
old_size = size
old_bits = 0
old_offset = offset
old_addr = addr
#print section.ai.sh_name, section.ai.sh_addr, section.ai.sh_offset, section.ai.sh_size, section.ai.sh_flags, rwx
print hex(new_offset), hex(new_addr), hex(new_size)
def main():
wedge = elf.ElfFile.from_file(sys.argv[1])
guest = elf.ElfFile.from_file(sys.argv[2])
print wedge.pheaders
for section in wedge.sheaders:
print section.name
section.name += ".linux"
print section.name
#del wedge.pheaders[:]
#print wedge.pheaders
wedge.write_file("foobar")
gen_pheaders(wedge)
if __name__ == "__main__":
main()

500
tools/pyelf/elf.py Normal file
View File

@@ -0,0 +1,500 @@
from aistruct import GFile, BitPoker, AIStruct
import StringIO
import sys
class Elf32Header(AIStruct):
EI_MAGIC = [0x7f, ord('E'), ord('L'), ord('F')]
EI_CLASS_32 = 1
EI_CLASS_64 = 2
EI_DATA_LSB = 1
EI_DATA_MSB = 2
EI_TYPE_NONE = 0
EI_TYPE_REL = 1
EI_TYPE_EXEC = 2
EI_TYPE_DYN = 3
EI_TYPE_CORE = 4
EI_TYPE_NUM = 5
EI_TYPE_LOPROC = 0xff00
EI_TYPE_HIPROC = 0xffff
MACHINE_NONE = 0
MACHINE_SPARC = 2
MACHINE_386 = 3
MACHINE_MIPS = 8
MACHINE_MIPS_RS4_BE = 10
MACHINE_SPARC32PLUS = 18
MACHINE_ARM = 40
MACHINE_FAKE_ALPHA = 41
MACHINE_SPARCV9 = 43
MACHINE_IA_64 = 50
MACHINE_ALPHA = 0x9026
VERSION_NONE = 0
VERSION_CURRENT = 1
def __init__(self):
AIStruct.__init__(self, AIStruct.SIZE32)
self.setup(
('UINT8', 'ei_magic', {'times': 4}),
('UINT8', 'ei_class', {'names' : { 1: "ELF32", 2: "ELF64" }} ),
('UINT8', 'ei_data', {'names' : { 1 : "2's complement, little endian", 2: "2's complement, big endian" }}),
('UINT8', 'ei_version', {'names' : { 1 : "1 (current)" }}),
('UINT8', 'ei_osabi', { 'names' : { 0 : "UNIX - System V", 1 : "HP-UX Operating System", 255 : "Standalone application"}}),
('UINT8', 'ei_abiversion'),
('UINT8', 'ei_padding', {'times': 7}),
('UINT16', 'e_type', { 'names' : { 2 : "EXEC (Executable file)" }} ),
('UINT16', 'e_machine', { 'names' : { 3 : "Intel 80836" } }),
('UINT32', 'e_version', {'format': "0x%x" }),
('UINT32', 'e_entry', {'format': "0x%x" }),
('UINT32', 'e_phoff', { 'format' : "%d (bytes into file)" }),
('UINT32', 'e_shoff', { 'format' : "%d (bytes into file)" }),
('UINT32', 'e_flags', {'format': "0x%x" }),
('UINT16', 'e_ehsize', { 'format' : "%d (bytes)" } ),
('UINT16', 'e_phentsize', { 'format' : "%d (bytes)" } ),
('UINT16', 'e_phnum'),
('UINT16', 'e_shentsize', { 'format' : "%d (bytes)" } ),
('UINT16', 'e_shnum'),
('UINT16', 'e_shstrndx')
)
def __str__(self):
f = StringIO.StringIO()
f.write("ELF Header:\n")
for name, attr in [("Class", "ei_class"),
("Data", "ei_data"),
("Version", "ei_version"),
("OS/ABI", "ei_osabi"),
("ABI Version", "ei_abiversion"),
("Type", "e_type"),
("Machine", "e_machine"),
("Version", "e_version"),
("Entry point address", "e_entry"),
("Start of program headers", "e_phoff"),
("Start of section headers", "e_shoff"),
("Flags", "e_flags"),
("Size of this header", "e_ehsize"),
("Size of program headers", "e_phentsize"),
("Number of program headers", "e_phnum"),
("Size of section headers", "e_shentsize"),
("Number of section headers", "e_shnum"),
("Section header string table index", "e_shstrndx"),
]:
f.write(" %-35s%s\n" % ("%s:" % name, getattr(self.ai, attr)))
return f.getvalue()
class Elf64Header(Elf32Header):
# Inherit from Elf32Header to get the constants.
def __init__(self):
AIStruct.__init__(self, AIStruct.SIZE64)
self.setup(
('UINT8', 'ei_magic', {'times': 4}),
('UINT8', 'ei_class'),
('UINT8', 'ei_data'),
('UINT8', 'ei_version'),
('UINT8', 'ei_padding', {'times': 9}),
('UINT16', 'e_type'),
('UINT16', 'e_machine'),
('UINT32', 'e_version'),
('UINT64', 'e_entry'),
('UINT64', 'e_phoff'),
('UINT64', 'e_shoff'),
('UINT32', 'e_flags'),
('UINT16', 'e_ehsize'),
('UINT16', 'e_phentsize'),
('UINT16', 'e_phnum'),
('UINT16', 'e_shentsize'),
('UINT16', 'e_shnum'),
('UINT16', 'e_shstrndx')
)
class Elf32SectionHeader(AIStruct):
SHF_WRITE = 1 << 0
SHF_ALLOC = 1 << 1
SHF_EXECINSTR = 1 << 2
def __init__(self, *args, **kwargs):
AIStruct.__init__(self, AIStruct.SIZE32)
self.elffile = kwargs["elffile"]
self.index = kwargs["index"]
def format_flags(x):
if x == (self.SHF_WRITE | self.SHF_ALLOC):
return "WA"
if x == (self.SHF_EXECINSTR | self.SHF_ALLOC):
return "AX"
if x == self.SHF_WRITE:
return "W"
if x == self.SHF_ALLOC:
return "A"
if x == self.SHF_EXECINSTR:
return "X"
return "%x" % x
self.mutated = 0
self._name = None
self.setup(
('UINT32', 'sh_name', {"format" : self.elffile.get_name}),
('UINT32', 'sh_type', {"names" : {0:"NULL", 1:"PROGBITS", 2:"SYMTAB", 3:"STRTAB", 8:"NOBITS"}}),
('UINT32', 'sh_flags', {"format": format_flags}),
('UINT32', 'sh_addr', {"format" : "%08x"}),
('UINT32', 'sh_offset', {"format" : "%06x"}),
('UINT32', 'sh_size', {"format" : "%06x"}),
('UINT32', 'sh_link'),
('UINT32', 'sh_info', {"format" : "%3d"}),
('UINT32', 'sh_addralign', {"format" : "%2d"}),
('UINT32', 'sh_entsize', {"format" : "%02x"}),
)
def get_name(self):
if not self._name:
self._name = self.elffile.get_name(self.ai.sh_name)
return self._name
def set_name(self, value):
self._name = value
name = property(get_name, set_name)
def get_sym_name(self, value):
strtab = ElfFileStringTable \
(self.elffile.gfile, self.elffile.sheaders[self.ai.sh_link.get()])
return strtab.read(value)
def allocable(self):
return self.ai.sh_flags.get() & self.SHF_ALLOC
def writable(self):
return self.ai.sh_flags.get() & self.SHF_WRITE
def executable(self):
return self.ai.sh_flags.get() & self.SHF_EXECINSTR
def get_perms(self):
# Only call on allocatable
assert self.allocable()
rwx = (1 << 2);
if self.writable():
rwx |= (1 << 1)
if self.executable():
rwx |= 1
return rwx
def container(self, cls):
size = cls(section = self).struct_size()
return ElfFileContainer(self.elffile.gfile, AIStruct.SIZE32, self.ai.sh_offset.get(),
size, self.ai.sh_size.get() / size,
cls, section=self)
class Elf64SectionHeader(AIStruct):
def __init__(self, *args, **kwargs):
AIStruct.__init__(self, AIStruct.SIZE64)
self.setup(
('UINT32', 'sh_name'),
('UINT32', 'sh_type'),
('UINT64', 'sh_flags'),
('UINT64', 'sh_addr'),
('UINT64', 'sh_offset'),
('UINT64', 'sh_size'),
('UINT32', 'sh_link'),
('UINT32', 'sh_info'),
('UINT64', 'sh_addralign'),
('UINT64', 'sh_entsize'),
)
class Elf32ProgramHeader(AIStruct):
PT_NULL = 0
PT_NULL = 0
PT_LOAD = 1
PT_DYNAMIC = 2
PT_INTERP = 3
PT_NOTE = 4
PT_SHLIB = 5
PT_PHDR = 6
PT_NUM = 7
PT_LOOS = 0x60000000
PT_HIOS = 0x6fffffff
PT_LOPROC = 0x70000000
PT_HIPROC = 0x7fffffff
PF_X = 1 << 0
PF_W = 1 << 1
PF_R = 1 << 2
PF_MASKPROC = 0xf0000000L
def __init__(self, *args, **kwargs):
AIStruct.__init__(self, AIStruct.SIZE32)
self.elffile = kwargs["elffile"]
def format_flags(x):
the_str = [' ', ' ', ' ']
if (x & self.PF_X):
the_str[2] = 'E'
if (x & self.PF_W):
the_str[1] = 'W'
if (x & self.PF_R):
the_str[0] = 'R'
return "".join(the_str)
self.setup(
('UINT32', 'p_type', {"names" :
{0: "NULL",
1: "LOAD",
2 : "DYNAMIC",
3 : "INTERP",
4 : "NOTE",
1685382481 : "GNU_STACK",
1694766464 : "PAX_FLAGS",
}
} ),
('UINT32', 'p_offset', {"format": "0x%06x"}),
('UINT32', 'p_vaddr', {"format": "0x%08x"}),
('UINT32', 'p_paddr', {"format": "0x%08x"}),
('UINT32', 'p_filesz', {"format": "0x%05x"}),
('UINT32', 'p_memsz', {"format": "0x%05x"}),
('UINT32', 'p_flags', {"format": format_flags}),
('UINT32', 'p_align', {"format": "0x%x"}),
)
class Elf64ProgramHeader(Elf32ProgramHeader):
def __init__(self, *args, **kwargs):
AIStruct.__init__(self, AIStruct.SIZE64)
self.setup(
('UINT32', 'p_type', {"names" : {0: "NULL", 1: "LOAD", 2 : "DYNAMIC", 3 : "INTERP", 4 : "NOTE"}} ),
('UINT32', 'p_flags'),
('UINT64', 'p_offset'),
('UINT64', 'p_vaddr'),
('UINT64', 'p_paddr'),
('UINT64', 'p_filesz'),
('UINT64', 'p_memsz'),
('UINT64', 'p_align'),
)
class ElfFileException(Exception):
pass
class ElfFileNotElfException(ElfFileException):
pass
class ElfFileContainer(object):
def __init__(self, gfile, word_size, offset, entsize, number, cls, **kwargs):
self.gfile = gfile
self.word_size = word_size
self.offset = offset
self.entsize = entsize
self.number = number
self.cls = cls
self.mutated = 0
self.container = []
self.kwargs = kwargs
def mutate(self):
print "Making it mutable"
for each in self:
self.container.append(each)
self.mutated = 1
def __delitem__(self, idx):
if not self.mutated: self.mutate()
self.container.__delitem__(idx)
def __getitem__(self, idx):
if type(idx) == type(""):
# Act like a dictionary
for each in self:
if str(each.ai.sh_name) == idx:
return each
raise "badness"
else:
if self.mutated:
print "mutated", idx, self.container
return self.container[idx]
else:
num = idx
assert num >= 0
if num >= self.number:
raise StopIteration()
inst = self.cls(self.word_size, index=num, **self.kwargs)
poker = BitPoker.new_with_gfile(self.gfile, self.offset + (self.entsize * num))
inst.read_from_poker(poker)
return inst
def __len__(self):
return self.number
class ElfFileProgramHeaderContainer(ElfFileContainer):
def __str__(self):
f = StringIO.StringIO()
f.write("Program Headers:\n")
format_str = " %-15s%-9s%-11s%-11s%-8s%-8s%-4s%s\n"
f.write(format_str % ("Type", "Offset", "VirtAddr",
"PhysAddr", "FileSiz", "MemSiz", "Flg", "Align"))
for header in self:
x = header.ai
f.write(format_str % (x.p_type, x.p_offset, x.p_vaddr,
x.p_paddr, x.p_filesz, x.p_memsz, x.p_flags, x.p_align))
return f.getvalue()
class ElfFileSectionHeaderContainer(ElfFileContainer):
def __str__(self):
f = StringIO.StringIO()
f.write("There are %d section headers, starting at offset 0x%x:\n\n" % (len(self), self.kwargs["elffile"].header.ai.e_shoff.get()))
f.write("Section Headers:\n")
format_str = " [%2s] %-17.17s %-15s %-8s %-6s %-6s %2s %3s %2s %-3s %-2s\n"
f.write(format_str % ("Nr", "Name", "Type", "Addr", "Off", "Size",
"ES", "Flg", "Lk", "Inf", "Al"))
for idx, header in enumerate(self):
x = header.ai
f.write(format_str % (idx, x.sh_name, x.sh_type, x.sh_addr,
x.sh_offset, x.sh_size, x.sh_entsize,
x.sh_flags, x.sh_link, x.sh_info,
x.sh_addralign))
return f.getvalue()
class ElfFileStringTable(object):
def __init__(self, gfile, section_header):
file_offset = section_header.ai.sh_offset
self.poker = BitPoker.new_with_gfile(gfile, file_offset)
def read(self, offset):
return self.poker.read_c_string(offset)
class Symbols(AIStruct):
def __init__(self, *args, **kwargs):
AIStruct.__init__(self, AIStruct.SIZE32)
self.section = kwargs["section"]
self.setup(
('UINT32', 'st_name', {"format" : self.section.get_sym_name}),
('UINT32', 'value'),
('UINT32', 'size'),
('UINT8', 'info'),
('UINT8', 'other'),
('UINT16', 'st_shndx')
)
def __str__(self):
return "[%s] 0x%08x %5d %3d" % \
(self.ai.st_name, self.ai.value.get(),
self.ai.size.get(), self.ai.st_shndx.get())
class ElfFileSymbolTable(object):
def __init__(self, gfile, section_header):
self.header = section_header
file_offset = section_header.ai.sh_offset
self.poker = BitPoker.new_with_gfile(gfile, file_offset)
def get_symbols(self, section_num = None):
if section_num:
return [ sym for sym in self.header.container(Symbols) if sym.ai.st_shndx.get() == section_num]
else:
return self.header.container(Symbols)
class ElfFile(object):
WORD_SIZE_MAP = {Elf32Header.EI_CLASS_32: Elf32Header.SIZE32,
Elf32Header.EI_CLASS_64: Elf32Header.SIZE64}
BYTE_ORDER_MAP = {Elf32Header.EI_DATA_LSB: 'lsb',
Elf32Header.EI_DATA_MSB: 'msb'}
HEADER_MAP = {Elf32Header.SIZE32: Elf32Header, Elf64Header.SIZE64: Elf64Header}
PROGRAM_HEADER_MAP = {Elf32Header.SIZE32: Elf32ProgramHeader, Elf64Header.SIZE64: Elf64ProgramHeader}
SECTION_HEADER_MAP = {Elf32Header.SIZE32: Elf32SectionHeader, Elf64Header.SIZE64: Elf64SectionHeader}
""" Python representation of an Elf file. """
def __init__(self, gfile, byte_ordering, word_size):
self.gfile = gfile
self.gfile.set_byte_ordering(byte_ordering)
self.byte_order = byte_ordering
self.word_size = word_size
self.header = ElfFile.HEADER_MAP[self.word_size]()
self.header.read_from_poker(BitPoker.new_with_gfile(self.gfile, 0))
# Setup the parts of the file
# ... program headers
self.pheaders = ElfFileProgramHeaderContainer \
(gfile, word_size, self.header.ai.e_phoff, self.header.ai.e_phentsize,
self.header.ai.e_phnum.get(), ElfFile.PROGRAM_HEADER_MAP[word_size], elffile=self)
# ... section headers
self.sheaders = ElfFileSectionHeaderContainer \
(gfile, word_size, self.header.ai.e_shoff, self.header.ai.e_shentsize,
self.header.ai.e_shnum.get(), ElfFile.SECTION_HEADER_MAP[word_size], elffile=self)
# ... string table
if self.header.ai.e_shstrndx != 0:
self.string_table = ElfFileStringTable \
(self.gfile, self.sheaders[self.header.ai.e_shstrndx])
# ... symbol table
self.symtable = None
for header in self.sheaders:
if header.get_name() == ".symtab":
self.symtable = ElfFileSymbolTable(self.gfile, header)
def set_source(self, poker):
self.source_poker = poker
def get_name(self, idx):
return self.string_table.read(idx)
def print_info(self):
print "* Information for ELF file:"
print "* Header info:"
self.header.print_info()
def write_file(selfm, filename):
# Write out head
print "Wirintg out", filename
def from_file(filename):
gfile = GFile.existing(filename)
poker = BitPoker()
poker.set_mmapfile(gfile.mapping, 0) # Offset of 0 from start of file.
poker.set_byte_ordering('lsb') # Use default because we don't (yet) know
header = Elf32Header() # Once again, use a default size.
header.read_from_poker(poker)
# Examine the header for info we need.
# Check the magic first. If we don't and the file is non-ELF, chances are
# class & data won't match which will result in a confusing error message
if header.ai.ei_magic != Elf32Header.EI_MAGIC:
raise ElfFileNotElfException("Wanted magic %r, got %r" \
% (Elf32Header.EI_MAGIC, header.ai.ei_magic))
word_size = ElfFile.WORD_SIZE_MAP[header.ai.ei_class.get()]
byte_order = ElfFile.BYTE_ORDER_MAP[header.ai.ei_data.get()]
return ElfFile(gfile, byte_order, word_size)
from_file = staticmethod(from_file)
def test():
"Test suite"
elf_file = ElfFile.from_file("a.out")
# Check can load an elf
success = 1
try:
x = ElfFile.from_file("a.out")
except:
success = 0
assert success
# CHeck can't load not and elf
success = 0
try:
x = ElfFile.from_file("pyelf.py")
except:
success = 1
assert success
def main():
filename = sys.argv[1] # Consider this a usage message :)
elf_file = ElfFile.from_file(filename)
elf_file.print_info()
if __name__ == '__main__':
if sys.argv[1] != "test":
main()
else:
test()

BIN
tools/pyelf/elf.pyc Normal file

Binary file not shown.

77
tools/pyelf/lmamove.py Executable file
View File

@@ -0,0 +1,77 @@
#!/usr/bin/python
import re, elf, sys, os
from optparse import OptionParser
toolprefix = "arm-none-linux-gnueabi-"
objdump = toolprefix + "objdump"
objcopy = toolprefix + "objcopy"
readelf = toolprefix + "readelf"
egrep = "egrep"
snames_file = "section_names.txt"
def get_section_names(path):
rest, filename = os.path.split(path)
sections = []
os.system(readelf + " -l " + path + " > readelf.out")
os.system(egrep + r' "(\.){1}(\w){1}" ' + " readelf.out > sections.out")
for line in file('sections.out'):
secnum, secname = str.split(line)
sections.append((secnum,secname))
print "Sections:\n" + str(sections)
return sections
helpmsg = \
'''Usage:
lmamove.py <filename> <[<op>]offset>
Where <filename> is the file to modify, <offset> is the offset to be added,
subtracted or assigned, depending on the <op>, which can be either + or -
!!! NOTE THIS TOOL IS NOT VERY USEFUL BECAUSE IT TURNS OUT THAT SOME SECTIONS
ARE LIKELY TO BE PUT TOGETHER INTO THE SAME SEGMENT. For example (.text with .rodata)
and (.data with .bss) Therefore moving one sectoin's LMA confuses what to do with the
other section. The segment layout is almost always not same as initial after LMA
modification with objcopy. !!!
'''
def check_args():
if len(sys.argv) < 3:
print helpmsg
sys.exit()
filename = sys.argv[1]
if not os.path.exists(filename):
print "Given path: " + filename + " does not exist.\n"
sys.exit()
def move_sections(path, sections, op, offset):
for secnum, secname in sections:
os.system(objcopy + " --change-section-lma " + secname + op + offset + " " + path)
def lmamove():
'''
Moves an ARM ELF executable's LMA by moving each of its sections by
the given offset. It uses binutils (namely readelf and objcopy) to do
the actual work. Normally objcopy supports a similar operation; moving
of VMA of the whole program by --adjust-vma switch. But it only supports
modifying LMAs by section. Therefore this script extracts all loadable
section names and moves them one by one in order to move the whole program.
'''
check_args()
filename = sys.argv[1]
offset = sys.argv[2]
if offset[0] in "+-":
op = offset[0]
offset = offset[1:]
else:
op = "+"
if offset[:1] == "0x":
offset = int(offset, 16)
section_names = get_section_names(filename)
move_sections(filename, section_names, op, offset)
print("Done.\n")
if __name__ == "__main__":
lmamove()

10
tools/pyelf/package.cfg Normal file
View File

@@ -0,0 +1,10 @@
[info]
Author: Ben Leslie
Name: PyELF
Copyright: NICTA
Licence: OzPLB
Description: PyELF library allows the parsing and manipulation
of ELF objects in a platform and toolchain independant manner.
It is primarily designed for the Kenge build system, but would
form a great basis for a general purpose Python ELF library.
Category: tool

45
tools/pyelf/pyelf.py Normal file
View File

@@ -0,0 +1,45 @@
import mmap
import os
class ELF:
EI_MAGIC = "\x7fELF"
def __init__(self, name):
f = file(name, "rb")
size = os.stat(name).st_size
self.data = mmap.mmap(f.fileno(), size, mmap.MAP_PRIVATE, mmap.PROT_READ)
if self.magic != self.EI_MAGIC:
raise "Not an elf"
def get_magic(self):
return self.data[:4]
magic = property(get_magic)
def get_class(self):
return self.data[4]
elf_class = property(get_class)
"Test suite"
x = ELF("a.out")
# Check can load an elf
success = 1
try:
x = ELF("a.out")
except:
success = 0
assert success
# CHeck can't load not and elf
success = 0
try:
x = ELF("pyelf.py")
except:
success = 1
assert success

120
tools/pyelf/readelf.py Executable file
View File

@@ -0,0 +1,120 @@
#!/usr/bin/env python
from aistruct import AIStruct
import elf, sys
from optparse import OptionParser
from os import path
class AfterBurner(AIStruct):
def __init__(self, *args, **kwargs):
AIStruct.__init__(self, AIStruct.SIZE32)
self.setup(
('UINT32', 'addr')
)
def __str__(self):
return "0x%x" % self.ai.addr.get()
def main():
parser = OptionParser(add_help_option=False)
parser.add_option("-h", "--file-header",
action="store_true", dest="header", default=False,
help="Display the ELF file header")
parser.add_option("-l", "--program-headers",
action="store_true", dest="program_headers", default=False,
help="Display the program headers")
parser.add_option("-S", "--section-headers",
action="store_true", dest="section_headers", default=False,
help="Display the section headers")
parser.add_option("--afterburn",
action="store_true", dest="afterburn", default=False,
help="Display the afterburn relocations")
parser.add_option("--first-free-page",
action="store_true", dest="ffpage", default=False,
help="Prints out (in .lds format) the address of the first free physical" + \
"page after this image at load time. Using this information at link" + \
"time, images can be compiled and linked consecutively and loaded in" + \
"consecutive memory regions at load time.")
parser.add_option("--lma-start-end", action="store_true", dest="lma_boundary", default=False,
help="Prints out the start and end LMA boundaries of an image." + \
"This is useful for autogenerating a structure for the microkernel" + \
"to discover at run-time where svc tasks are loaded.")
(options, args) = parser.parse_args()
if len(args) != 1:
parser.print_help()
return
elffile = elf.ElfFile.from_file(args[0])
if options.header:
print elffile.header
if options.program_headers:
print elffile.pheaders
if options.section_headers:
print elffile.sheaders
if options.afterburn:
burnheader = elffile.sheaders[".afterburn"]
burns = burnheader.container(AfterBurner)
print "There are %d afterburn entry points" % len(burns)
print "Afterburn:"
for burn in burns:
print " ", burn
if options.lma_boundary:
paddr_first = 0
paddr_start = 0
paddr_end = 0
for pheader in elffile.pheaders:
x = pheader.ai
if str(x.p_type) != "LOAD":
continue
# First time assign the first p_paddr.
if paddr_first == 0:
paddr_first = 1
paddr_start = x.p_paddr.value
# Then if new paddr is lesser, reassign start.
if paddr_start > x.p_paddr.value:
paddr_start = x.p_paddr.value
# If end of segment is greater, reassign end.
if paddr_end < x.p_paddr + x.p_memsz:
paddr_end = x.p_paddr + x.p_memsz
rest, image_name = path.split(args[0])
if image_name[-4] == ".":
image_name = image_name[:-4]
print image_name
print "image_start " + hex(paddr_start)[:-1]
print "image_end " + hex(paddr_end)[:-1]
if options.ffpage:
paddr_max = 0
p_align = 0
for pheader in elffile.pheaders:
x = pheader.ai
if str(x.p_type) == "LOAD":
paddr = x.p_paddr + x.p_memsz
p_align = x.p_align
if paddr > paddr_max:
paddr_max = paddr
print "/*\n" + \
" * The next free p_align'ed LMA base address\n" + \
" *\n" + \
" * p_align = " + hex(p_align.value) + "\n" + \
" *\n" + \
" * Recap from ELF spec: p_align: Loadable process segments must have\n" + \
" * congruent values for p_vaddr and p_offset, modulo the page size. \n" + \
" * This member gives the value to which the segments are aligned in \n" + \
" * memory and in the file. Values 0 and 1 mean that no alignment is \n" + \
" * required. Otherwise, p_align should be a positive, integral power\n" + \
" * of 2, and p_addr should equal p_offset, modulo p_align. \n" + \
" * This essentially means next available address must be aligned at\n" + \
" * p_align, rather than the page_size, which one (well, I) would \n" + \
" * normally expect. \n" + \
" */\n"
paddr_aligned = paddr_max & ~(p_align.value - 1)
if paddr_max & (p_align.value - 1):
paddr_aligned += p_align.value
print "physical_base = " + hex(paddr_aligned)[:-1] + ";"
if __name__ == "__main__":
main()