Skip to content

Commit

Permalink
Mach-O: major refactoring and support for relocation symbols
Browse files Browse the repository at this point in the history
  • Loading branch information
Maratyszcza committed Sep 2, 2015
1 parent 54474f7 commit 06deb08
Show file tree
Hide file tree
Showing 5 changed files with 429 additions and 265 deletions.
116 changes: 15 additions & 101 deletions peachpy/formats/macho/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,6 @@ class FileType(IntEnum):
kext_bundle = 11


class MemoryProtection(IntEnum):
read = 0x01
write = 0x02
execute = 0x04
default = 0x07


class CpuType(IntEnum):
x86 = 0x00000007
x86_64 = 0x01000007
Expand Down Expand Up @@ -102,105 +95,26 @@ def __init__(self, abi):
else:
raise ValueError("Unsupported ABI: %s" % str(abi))
self.file_type = FileType.object
self.command_count = 0
self.command_size = 0
self.commands_count = 0
self.commands_size = 0
self.flags = 0

@property
def as_bytearray(self):
import peachpy.encoder
@staticmethod
def get_size(abi):
from peachpy.abi import ABI
assert isinstance(abi, ABI)
assert abi.pointer_size in [4, 8]

encoder = peachpy.encoder.Encoder(self.abi.endianness)
return {4: 24, 8: 32}[abi.pointer_size]

def encode(self, encoder):
bytes = encoder.uint32(self.magic) + \
encoder.uint32(self.cpu_type) + \
encoder.uint32(self.cpu_subtype) + \
encoder.uint32(self.file_type) + \
encoder.uint32(self.command_count) + \
encoder.uint32(self.command_size) + \
encoder.uint32(self.flags)
encoder.uint32(self.cpu_type) + \
encoder.uint32(self.cpu_subtype) + \
encoder.uint32(self.file_type) + \
encoder.uint32(self.commands_count) + \
encoder.uint32(self.commands_size) + \
encoder.uint32(self.flags)
if self.abi.pointer_size == 8:
bytes += bytearray(4)
return bytes


class LoadCommand(object):
def __init__(self, abi, id, size):
super(LoadCommand, self).__init__()
self.abi = abi
self.id = id
self.size = size

@property
def as_bytearray(self):
import peachpy.encoder
encoder = peachpy.encoder.Encoder(self.abi.endianness)

return encoder.uint32(self.id) + encoder.uint32(self.size)


class SymbolTableCommand(LoadCommand):
def __init__(self, abi):
super(SymbolTableCommand, self).__init__(abi, id=0x2, size=24)
self.symbol_offset = None
self.symbol_count = 0
self.string_offset = None
self.string_size = 0

@property
def as_bytearray(self):
import peachpy.encoder
encoder = peachpy.encoder.Encoder(self.abi.endianness)

return encoder.uint32(self.id) + \
encoder.uint32(self.size) + \
encoder.uint32(self.symbol_offset or 0) + \
encoder.uint32(self.symbol_count) + \
encoder.uint32(self.string_offset or 0) + \
encoder.uint32(self.string_size)


class SegmentCommand(LoadCommand):
def __init__(self, abi):
super(SegmentCommand, self).__init__(abi,
id={4: 0x1, 8: 0x19}[abi.pointer_size],
size={4: 56, 8: 72}[abi.pointer_size])
self.name = None
self.address = None
self.memory_size = 0
self.offset = None
self.file_size = 0
self.section_count = 0
self.flags = 0

@property
def as_bytearray(self):
import peachpy.encoder
encoder = peachpy.encoder.Encoder(self.abi.endianness)

if self.abi.pointer_size == 4:
return encoder.uint32(self.id) + \
encoder.uint32(self.size) + \
encoder.fixed_string(self.name, 16) + \
encoder.uint32(self.address or 0) + \
encoder.uint32(self.memory_size) + \
encoder.uint32(self.offset) + \
encoder.uint32(self.file_size) + \
encoder.uint32(MemoryProtection.default) + \
encoder.uint32(MemoryProtection.default) + \
encoder.uint32(self.section_count) + \
encoder.uint32(self.flags)
else:
return encoder.uint32(self.id) + \
encoder.uint32(self.size) + \
encoder.fixed_string(self.name, 16) + \
encoder.uint64(self.address or 0) + \
encoder.uint64(self.memory_size) + \
encoder.uint64(self.offset) + \
encoder.uint64(self.file_size) + \
encoder.uint32(MemoryProtection.default) + \
encoder.uint32(MemoryProtection.default) + \
encoder.uint32(self.section_count) + \
encoder.uint32(self.flags)


173 changes: 102 additions & 71 deletions peachpy/formats/macho/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,90 +4,121 @@

class Image:
def __init__(self, abi):
from peachpy.formats.macho.file import SegmentCommand, SymbolTableCommand
from peachpy.formats.macho.section import StringTable, TextSection, ConstSection
from peachpy.formats.macho.section import Segment, TextSection, ConstSection, StringTable, SymbolTable

self.abi = abi
self.segment_command = SegmentCommand(abi)
self.segment_command.section_count = 0
self.sections = list()
text_section = TextSection(abi)
self.bind_section(text_section)
const_section = ConstSection(abi)
self.bind_section(const_section)
self.symbol_table_command = SymbolTableCommand(abi)
self.symbols = list()
self.string_table = StringTable()
self.segments = list()

