xref: /xnu-11417.121.6/tools/lldbmacros/macho.py (revision a1e26a70f38d1d7daa7b49b258e2f8538ad81650) !
1*a1e26a70SApple OSS Distributionsimport macholib
2*a1e26a70SApple OSS Distributionsfrom macholib import MachO as macho
3*a1e26a70SApple OSS Distributionsfrom collections import namedtuple
4*a1e26a70SApple OSS Distributionsimport uuid
5*a1e26a70SApple OSS Distributionsimport sys
6*a1e26a70SApple OSS Distributions
7*a1e26a70SApple OSS Distributions
8*a1e26a70SApple OSS Distributions#
9*a1e26a70SApple OSS Distributions# Defines segment/section descriptions that can be used by external users
10*a1e26a70SApple OSS Distributions# like kext management to keep track about memory layout. To avoid the need
11*a1e26a70SApple OSS Distributions# to keep full Mach-O instance around.
12*a1e26a70SApple OSS Distributions#
13*a1e26a70SApple OSS Distributions
14*a1e26a70SApple OSS DistributionsMachOSegment = namedtuple(
15*a1e26a70SApple OSS Distributions    'MachOSegment',
16*a1e26a70SApple OSS Distributions    'name vmaddr vmsize fileoff filesize sections'
17*a1e26a70SApple OSS Distributions)
18*a1e26a70SApple OSS Distributions
19*a1e26a70SApple OSS DistributionsMachOSection = namedtuple(
20*a1e26a70SApple OSS Distributions    'MachOSection',
21*a1e26a70SApple OSS Distributions    'sectname addr size fileoff'
22*a1e26a70SApple OSS Distributions)
23*a1e26a70SApple OSS Distributions
24*a1e26a70SApple OSS Distributions
25*a1e26a70SApple OSS Distributions#
26*a1e26a70SApple OSS Distributions# The Mach-O library loads data for each section in a Mach-O.
27*a1e26a70SApple OSS Distributions# This breaks our macros in few ways:
28*a1e26a70SApple OSS Distributions#   - It is slow and no one is really using it.
29*a1e26a70SApple OSS Distributions#   - File offsets in fileset KC points outside of the file window reported
30*a1e26a70SApple OSS Distributions#     by OSkext API.
31*a1e26a70SApple OSS Distributions#
32*a1e26a70SApple OSS Distributions# Until macholib gets some optin to avoid reading section data we have to
33*a1e26a70SApple OSS Distributions# patch it here.
34*a1e26a70SApple OSS Distributions#
35*a1e26a70SApple OSS Distributions# !!! Note. This works only with the latest lib 1.15.1 !!!
36*a1e26a70SApple OSS Distributions
37*a1e26a70SApple OSS Distributionsif macholib.__version__ == "1.15.2":
38*a1e26a70SApple OSS Distributions    from macholib.mach_o import (
39*a1e26a70SApple OSS Distributions        LC_ID_DYLIB,
40*a1e26a70SApple OSS Distributions        LC_REGISTRY,
41*a1e26a70SApple OSS Distributions        LC_SEGMENT,
42*a1e26a70SApple OSS Distributions        LC_SEGMENT_64,
43*a1e26a70SApple OSS Distributions        S_ZEROFILL,
44*a1e26a70SApple OSS Distributions        load_command,
45*a1e26a70SApple OSS Distributions        section,
46*a1e26a70SApple OSS Distributions        section_64,
47*a1e26a70SApple OSS Distributions    )
48*a1e26a70SApple OSS Distributions    from macholib.ptypes import sizeof
49*a1e26a70SApple OSS Distributions    from macholib.util import fileview
50*a1e26a70SApple OSS Distributions
51*a1e26a70SApple OSS Distributions
52*a1e26a70SApple OSS Distributions    # !!! This is the actual patch for macholib 1.15.2 !!!
53*a1e26a70SApple OSS Distributions    #
54*a1e26a70SApple OSS Distributions    #   1. MemMachOHeader subclasses macho.MachOHeader
55*a1e26a70SApple OSS Distributions    #   2. Overloaded load() method is copy/paste of the original load() with
56*a1e26a70SApple OSS Distributions    #      small patch added that disables section contents loading.
57*a1e26a70SApple OSS Distributions    #   3. The new MemMachOHeader is injected back into library and used
58*a1e26a70SApple OSS Distributions    #      in place of macho.MachOHeader.
59*a1e26a70SApple OSS Distributions    #
60*a1e26a70SApple OSS Distributions    # This code should not ever exist in the first place. So the plan is to
61*a1e26a70SApple OSS Distributions    # remove it when macholib gets improved or abandoned by our own
62*a1e26a70SApple OSS Distributions    # implementation.
63*a1e26a70SApple OSS Distributions    class MemMachOHeader(macho.MachOHeader):
64*a1e26a70SApple OSS Distributions        """ Mach-O header parser that does not try to load section data. """
65*a1e26a70SApple OSS Distributions
66*a1e26a70SApple OSS Distributions        def load(self, fh):
67*a1e26a70SApple OSS Distributions            fh = fileview(fh, self.offset, self.size)
68*a1e26a70SApple OSS Distributions            fh.seek(0)
69*a1e26a70SApple OSS Distributions
70*a1e26a70SApple OSS Distributions            self.sizediff = 0
71*a1e26a70SApple OSS Distributions            kw = {"_endian_": self.endian}
72*a1e26a70SApple OSS Distributions            header = self.mach_header.from_fileobj(fh, **kw)
73*a1e26a70SApple OSS Distributions            self.header = header
74*a1e26a70SApple OSS Distributions            # If header.magic != self.MH_MAGIC:
75*a1e26a70SApple OSS Distributions            #    raise ValueError("header has magic %08x, expecting %08x" % (
76*a1e26a70SApple OSS Distributions            #        header.magic, self.MH_MAGIC))
77*a1e26a70SApple OSS Distributions
78*a1e26a70SApple OSS Distributions            cmd = self.commands = []
79*a1e26a70SApple OSS Distributions
80*a1e26a70SApple OSS Distributions            self.filetype = self.get_filetype_shortname(header.filetype)
81*a1e26a70SApple OSS Distributions
82*a1e26a70SApple OSS Distributions            read_bytes = 0
83*a1e26a70SApple OSS Distributions            low_offset = sys.maxsize
84*a1e26a70SApple OSS Distributions            for i in range(header.ncmds):
85*a1e26a70SApple OSS Distributions                # read the load command
86*a1e26a70SApple OSS Distributions                cmd_load = load_command.from_fileobj(fh, **kw)
87*a1e26a70SApple OSS Distributions
88*a1e26a70SApple OSS Distributions                # read the specific command
89*a1e26a70SApple OSS Distributions                klass = LC_REGISTRY.get(cmd_load.cmd, None)
90*a1e26a70SApple OSS Distributions                if klass is None:
91*a1e26a70SApple OSS Distributions                    raise ValueError("Unknown load command: %d" % (cmd_load.cmd,))
92*a1e26a70SApple OSS Distributions                cmd_cmd = klass.from_fileobj(fh, **kw)
93*a1e26a70SApple OSS Distributions
94*a1e26a70SApple OSS Distributions                if cmd_load.cmd == LC_ID_DYLIB:
95*a1e26a70SApple OSS Distributions                    # remember where this command was
96*a1e26a70SApple OSS Distributions                    if self.id_cmd is not None:
97*a1e26a70SApple OSS Distributions                        raise ValueError("This dylib already has an id")
98*a1e26a70SApple OSS Distributions                    self.id_cmd = i
99*a1e26a70SApple OSS Distributions
100*a1e26a70SApple OSS Distributions                if cmd_load.cmd in (LC_SEGMENT, LC_SEGMENT_64):
101*a1e26a70SApple OSS Distributions                    # for segment commands, read the list of segments
102*a1e26a70SApple OSS Distributions                    segs = []
103*a1e26a70SApple OSS Distributions                    # assert that the size makes sense
104*a1e26a70SApple OSS Distributions                    if cmd_load.cmd == LC_SEGMENT:
105*a1e26a70SApple OSS Distributions                        section_cls = section
106*a1e26a70SApple OSS Distributions                    else:  # LC_SEGMENT_64
107*a1e26a70SApple OSS Distributions                        section_cls = section_64
108*a1e26a70SApple OSS Distributions
109*a1e26a70SApple OSS Distributions                    expected_size = (
110*a1e26a70SApple OSS Distributions                        sizeof(klass)
111*a1e26a70SApple OSS Distributions                        + sizeof(load_command)
112*a1e26a70SApple OSS Distributions                        + (sizeof(section_cls) * cmd_cmd.nsects)
113*a1e26a70SApple OSS Distributions                    )
114*a1e26a70SApple OSS Distributions                    if cmd_load.cmdsize != expected_size:
115*a1e26a70SApple OSS Distributions                        raise ValueError("Segment size mismatch")
116*a1e26a70SApple OSS Distributions                    # this is a zero block or something
117*a1e26a70SApple OSS Distributions                    # so the beginning is wherever the fileoff of this command is
118*a1e26a70SApple OSS Distributions                    if cmd_cmd.nsects == 0:
119*a1e26a70SApple OSS Distributions                        if cmd_cmd.filesize != 0:
120*a1e26a70SApple OSS Distributions                            low_offset = min(low_offset, cmd_cmd.fileoff)
121*a1e26a70SApple OSS Distributions                    else:
122*a1e26a70SApple OSS Distributions                        # this one has multiple segments
123*a1e26a70SApple OSS Distributions                        for _j in range(cmd_cmd.nsects):
124*a1e26a70SApple OSS Distributions                            # read the segment
125*a1e26a70SApple OSS Distributions                            seg = section_cls.from_fileobj(fh, **kw)
126*a1e26a70SApple OSS Distributions                            # If the segment has a size and is not zero filled
127*a1e26a70SApple OSS Distributions                            # then its beginning is the offset of this segment
128*a1e26a70SApple OSS Distributions                            not_zerofill = (seg.flags & S_ZEROFILL) != S_ZEROFILL
129*a1e26a70SApple OSS Distributions                            if seg.offset > 0 and seg.size > 0 and not_zerofill:
130*a1e26a70SApple OSS Distributions                                low_offset = min(low_offset, seg.offset)
131*a1e26a70SApple OSS Distributions
132*a1e26a70SApple OSS Distributions                            # Do NOT read section data. It is not required and
133*a1e26a70SApple OSS Distributions                            # does not work well with filset KC offsets.
134*a1e26a70SApple OSS Distributions                            """
135*a1e26a70SApple OSS Distributions                            if not_zerofill:
136*a1e26a70SApple OSS Distributions                                c = fh.tell()
137*a1e26a70SApple OSS Distributions                                fh.seek(seg.offset)
138*a1e26a70SApple OSS Distributions                                sd = fh.read(seg.size)
139*a1e26a70SApple OSS Distributions                                seg.add_section_data(sd)
140*a1e26a70SApple OSS Distributions                                fh.seek(c)
141*a1e26a70SApple OSS Distributions                            """
142*a1e26a70SApple OSS Distributions                            segs.append(seg)
143*a1e26a70SApple OSS Distributions                    # data is a list of segments
144*a1e26a70SApple OSS Distributions                    cmd_data = segs
145*a1e26a70SApple OSS Distributions
146*a1e26a70SApple OSS Distributions                else:
147*a1e26a70SApple OSS Distributions                    # data is a raw str
148*a1e26a70SApple OSS Distributions                    data_size = cmd_load.cmdsize - sizeof(klass) - sizeof(load_command)
149*a1e26a70SApple OSS Distributions                    cmd_data = fh.read(data_size)
150*a1e26a70SApple OSS Distributions                cmd.append((cmd_load, cmd_cmd, cmd_data))
151*a1e26a70SApple OSS Distributions                read_bytes += cmd_load.cmdsize
152*a1e26a70SApple OSS Distributions
153*a1e26a70SApple OSS Distributions            # make sure the header made sense
154*a1e26a70SApple OSS Distributions            if read_bytes != header.sizeofcmds:
155*a1e26a70SApple OSS Distributions                raise ValueError(
156*a1e26a70SApple OSS Distributions                    "Read %d bytes, header reports %d bytes"
157*a1e26a70SApple OSS Distributions                    % (read_bytes, header.sizeofcmds)
158*a1e26a70SApple OSS Distributions                )
159*a1e26a70SApple OSS Distributions            self.total_size = sizeof(self.mach_header) + read_bytes
160*a1e26a70SApple OSS Distributions            self.low_offset = low_offset
161*a1e26a70SApple OSS Distributions
162*a1e26a70SApple OSS Distributions
163*a1e26a70SApple OSS Distributions    # Patch the library to use our own header class instead.
164*a1e26a70SApple OSS Distributions    macho.MachOHeader = MemMachOHeader
165*a1e26a70SApple OSS Distributions
166*a1e26a70SApple OSS Distributions
167*a1e26a70SApple OSS Distributionsclass MemMachO(macho.MachO):
168*a1e26a70SApple OSS Distributions    """ Mach-O implementation that accepts I/O stream instead of file. """
169*a1e26a70SApple OSS Distributions
170*a1e26a70SApple OSS Distributions    def __init__(self, file):
171*a1e26a70SApple OSS Distributions        """ Creates Mach-O parser on top of provided I/O. """
172*a1e26a70SApple OSS Distributions
173*a1e26a70SApple OSS Distributions        # Figured out file size from the I/O.
174*a1e26a70SApple OSS Distributions        file.seek(0, 2)
175*a1e26a70SApple OSS Distributions        size = file.tell()
176*a1e26a70SApple OSS Distributions        file.seek(0, 0)
177*a1e26a70SApple OSS Distributions
178*a1e26a70SApple OSS Distributions        # supports the ObjectGraph protocol
179*a1e26a70SApple OSS Distributions        self.graphident = 'mem:%d//'.format(size)
180*a1e26a70SApple OSS Distributions        self.filename = 'mem:%d//'.format(size)
181*a1e26a70SApple OSS Distributions        self.loader_path = "<no-path>"
182*a1e26a70SApple OSS Distributions
183*a1e26a70SApple OSS Distributions        # initialized by load
184*a1e26a70SApple OSS Distributions        self.fat = None
185*a1e26a70SApple OSS Distributions        self.headers = []
186*a1e26a70SApple OSS Distributions
187*a1e26a70SApple OSS Distributions        self.load(file)
188*a1e26a70SApple OSS Distributions
189*a1e26a70SApple OSS Distributions    @staticmethod
190*a1e26a70SApple OSS Distributions    def make_seg(seg, sects):
191*a1e26a70SApple OSS Distributions        """ Constructs MachOSegment from input. """
192*a1e26a70SApple OSS Distributions
193*a1e26a70SApple OSS Distributions        # Wrap all sections in MachOSection tuple.
194*a1e26a70SApple OSS Distributions        segsec = [
195*a1e26a70SApple OSS Distributions            MachOSection(
196*a1e26a70SApple OSS Distributions                sectname = s.segname[:s.segname.find(b'\x00')].decode(),
197*a1e26a70SApple OSS Distributions                addr = s.addr,
198*a1e26a70SApple OSS Distributions                fileoff = s.offset,
199*a1e26a70SApple OSS Distributions                size = s.size
200*a1e26a70SApple OSS Distributions            )
201*a1e26a70SApple OSS Distributions            for s in sects
202*a1e26a70SApple OSS Distributions        ]
203*a1e26a70SApple OSS Distributions
204*a1e26a70SApple OSS Distributions        # Return MachOSegment
205*a1e26a70SApple OSS Distributions        return MachOSegment(
206*a1e26a70SApple OSS Distributions            name=seg.segname[:seg.segname.find(b'\x00')].decode(),
207*a1e26a70SApple OSS Distributions            vmaddr = seg.vmaddr,
208*a1e26a70SApple OSS Distributions            vmsize = seg.vmsize,
209*a1e26a70SApple OSS Distributions            fileoff = seg.fileoff,
210*a1e26a70SApple OSS Distributions            filesize = seg.filesize,
211*a1e26a70SApple OSS Distributions            sections = segsec
212*a1e26a70SApple OSS Distributions        )
213*a1e26a70SApple OSS Distributions
214*a1e26a70SApple OSS Distributions    @property
215*a1e26a70SApple OSS Distributions    def segments(self):
216*a1e26a70SApple OSS Distributions        """ Constructs section/segment descriptors.
217*a1e26a70SApple OSS Distributions
218*a1e26a70SApple OSS Distributions            Values are cached in an instance attribute.
219*a1e26a70SApple OSS Distributions        """
220*a1e26a70SApple OSS Distributions        if hasattr(self, '_segments'):
221*a1e26a70SApple OSS Distributions            return self._segments
222*a1e26a70SApple OSS Distributions
223*a1e26a70SApple OSS Distributions        # Wrap all segments/sections into a MachOSegment/MachOSection.
224*a1e26a70SApple OSS Distributions        self._segments = [
225*a1e26a70SApple OSS Distributions            self.make_seg(seg, sec)
226*a1e26a70SApple OSS Distributions            for h in self.headers
227*a1e26a70SApple OSS Distributions            for _, seg, sec in h.commands
228*a1e26a70SApple OSS Distributions            if isinstance(seg, SEGMENT_TYPES)
229*a1e26a70SApple OSS Distributions        ]
230*a1e26a70SApple OSS Distributions
231*a1e26a70SApple OSS Distributions        return self._segments
232*a1e26a70SApple OSS Distributions
233*a1e26a70SApple OSS Distributions    @property
234*a1e26a70SApple OSS Distributions    def uuid(self):
235*a1e26a70SApple OSS Distributions        """ Returns UUID of the Mach-O. """
236*a1e26a70SApple OSS Distributions        if hasattr(self, '_uuid'):
237*a1e26a70SApple OSS Distributions            return self._uuid
238*a1e26a70SApple OSS Distributions
239*a1e26a70SApple OSS Distributions        for h in self.headers:
240*a1e26a70SApple OSS Distributions            for cmd in h.commands:
241*a1e26a70SApple OSS Distributions                # cmds is [(load_command, segment, [sections..])]
242*a1e26a70SApple OSS Distributions                (_, segment, _) = cmd
243*a1e26a70SApple OSS Distributions                if isinstance(segment, macholib.mach_o.uuid_command):
244*a1e26a70SApple OSS Distributions                    self._uuid = str(uuid.UUID(bytes=segment.uuid)).upper()
245*a1e26a70SApple OSS Distributions        return self._uuid
246*a1e26a70SApple OSS Distributions
247*a1e26a70SApple OSS Distributions
248*a1e26a70SApple OSS Distributions# some fixups in macholib that are required for kext support
249*a1e26a70SApple OSS Distributionsmacholib.mach_o.MH_KEXT_BUNDLE = 0xB
250*a1e26a70SApple OSS Distributions
251*a1e26a70SApple OSS Distributionsmacholib.mach_o.MH_FILETYPE_NAMES[macholib.mach_o.MH_KEXT_BUNDLE] = "kext bundle"
252*a1e26a70SApple OSS Distributionsmacholib.mach_o.MH_FILETYPE_SHORTNAMES[macholib.mach_o.MH_KEXT_BUNDLE] = "kext"
253*a1e26a70SApple OSS Distributions
254*a1e26a70SApple OSS DistributionsSEGMENT_TYPES = (macholib.mach_o.segment_command_64, macholib.mach_o.segment_command)
255*a1e26a70SApple OSS Distributions
256*a1e26a70SApple OSS Distributionsdef get_load_command_human_name(lc):
257*a1e26a70SApple OSS Distributions    return lc.get_cmd_name()
258*a1e26a70SApple OSS Distributions
259*a1e26a70SApple OSS Distributions
260*a1e26a70SApple OSS Distributionsclass VisualMachoMap(object):
261*a1e26a70SApple OSS Distributions    KB_1 = 1024
262*a1e26a70SApple OSS Distributions    KB_16 = 16 * 1024
263*a1e26a70SApple OSS Distributions    MB_1 = 1 * 1024 * 1024
264*a1e26a70SApple OSS Distributions    GB_1 = 1 * 1024 * 1024 * 1024
265*a1e26a70SApple OSS Distributions
266*a1e26a70SApple OSS Distributions    def __init__(self, name, width=40):
267*a1e26a70SApple OSS Distributions        self.name = name
268*a1e26a70SApple OSS Distributions        self.width = 40
269*a1e26a70SApple OSS Distributions        self.default_side_padding = 2
270*a1e26a70SApple OSS Distributions
271*a1e26a70SApple OSS Distributions    def get_header_line(self):
272*a1e26a70SApple OSS Distributions        return '+' + '-' * (self.width - 2) + '+'
273*a1e26a70SApple OSS Distributions
274*a1e26a70SApple OSS Distributions    def get_space_line(self):
275*a1e26a70SApple OSS Distributions        return '|' + ' ' * (self.width - 2) + '|'
276*a1e26a70SApple OSS Distributions
277*a1e26a70SApple OSS Distributions    def get_dashed_line(self):
278*a1e26a70SApple OSS Distributions        return '|' + '-' * (self.width - 2) + '|'
279*a1e26a70SApple OSS Distributions
280*a1e26a70SApple OSS Distributions    def get_dotted_line(self):
281*a1e26a70SApple OSS Distributions        return '|' + '.' * (self.width - 2) + '|'
282*a1e26a70SApple OSS Distributions
283*a1e26a70SApple OSS Distributions    def center_text_in_line(self, line, text):
284*a1e26a70SApple OSS Distributions        even_length = bool(len(text) % 2 == 0)
285*a1e26a70SApple OSS Distributions        if len(text) > len(line) - 2:
286*a1e26a70SApple OSS Distributions            raise ValueError("text is larger than line of text")
287*a1e26a70SApple OSS Distributions
288*a1e26a70SApple OSS Distributions        lbreak_pos = (len(line) // 2) - (len(text) // 2)
289*a1e26a70SApple OSS Distributions        if not even_length:
290*a1e26a70SApple OSS Distributions            lbreak_pos -= 1
291*a1e26a70SApple OSS Distributions        out = line[:lbreak_pos] + text
292*a1e26a70SApple OSS Distributions        return out + line[len(out):]
293*a1e26a70SApple OSS Distributions
294*a1e26a70SApple OSS Distributions    def get_separator_lines(self):
295*a1e26a70SApple OSS Distributions        return ['/' + ' ' * (self.width - 2) + '/', '/' + ' ' * (self.width - 2) + '/']
296*a1e26a70SApple OSS Distributions
297*a1e26a70SApple OSS Distributions    def printMachoMap(self, mobj):
298*a1e26a70SApple OSS Distributions        MapBlock = namedtuple('MapBlock', 'name vmaddr vmsize fileoff filesize extra_info is_segment')
299*a1e26a70SApple OSS Distributions        outstr = self.name + '\n'
300*a1e26a70SApple OSS Distributions        other_cmds = ''
301*a1e26a70SApple OSS Distributions        blocks = []
302*a1e26a70SApple OSS Distributions        for hdr in mobj.headers:
303*a1e26a70SApple OSS Distributions            cmd_index = 0
304*a1e26a70SApple OSS Distributions            for cmd in hdr.commands:
305*a1e26a70SApple OSS Distributions                # cmds is [(load_command, segment, [sections..])]
306*a1e26a70SApple OSS Distributions                (lc, segment, sections) = cmd
307*a1e26a70SApple OSS Distributions                lc_cmd_str = get_load_command_human_name(lc)
308*a1e26a70SApple OSS Distributions                lc_str_rep = "\n\t LC: {:s} size:{:d} nsects:{:d}".format(lc_cmd_str, lc.cmdsize, len(sections))
309*a1e26a70SApple OSS Distributions                # print lc_str_rep
310*a1e26a70SApple OSS Distributions                if isinstance(segment, SEGMENT_TYPES):
311*a1e26a70SApple OSS Distributions                    segname = segment.segname[:segment.segname.find(b'\x00')].decode()
312*a1e26a70SApple OSS Distributions                    # print "\tsegment: {:s} vmaddr: {:x} vmsize:{:d} fileoff: {:x} filesize: {:d}".format(
313*a1e26a70SApple OSS Distributions                    #             segname, segment.vmaddr, segment.vmsize, segment.fileoff, segment.filesize)
314*a1e26a70SApple OSS Distributions                    blocks.append(MapBlock(segname, segment.vmaddr, segment.vmsize, segment.fileoff, segment.filesize,
315*a1e26a70SApple OSS Distributions                                            ' LC:{} : {} init:{:#0X} max:{:#0X}'.format(lc_cmd_str, segname, segment.initprot, segment.maxprot),
316*a1e26a70SApple OSS Distributions                                            True))
317*a1e26a70SApple OSS Distributions                    for section in sections:
318*a1e26a70SApple OSS Distributions                        section_name = section.sectname[:section.sectname.find(b'\x00')].decode()
319*a1e26a70SApple OSS Distributions                        blocks.append(MapBlock(section_name, section.addr, section.size, section.offset,
320*a1e26a70SApple OSS Distributions                                                section.size, 'al:{} flags:{:#0X}'.format(section.align, section.flags), False))
321*a1e26a70SApple OSS Distributions                        #print "\t\tsection:{:s} addr:{:x} off:{:x} size:{:d}".format(section_name, section.addr, section.offset, section.size)
322*a1e26a70SApple OSS Distributions                elif isinstance(segment, macholib.mach_o.uuid_command):
323*a1e26a70SApple OSS Distributions                    other_cmds += "\n\t uuid: {:s}".format(str(uuid.UUID(bytes=segment.uuid)).upper())
324*a1e26a70SApple OSS Distributions                elif isinstance(segment, macholib.mach_o.rpath_command):
325*a1e26a70SApple OSS Distributions                    other_cmds += "\n\t rpath: {:s}".format(segment.path)
326*a1e26a70SApple OSS Distributions                elif isinstance(segment, macholib.mach_o.dylib_command):
327*a1e26a70SApple OSS Distributions                    other_cmds += "\n\t dylib: {:s} ({:s})".format(str(sections[:sections.find(b'\x00')]), str(segment.current_version))
328*a1e26a70SApple OSS Distributions                else:
329*a1e26a70SApple OSS Distributions                    other_cmds += lc_str_rep
330*a1e26a70SApple OSS Distributions                cmd_index += 1
331*a1e26a70SApple OSS Distributions
332*a1e26a70SApple OSS Distributions        # fixup the self.width param
333*a1e26a70SApple OSS Distributions        for _b in blocks:
334*a1e26a70SApple OSS Distributions            if self.default_side_padding + len(_b.name) + 2 > self.width:
335*a1e26a70SApple OSS Distributions                self.width = self.default_side_padding + len(_b.name) + 2
336*a1e26a70SApple OSS Distributions        if self.width % 2 != 0:
337*a1e26a70SApple OSS Distributions            self.width += 1
338*a1e26a70SApple OSS Distributions
339*a1e26a70SApple OSS Distributions        sorted_blocks = sorted(blocks, key=lambda b: b.vmaddr)
340*a1e26a70SApple OSS Distributions        mstr = [self.get_header_line()]
341*a1e26a70SApple OSS Distributions        prev_block = MapBlock('', 0, 0, 0, 0, '', False)
342*a1e26a70SApple OSS Distributions        for b in sorted_blocks:
343*a1e26a70SApple OSS Distributions            # TODO add separator blocks if vmaddr is large from prev_block
344*a1e26a70SApple OSS Distributions            if b.is_segment:
345*a1e26a70SApple OSS Distributions                s = self.get_dashed_line()
346*a1e26a70SApple OSS Distributions            else:
347*a1e26a70SApple OSS Distributions                s = self.get_dotted_line()
348*a1e26a70SApple OSS Distributions            s = self.center_text_in_line(s, b.name)
349*a1e26a70SApple OSS Distributions            line = "{:s} {: <#020X} ({: <10d}) floff:{: <#08x}  {}".format(s, b.vmaddr, b.vmsize, b.fileoff, b.extra_info)
350*a1e26a70SApple OSS Distributions            if (b.vmaddr - prev_block.vmaddr) > VisualMachoMap.KB_16:
351*a1e26a70SApple OSS Distributions                mstr.append(self.get_space_line())
352*a1e26a70SApple OSS Distributions                mstr.append(self.get_space_line())
353*a1e26a70SApple OSS Distributions
354*a1e26a70SApple OSS Distributions            mstr.append(line)
355*a1e26a70SApple OSS Distributions
356*a1e26a70SApple OSS Distributions            if b.vmsize > VisualMachoMap.MB_1:
357*a1e26a70SApple OSS Distributions                mstr.append(self.get_space_line())
358*a1e26a70SApple OSS Distributions                mstr.extend(self.get_separator_lines())
359*a1e26a70SApple OSS Distributions                mstr.append(self.get_space_line())
360*a1e26a70SApple OSS Distributions            #mstr.append(self.get_space_line())
361*a1e26a70SApple OSS Distributions            prev_block = b
362*a1e26a70SApple OSS Distributions        mstr.append(self.get_space_line())
363*a1e26a70SApple OSS Distributions        if prev_block.vmsize > VisualMachoMap.KB_16:
364*a1e26a70SApple OSS Distributions            mstr.append(self.get_space_line())
365*a1e26a70SApple OSS Distributions        mstr.append(self.get_header_line())
366*a1e26a70SApple OSS Distributions        print(outstr)
367*a1e26a70SApple OSS Distributions        print("\n".join(mstr))
368*a1e26a70SApple OSS Distributions        print("\n\n=============== Other Load Commands ===============")
369*a1e26a70SApple OSS Distributions        print(other_cmds)
370*a1e26a70SApple OSS Distributions
371*a1e26a70SApple OSS Distributions
372*a1e26a70SApple OSS Distributionsif __name__ == '__main__':
373*a1e26a70SApple OSS Distributions    import sys
374*a1e26a70SApple OSS Distributions    if len(sys.argv) < 2:
375*a1e26a70SApple OSS Distributions        print("Usage: {} /path/to/macho_binary".format(sys.argv[0]))
376*a1e26a70SApple OSS Distributions        sys.exit(1)
377*a1e26a70SApple OSS Distributions    with open(sys.argv[-1], 'rb') as fp:
378*a1e26a70SApple OSS Distributions        mobject = MemMachO(fp)
379*a1e26a70SApple OSS Distributions
380*a1e26a70SApple OSS Distributions        p = VisualMachoMap(sys.argv[-1])
381*a1e26a70SApple OSS Distributions        p.printMachoMap(mobject)
382*a1e26a70SApple OSS Distributions    sys.exit(0)
383