Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(101)

Side by Side Diff: build/mac/change_mach_o_flags.py

Issue 2101243005: Add a snapshot of flutter/engine/src/build to our sdk (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: add README.dart Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « build/mac/asan.gyp ('k') | build/mac/change_mach_o_flags_from_xcode.sh » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Usage: change_mach_o_flags.py [--executable-heap] [--no-pie] <executablepath>
7
8 Arranges for the executable at |executable_path| to have its data (heap)
9 pages protected to prevent execution on Mac OS X 10.7 ("Lion"), and to have
10 the PIE (position independent executable) bit set to enable ASLR (address
11 space layout randomization). With --executable-heap or --no-pie, the
12 respective bits are cleared instead of set, making the heap executable or
13 disabling PIE/ASLR.
14
15 This script is able to operate on thin (single-architecture) Mach-O files
16 and fat (universal, multi-architecture) files. When operating on fat files,
17 it will set or clear the bits for each architecture contained therein.
18
19 NON-EXECUTABLE HEAP
20
21 Traditionally in Mac OS X, 32-bit processes did not have data pages set to
22 prohibit execution. Although user programs could call mprotect and
23 mach_vm_protect to deny execution of code in data pages, the kernel would
24 silently ignore such requests without updating the page tables, and the
25 hardware would happily execute code on such pages. 64-bit processes were
26 always given proper hardware protection of data pages. This behavior was
27 controllable on a system-wide level via the vm.allow_data_exec sysctl, which
28 is set by default to 1. The bit with value 1 (set by default) allows code
29 execution on data pages for 32-bit processes, and the bit with value 2
30 (clear by default) does the same for 64-bit processes.
31
32 In Mac OS X 10.7, executables can "opt in" to having hardware protection
33 against code execution on data pages applied. This is done by setting a new
34 bit in the |flags| field of an executable's |mach_header|. When
35 MH_NO_HEAP_EXECUTION is set, proper protections will be applied, regardless
36 of the setting of vm.allow_data_exec. See xnu-1699.22.73/osfmk/vm/vm_map.c
37 override_nx and xnu-1699.22.73/bsd/kern/mach_loader.c load_machfile.
38
39 The Apple toolchain has been revised to set the MH_NO_HEAP_EXECUTION when
40 producing executables, provided that -allow_heap_execute is not specified
41 at link time. Only linkers shipping with Xcode 4.0 and later (ld64-123.2 and
42 later) have this ability. See ld64-123.2.1/src/ld/Options.cpp
43 Options::reconfigureDefaults() and
44 ld64-123.2.1/src/ld/HeaderAndLoadCommands.hpp
45 HeaderAndLoadCommandsAtom<A>::flags().
46
47 This script sets the MH_NO_HEAP_EXECUTION bit on Mach-O executables. It is
48 intended for use with executables produced by a linker that predates Apple's
49 modifications to set this bit itself. It is also useful for setting this bit
50 for non-i386 executables, including x86_64 executables. Apple's linker only
51 sets it for 32-bit i386 executables, presumably under the assumption that
52 the value of vm.allow_data_exec is set in stone. However, if someone were to
53 change vm.allow_data_exec to 2 or 3, 64-bit x86_64 executables would run
54 without hardware protection against code execution on data pages. This
55 script can set the bit for x86_64 executables, guaranteeing that they run
56 with appropriate protection even when vm.allow_data_exec has been tampered
57 with.
58
59 POSITION-INDEPENDENT EXECUTABLES/ADDRESS SPACE LAYOUT RANDOMIZATION
60
61 This script sets or clears the MH_PIE bit in an executable's Mach-O header,
62 enabling or disabling position independence on Mac OS X 10.5 and later.
63 Processes running position-independent executables have varying levels of
64 ASLR protection depending on the OS release. The main executable's load
65 address, shared library load addresess, and the heap and stack base
66 addresses may be randomized. Position-independent executables are produced
67 by supplying the -pie flag to the linker (or defeated by supplying -no_pie).
68 Executables linked with a deployment target of 10.7 or higher have PIE on
69 by default.
70
71 This script is never strictly needed during the build to enable PIE, as all
72 linkers used are recent enough to support -pie. However, it's used to
73 disable the PIE bit as needed on already-linked executables.
74 """
75
76 import optparse
77 import os
78 import struct
79 import sys
80
81
82 # <mach-o/fat.h>
83 FAT_MAGIC = 0xcafebabe
84 FAT_CIGAM = 0xbebafeca
85
86 # <mach-o/loader.h>
87 MH_MAGIC = 0xfeedface
88 MH_CIGAM = 0xcefaedfe
89 MH_MAGIC_64 = 0xfeedfacf
90 MH_CIGAM_64 = 0xcffaedfe
91 MH_EXECUTE = 0x2
92 MH_PIE = 0x00200000
93 MH_NO_HEAP_EXECUTION = 0x01000000
94
95
96 class MachOError(Exception):
97 """A class for exceptions thrown by this module."""
98
99 pass
100
101
102 def CheckedSeek(file, offset):
103 """Seeks the file-like object at |file| to offset |offset| and raises a
104 MachOError if anything funny happens."""
105
106 file.seek(offset, os.SEEK_SET)
107 new_offset = file.tell()
108 if new_offset != offset:
109 raise MachOError, \
110 'seek: expected offset %d, observed %d' % (offset, new_offset)
111
112
113 def CheckedRead(file, count):
114 """Reads |count| bytes from the file-like |file| object, raising a
115 MachOError if any other number of bytes is read."""
116
117 bytes = file.read(count)
118 if len(bytes) != count:
119 raise MachOError, \
120 'read: expected length %d, observed %d' % (count, len(bytes))
121
122 return bytes
123
124
125 def ReadUInt32(file, endian):
126 """Reads an unsinged 32-bit integer from the file-like |file| object,
127 treating it as having endianness specified by |endian| (per the |struct|
128 module), and returns it as a number. Raises a MachOError if the proper
129 length of data can't be read from |file|."""
130
131 bytes = CheckedRead(file, 4)
132
133 (uint32,) = struct.unpack(endian + 'I', bytes)
134 return uint32
135
136
137 def ReadMachHeader(file, endian):
138 """Reads an entire |mach_header| structure (<mach-o/loader.h>) from the
139 file-like |file| object, treating it as having endianness specified by
140 |endian| (per the |struct| module), and returns a 7-tuple of its members
141 as numbers. Raises a MachOError if the proper length of data can't be read
142 from |file|."""
143
144 bytes = CheckedRead(file, 28)
145
146 magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = \
147 struct.unpack(endian + '7I', bytes)
148 return magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags
149
150
151 def ReadFatArch(file):
152 """Reads an entire |fat_arch| structure (<mach-o/fat.h>) from the file-like
153 |file| object, treating it as having endianness specified by |endian|
154 (per the |struct| module), and returns a 5-tuple of its members as numbers.
155 Raises a MachOError if the proper length of data can't be read from
156 |file|."""
157
158 bytes = CheckedRead(file, 20)
159
160 cputype, cpusubtype, offset, size, align = struct.unpack('>5I', bytes)
161 return cputype, cpusubtype, offset, size, align
162
163
164 def WriteUInt32(file, uint32, endian):
165 """Writes |uint32| as an unsinged 32-bit integer to the file-like |file|
166 object, treating it as having endianness specified by |endian| (per the
167 |struct| module)."""
168
169 bytes = struct.pack(endian + 'I', uint32)
170 assert len(bytes) == 4
171
172 file.write(bytes)
173
174
175 def HandleMachOFile(file, options, offset=0):
176 """Seeks the file-like |file| object to |offset|, reads its |mach_header|,
177 and rewrites the header's |flags| field if appropriate. The header's
178 endianness is detected. Both 32-bit and 64-bit Mach-O headers are supported
179 (mach_header and mach_header_64). Raises MachOError if used on a header that
180 does not have a known magic number or is not of type MH_EXECUTE. The
181 MH_PIE and MH_NO_HEAP_EXECUTION bits are set or cleared in the |flags| field
182 according to |options| and written to |file| if any changes need to be made.
183 If already set or clear as specified by |options|, nothing is written."""
184
185 CheckedSeek(file, offset)
186 magic = ReadUInt32(file, '<')
187 if magic == MH_MAGIC or magic == MH_MAGIC_64:
188 endian = '<'
189 elif magic == MH_CIGAM or magic == MH_CIGAM_64:
190 endian = '>'
191 else:
192 raise MachOError, \
193 'Mach-O file at offset %d has illusion of magic' % offset
194
195 CheckedSeek(file, offset)
196 magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = \
197 ReadMachHeader(file, endian)
198 assert magic == MH_MAGIC or magic == MH_MAGIC_64
199 if filetype != MH_EXECUTE:
200 raise MachOError, \
201 'Mach-O file at offset %d is type 0x%x, expected MH_EXECUTE' % \
202 (offset, filetype)
203
204 original_flags = flags
205
206 if options.no_heap_execution:
207 flags |= MH_NO_HEAP_EXECUTION
208 else:
209 flags &= ~MH_NO_HEAP_EXECUTION
210
211 if options.pie:
212 flags |= MH_PIE
213 else:
214 flags &= ~MH_PIE
215
216 if flags != original_flags:
217 CheckedSeek(file, offset + 24)
218 WriteUInt32(file, flags, endian)
219
220
221 def HandleFatFile(file, options, fat_offset=0):
222 """Seeks the file-like |file| object to |offset| and loops over its
223 |fat_header| entries, calling HandleMachOFile for each."""
224
225 CheckedSeek(file, fat_offset)
226 magic = ReadUInt32(file, '>')
227 assert magic == FAT_MAGIC
228
229 nfat_arch = ReadUInt32(file, '>')
230
231 for index in xrange(0, nfat_arch):
232 cputype, cpusubtype, offset, size, align = ReadFatArch(file)
233 assert size >= 28
234
235 # HandleMachOFile will seek around. Come back here after calling it, in
236 # case it sought.
237 fat_arch_offset = file.tell()
238 HandleMachOFile(file, options, offset)
239 CheckedSeek(file, fat_arch_offset)
240
241
242 def main(me, args):
243 parser = optparse.OptionParser('%prog [options] <executable_path>')
244 parser.add_option('--executable-heap', action='store_false',
245 dest='no_heap_execution', default=True,
246 help='Clear the MH_NO_HEAP_EXECUTION bit')
247 parser.add_option('--no-pie', action='store_false',
248 dest='pie', default=True,
249 help='Clear the MH_PIE bit')
250 (options, loose_args) = parser.parse_args(args)
251 if len(loose_args) != 1:
252 parser.print_usage()
253 return 1
254
255 executable_path = loose_args[0]
256 executable_file = open(executable_path, 'rb+')
257
258 magic = ReadUInt32(executable_file, '<')
259 if magic == FAT_CIGAM:
260 # Check FAT_CIGAM and not FAT_MAGIC because the read was little-endian.
261 HandleFatFile(executable_file, options)
262 elif magic == MH_MAGIC or magic == MH_CIGAM or \
263 magic == MH_MAGIC_64 or magic == MH_CIGAM_64:
264 HandleMachOFile(executable_file, options)
265 else:
266 raise MachOError, '%s is not a Mach-O or fat file' % executable_file
267
268 executable_file.close()
269 return 0
270
271
272 if __name__ == '__main__':
273 sys.exit(main(sys.argv[0], sys.argv[1:]))
OLDNEW
« no previous file with comments | « build/mac/asan.gyp ('k') | build/mac/change_mach_o_flags_from_xcode.sh » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698