self.text_segment = Segment("__TEXT")
self.add_segment(self.text_segment)

def bind_section(self, section):
self.sections.append(section)
section.index = len(self.sections)
self.segment_command.section_count += 1
self.segment_command.size += section.header.size
return section.index
self.text_section = TextSection()
self.text_segment.add_section(self.text_section)

@property
def text_section(self):
return self.sections[0]
self.const_section = ConstSection()
self.text_segment.add_section(self.const_section)

self.string_table = StringTable()
self.symbol_table = SymbolTable(self.string_table)

@property
def const_section(self):
return self.sections[1]
def add_segment(self, segment):
from peachpy.formats.macho.section import Segment
assert isinstance(segment, Segment)

@property
def as_bytearray(self):
self.segments.append(segment)

def encode(self):
from peachpy.formats.macho.file import MachHeader
from peachpy.formats.macho.symbol import Relocation
from peachpy.util import roundup
from peachpy.encoder import Encoder

bitness = {4: 32, 8: 64}[self.abi.pointer_size]
encoder = Encoder(self.abi.endianness, bitness)

mach_header = MachHeader(self.abi)
mach_header.command_size = self.segment_command.size + self.symbol_table_command.size
mach_header.command_count = 2
data = mach_header.as_bytearray

# Update section offsets
data_offset = mach_header.size + \
self.segment_command.size + \
self.symbol_table_command.size
self.segment_command.offset = data_offset
for section in self.sections:
if data_offset % self.abi.pointer_size != 0:
padding_length = self.abi.pointer_size - data_offset % self.abi.pointer_size
data_offset += padding_length
section.header.offset = data_offset
data_offset += section.header.content_size

self.symbol_table_command.symbol_count = len(self.symbols)
if len(self.symbols):
if data_offset % self.abi.pointer_size != 0:
padding_length = self.abi.pointer_size - data_offset % self.abi.pointer_size
data_offset += padding_length
self.symbol_table_command.symbol_offset = data_offset
data_offset += sum([symbol.size for symbol in self.symbols])

self.symbol_table_command.string_size = self.string_table.size
if self.string_table.size:
self.symbol_table_command.string_offset = data_offset
mach_header.commands_size = self.symbol_table.command_size
mach_header.commands_count = 1
for segment in self.segments:
mach_header.commands_size += segment.get_command_size(self.abi)
mach_header.commands_count += 1

symbol_offset_map = dict()
section_offset_map = dict()
section_address_map = dict()
section_relocations_map = dict()

# Layout the commands
data_offset = mach_header.get_size(self.abi)
for segment in self.segments:
data_offset += segment.get_command_size(self.abi)
data_offset += self.symbol_table.command_size

# Layout section data
data_address = 0
for segment in self.segments:
for section in segment.sections:
data_offset = roundup(data_offset, section.alignment)
data_address = roundup(data_address, section.alignment)
section_offset_map[section] = data_offset
section_address_map[section] = data_address
data_offset += section.content_size
data_address += section.content_size
data_offset = roundup(data_offset, self.abi.pointer_size)

# Layout the relocations
for segment in self.segments:
for section in segment.sections:
if section.relocations:
section_relocations_map[section] = data_offset
data_offset += Relocation.size * len(section.relocations)

# Layout the symbols
for symbol in self.symbol_table.symbols:
symbol_offset_map[symbol] = data_offset
data_offset += symbol.get_entry_size(self.abi)

# Layout the strings
string_table_offset = data_offset

# Create map: section->index
section_index = 1
section_index_map = dict()
for segment in self.segments:
for section in segment.sections:
section_index_map[section] = section_index
section_index += 1

# Create map: symbol->index
symbol_index_map = {symbol: index for index, symbol in enumerate(self.symbol_table.symbols)}

# Write Mach-O header
data = mach_header.encode(encoder)

# Write commands
data += self.segment_command.as_bytearray
for section in self.sections:
data += section.header.as_bytearray
data += self.symbol_table_command.as_bytearray

# Write section content
for section in self.sections:
if len(data) % self.abi.pointer_size != 0:
padding_length = self.abi.pointer_size - len(data) % self.abi.pointer_size
data += bytearray(padding_length)
data += section.content
for segment in self.segments:
data += segment.encode_command(encoder, section_offset_map, section_address_map, section_relocations_map)
data += self.symbol_table.encode_command(encoder, symbol_offset_map, string_table_offset)

# Write section data
for segment in self.segments:
for section in segment.sections:
padding = bytearray(roundup(len(data), section.alignment) - len(data))
data += padding + section.content
padding = bytearray(roundup(len(data), self.abi.pointer_size) - len(data))
data += padding

# Write relocations
for segment in self.segments:
for section in segment.sections:
for relocation in section.relocations:
data += relocation.encode(encoder, section_index_map, symbol_index_map)

# Write symbols
if len(self.symbols):
if len(data) % self.abi.pointer_size != 0:
padding_length = self.abi.pointer_size - len(data) % self.abi.pointer_size
data += bytearray(padding_length)
for symbol in self.symbols:
data += symbol.as_bytearray
for symbol in self.symbol_table.symbols:
data += symbol.encode(encoder, self.string_table.string_index_map, section_index_map, section_address_map)

# Write string table
data += self.string_table.as_bytearray
data += self.string_table.encode()

return data
Loading

0 comments on commit 06deb08

Please sign in to comment.