]>
git.saurik.com Git - apple/xnu.git/blob - tools/lldbmacros/macho.py
3 from macholib
import MachO
as macho
4 from collections
import namedtuple
7 # some fixups in macholib that are required for kext support
8 macholib
.mach_o
.MH_KEXT_BUNDLE
= 0xB
10 macholib
.mach_o
.MH_FILETYPE_NAMES
[macholib
.mach_o
.MH_KEXT_BUNDLE
] = "kext bundle"
11 macholib
.mach_o
.MH_FILETYPE_SHORTNAMES
[macholib
.mach_o
.MH_KEXT_BUNDLE
] = "kext"
13 _old_MachOHeader_load
= macho
.MachOHeader
.load
16 _old_MachOHeader_load(s
, fh
)
17 except ValueError as e
:
18 if str(e
.message
).find('total_size > low_offset') >= 0:
22 except Exception as e
:
24 macho
.MachOHeader
.load
= new_load
26 class MemFile(object):
27 def __init__(self
, memory
, size
):
36 def check_bounds(self
, seek_position
, operation
):
37 if not (self
._start
<= seek_position
<= self
._end
):
38 raise IOError("%s to offset %d failed bounds check [%d, %d]" % (
39 operation
, seek_position
, self
._start
, self
._end
))
41 def seek(self
, offset
, whence
=0):
50 raise IOError("Invalid whence argument to seek: %r" % (whence
,))
51 self
.check_bounds(seekto
, 'seek')
54 def write(self
, bytes):
55 raise NotImplementedError('write is not supported')
57 def read(self
, size
=sys
.maxsize
):
59 raise ValueError("Invalid size {} while reading from {}".format(size
, self
._fileobj
))
61 self
.check_bounds(here
, 'read')
62 bytes = min(size
, self
._end
- here
)
63 retval
= self
._mem
[self
._readp
:self
._readp
+ bytes]
67 MachOSegment
= namedtuple('MachOSegment', 'name vmaddr vmsize fileoff filesize')
69 class MemMacho(macho
.MachO
):
71 def __init__(self
, memdata
, size
=None):
73 super(MemMacho
,self
).__init
__(memdata
)
76 # supports the ObjectGraph protocol
77 self
.graphident
= 'mem:%d//'.format(size
)
78 self
.filename
= 'mem:%d//'.format(size
)
83 fp
= MemFile(memdata
, size
)
87 def get_segments_with_name(self
, filter_re
):
88 """ param: filter_re is a compiled re which will be matched against segment name.
89 Use: '' to match anything and everything
90 returns: [ MachOSegment, MachOSegment, ... ]
92 if type(filter_re
) is str:
93 filter_re
= re
.compile(filter_re
)
95 for h
in self
.headers
:
96 for cmd
in h
.commands
:
97 # cmds is [(load_command, segment, [sections..])]
98 (lc
, segment
, sections
) = cmd
99 if isinstance(segment
, SEGMENT_TYPES
):
100 segname
= segment
.segname
[:segment
.segname
.find('\x00')]
101 if filter_re
.match(segname
):
102 retval
.append(MachOSegment(segname
, segment
.vmaddr
, segment
.vmsize
, segment
.fileoff
, segment
.filesize
))
105 def get_sections_with_name(self
, filter_re
):
106 """ param: filter_re is a compiled re which will be matched against <segment_name>.<section_name>
107 Use: '' to match anything and everything
108 returns: [ MachOSegment, MachOSegment, ... ]
109 where each MachOSegment.name is <segment_name>.<section_name>
111 if type(filter_re
) is str:
112 filter_re
= re
.compile(filter_re
)
114 for h
in self
.headers
:
115 for cmd
in h
.commands
:
116 # cmds is [(load_command, segment, [sections..])]
117 (lc
, segment
, sections
) = cmd
118 if isinstance(segment
, SEGMENT_TYPES
):
119 segname
= segment
.segname
[:segment
.segname
.find('\x00')]
120 for section
in sections
:
121 section_name
= section
.sectname
[:section
.sectname
.find('\x00')]
122 full_section_name
= "{}.{}".format(segname
, section_name
)
123 if filter_re
.match(full_section_name
):
124 retval
.append(MachOSegment(full_section_name
, section
.addr
, section
.size
, section
.offset
, section
.size
))
130 for h
in self
.headers
:
131 for cmd
in h
.commands
:
132 # cmds is [(load_command, segment, [sections..])]
133 (lc
, segment
, sections
) = cmd
134 if isinstance(segment
, macholib
.mach_o
.uuid_command
):
135 retval
= GetUUIDSummary(segment
.uuid
)
138 def get_text_segment(segments
):
141 if s
.name
== '__TEXT_EXEC':
144 if s
.name
== '__TEXT':
148 def get_segment_with_addr(segments
, addr
):
149 """ param: segments [MachOSegment, ...]
150 return: None or MachOSegment where addr is in vmaddr...(vmaddr+vmsize)
153 if addr
>= s
.vmaddr
and addr
< (s
.vmaddr
+ s
.vmsize
):
157 def GetUUIDSummary(arr
):
160 data
.append(ord(arr
[i
]))
161 return "{a[0]:02X}{a[1]:02X}{a[2]:02X}{a[3]:02X}-{a[4]:02X}{a[5]:02X}-{a[6]:02X}{a[7]:02X}-{a[8]:02X}{a[9]:02X}-{a[10]:02X}{a[11]:02X}{a[12]:02X}{a[13]:02X}{a[14]:02X}{a[15]:02X}".format(a
=data
)
163 SEGMENT_TYPES
= (macholib
.mach_o
.segment_command_64
, macholib
.mach_o
.segment_command
)
165 def get_load_command_human_name(cmd
):
166 """ return string name of LC_LOAD_DYLIB => "load_dylib"
167 "<unknown>" if not found
170 if cmd
in macho
.LC_REGISTRY
:
171 retval
= macho
.LC_REGISTRY
[cmd
].__name
__
172 retval
= retval
.replace("_command","")
175 class VisualMachoMap(object):
178 MB_1
= 1 * 1024 * 1024
179 GB_1
= 1 * 1024 * 1024 * 1024
181 def __init__(self
, name
, width
=40):
184 self
.default_side_padding
= 2
186 def get_header_line(self
):
187 return '+' + '-' * (self
.width
- 2) + '+'
189 def get_space_line(self
):
190 return '|' + ' ' * (self
.width
- 2) + '|'
192 def get_dashed_line(self
):
193 return '|' + '-' * (self
.width
- 2) + '|'
195 def get_dotted_line(self
):
196 return '|' + '.' * (self
.width
- 2) + '|'
198 def center_text_in_line(self
, line
, text
):
199 even_length
= bool(len(text
) % 2 == 0)
200 if len(text
) > len(line
) - 2:
201 raise ValueError("text is larger than line of text")
203 lbreak_pos
= len(line
)/2 - len(text
)/2
206 out
= line
[:lbreak_pos
] + text
207 return out
+ line
[len(out
):]
209 def get_separator_lines(self
):
210 return ['/' + ' ' * (self
.width
- 2) + '/', '/' + ' ' * (self
.width
- 2) + '/']
212 def printMachoMap(self
, mobj
):
213 MapBlock
= namedtuple('MapBlock', 'name vmaddr vmsize fileoff filesize extra_info is_segment')
214 outstr
= self
.name
+ '\n'
217 for hdr
in mobj
.headers
:
219 for cmd
in hdr
.commands
:
220 # cmds is [(load_command, segment, [sections..])]
221 (lc
, segment
, sections
) = cmd
222 lc_cmd_str
= get_load_command_human_name(lc
.cmd
)
223 lc_str_rep
= "\n\t LC: {:s} size:{:d} nsects:{:d}".format(lc_cmd_str
, lc
.cmdsize
, len(sections
))
225 if isinstance(segment
, SEGMENT_TYPES
):
226 segname
= segment
.segname
[:segment
.segname
.find('\x00')]
227 # print "\tsegment: {:s} vmaddr: {:x} vmsize:{:d} fileoff: {:x} filesize: {:d}".format(
228 # segname, segment.vmaddr, segment.vmsize, segment.fileoff, segment.filesize)
229 blocks
.append(MapBlock(segname
, segment
.vmaddr
, segment
.vmsize
, segment
.fileoff
, segment
.filesize
,
230 ' LC:{} : {} init:{:#0X} max:{:#0X}'.format(lc_cmd_str
, segname
, segment
.initprot
, segment
.maxprot
),
232 for section
in sections
:
233 section_name
= section
.sectname
[:section
.sectname
.find('\x00')]
234 blocks
.append(MapBlock(section_name
, section
.addr
, section
.size
, section
.offset
,
235 section
.size
, 'al:{} flags:{:#0X}'.format(section
.align
, section
.flags
), False))
236 #print "\t\tsection:{:s} addr:{:x} off:{:x} size:{:d}".format(section_name, section.addr, section.offset, section.size)
237 elif isinstance(segment
, macholib
.mach_o
.uuid_command
):
238 other_cmds
+= "\n\t uuid: {:s}".format(GetUUIDSummary(segment
.uuid
))
239 elif isinstance(segment
, macholib
.mach_o
.rpath_command
):
240 other_cmds
+= "\n\t rpath: {:s}".format(segment
.path
)
241 elif isinstance(segment
, macholib
.mach_o
.dylib_command
):
242 other_cmds
+= "\n\t dylib: {:s} ({:s})".format(str(sections
[:sections
.find('\x00')]), str(segment
.current_version
))
244 other_cmds
+= lc_str_rep
247 # fixup the self.width param
249 if self
.default_side_padding
+ len(_b
.name
) + 2 > self
.width
:
250 self
.width
= self
.default_side_padding
+ len(_b
.name
) + 2
251 if self
.width
% 2 != 0:
254 sorted_blocks
= sorted(blocks
, key
=lambda b
: b
.vmaddr
)
255 mstr
= [self
.get_header_line()]
256 prev_block
= MapBlock('', 0, 0, 0, 0, '', False)
257 for b
in sorted_blocks
:
258 # TODO add separator blocks if vmaddr is large from prev_block
260 s
= self
.get_dashed_line()
262 s
= self
.get_dotted_line()
263 s
= self
.center_text_in_line(s
, b
.name
)
264 line
= "{:s} {: <#020X} ({: <10d}) floff:{: <#08x} {}".format(s
, b
.vmaddr
, b
.vmsize
, b
.fileoff
, b
.extra_info
)
265 if (b
.vmaddr
- prev_block
.vmaddr
) > VisualMachoMap
.KB_16
:
266 mstr
.append(self
.get_space_line())
267 mstr
.append(self
.get_space_line())
271 if b
.vmsize
> VisualMachoMap
.MB_1
:
272 mstr
.append(self
.get_space_line())
273 mstr
.extend(self
.get_separator_lines())
274 mstr
.append(self
.get_space_line())
275 #mstr.append(self.get_space_line())
277 mstr
.append(self
.get_space_line())
278 if prev_block
.vmsize
> VisualMachoMap
.KB_16
:
279 mstr
.append(self
.get_space_line())
280 mstr
.append(self
.get_header_line())
282 print "\n".join(mstr
)
283 print "\n\n=============== Other Load Commands ==============="
287 if __name__
== '__main__':
289 if len(sys
.argv
) < 2:
290 print "Usage: {} /path/to/macho_binary".format(sys
.argv
[0])
292 with open(sys
.argv
[-1], 'rb') as fp
:
294 mobject
= MemMacho(data
, len(data
))
296 p
= VisualMachoMap(sys
.argv
[-1])
297 p
.printMachoMap(mobject
)