1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
"""
Utilities for reading and writing Mach-O headers
"""
import os
import sys
from altgraph.ObjectGraph import ObjectGraph
from macholib.dyld import dyld_find
from macholib.MachO import MachO
from macholib.itergraphreport import itergraphreport
__all__ = ['MachOGraph']
try:
unicode
except NameError:
unicode = str
class MissingMachO(object):
def __init__(self, filename):
self.graphident = filename
self.headers = ()
def __repr__(self):
return '<%s graphident=%r>' % (type(self).__name__, self.graphident)
class MachOGraph(ObjectGraph):
"""
Graph data structure of Mach-O dependencies
"""
def __init__(self, debug=0, graph=None, env=None, executable_path=None):
super(MachOGraph, self).__init__(debug=debug, graph=graph)
self.env = env
self.trans_table = {}
self.executable_path = executable_path
def locate(self, filename, loader=None):
if not isinstance(filename, (str, unicode)):
raise TypeError("%r is not a string" % (filename,))
if filename.startswith('@loader_path/') and loader is not None:
fn = self.trans_table.get((loader.filename, filename))
if fn is None:
loader_path = loader.loader_path
try:
fn = dyld_find(
filename, env=self.env,
executable_path=self.executable_path,
loader_path=loader_path)
self.trans_table[(loader.filename, filename)] = fn
except ValueError:
return None
else:
fn = self.trans_table.get(filename)
if fn is None:
try:
fn = dyld_find(
filename, env=self.env,
executable_path=self.executable_path)
self.trans_table[filename] = fn
except ValueError:
return None
return fn
def findNode(self, name, loader=None):
assert isinstance(name, (str, unicode))
data = super(MachOGraph, self).findNode(name)
if data is not None:
return data
newname = self.locate(name, loader=loader)
if newname is not None and newname != name:
return self.findNode(newname)
return None
def run_file(self, pathname, caller=None):
assert isinstance(pathname, (str, unicode))
self.msgin(2, "run_file", pathname)
m = self.findNode(pathname, loader=caller)
if m is None:
if not os.path.exists(pathname):
raise ValueError('%r does not exist' % (pathname,))
m = self.createNode(MachO, pathname)
self.createReference(caller, m, edge_data='run_file')
self.scan_node(m)
self.msgout(2, '')
return m
def load_file(self, name, caller=None):
assert isinstance(name, (str, unicode))
self.msgin(2, "load_file", name, caller)
m = self.findNode(name, loader=caller)
if m is None:
newname = self.locate(name, loader=caller)
if newname is not None and newname != name:
return self.load_file(newname, caller=caller)
if os.path.exists(name):
m = self.createNode(MachO, name)
self.scan_node(m)
else:
m = self.createNode(MissingMachO, name)
self.msgout(2, '')
return m
def scan_node(self, node):
self.msgin(2, 'scan_node', node)
for header in node.headers:
for idx, name, filename in header.walkRelocatables():
assert isinstance(name, (str, unicode))
assert isinstance(filename, (str, unicode))
m = self.load_file(filename, caller=node)
self.createReference(node, m, edge_data=name)
self.msgout(2, '', node)
def itergraphreport(self, name='G'):
nodes = map(self.graph.describe_node, self.graph.iterdfs(self))
describe_edge = self.graph.describe_edge
return itergraphreport(nodes, describe_edge, name=name)
def graphreport(self, fileobj=None):
if fileobj is None:
fileobj = sys.stdout
fileobj.writelines(self.itergraphreport())
def main(args):
g = MachOGraph()
for arg in args:
g.run_file(arg)
g.graphreport()
if __name__ == '__main__':
main(sys.argv[1:] or ['/bin/ls'])
|