diff --git a/util/mach/mig.py b/util/mach/mig.py index 9833c8c5..c2357338 100755 --- a/util/mach/mig.py +++ b/util/mach/mig.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # coding: utf-8 -# Copyright 2014 The Crashpad Authors. All rights reserved. +# Copyright 2019 The Crashpad Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,143 +15,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -import argparse -import os -import re -import subprocess import sys -def FixUserImplementation(implementation): - """Rewrites a MIG-generated user implementation (.c) file. - - Rewrites the file at |implementation| by adding “__attribute__((unused))” to - the definition of any structure typedefed as “__Reply” by searching for the - pattern unique to those structure definitions. These structures are in fact - unused in the user implementation file, and this will trigger a - -Wunused-local-typedefs warning in gcc unless removed or marked with the - “unused” attribute. - """ - - file = open(implementation, 'r+') - contents = file.read() - - pattern = re.compile('^(\t} __Reply);$', re.MULTILINE) - contents = pattern.sub(r'\1 __attribute__((unused));', contents) - - file.seek(0) - file.truncate() - file.write(contents) - file.close() - -def FixServerImplementation(implementation): - """Rewrites a MIG-generated server implementation (.c) file. - - Rewrites the file at |implementation| by replacing “mig_internal” with - “mig_external” on functions that begin with “__MIG_check__”. This makes these - functions available to other callers outside this file from a linkage - perspective. It then returns, as a list of lines, declarations that can be - added to a header file, so that other files that include that header file will - have access to these declarations from a compilation perspective. - """ - - file = open(implementation, 'r+') - contents = file.read() - - # Find interesting declarations. - declaration_pattern = \ - re.compile('^mig_internal (kern_return_t __MIG_check__.*)$', - re.MULTILINE) - declarations = declaration_pattern.findall(contents) - - # Remove “__attribute__((__unused__))” from the declarations, and call them - # “mig_external” or “extern” depending on whether “mig_external” is defined. - attribute_pattern = re.compile(r'__attribute__\(\(__unused__\)\) ') - declarations = ['#ifdef mig_external\nmig_external\n#else\nextern\n#endif\n' + - attribute_pattern.sub('', x) + - ';\n' for x in declarations] - - # Rewrite the declarations in this file as “mig_external”. - contents = declaration_pattern.sub(r'mig_external \1', contents); - - # Crashpad never implements the mach_msg_server() MIG callouts. To avoid - # needing to provide stub implementations, set KERN_FAILURE as the RetCode - # and abort(). - routine_callout_pattern = re.compile( - r'OutP->RetCode = (([a-zA-Z0-9_]+)\(.+\));') - routine_callouts = routine_callout_pattern.findall(contents) - for routine in routine_callouts: - contents = contents.replace(routine[0], 'KERN_FAILURE; abort()') - - # Include the header for abort(). - contents = '#include \n' + contents - - file.seek(0) - file.truncate() - file.write(contents) - file.close() - return declarations - -def FixHeader(header, declarations=[]): - """Rewrites a MIG-generated header (.h) file. - - Rewrites the file at |header| by placing it inside an “extern "C"” block, so - that it declares things properly when included by a C++ compilation unit. - |declarations| can be a list of additional declarations to place inside the - “extern "C"” block after the original contents of |header|. - """ - - file = open(header, 'r+') - contents = file.read() - declarations_text = ''.join(declarations) - contents = '''\ -#ifdef __cplusplus -extern "C" { -#endif - -%s -%s -#ifdef __cplusplus -} -#endif -''' % (contents, declarations_text) - file.seek(0) - file.truncate() - file.write(contents) - file.close() +import mig_fix +import mig_gen def main(args): - parser = argparse.ArgumentParser() - parser.add_argument('--developer-dir', help='Path to Xcode') - parser.add_argument('--sdk', help='Path to SDK') - parser.add_argument('--include', - default=[], - action='append', - help='Additional include directory') - parser.add_argument('defs') - parser.add_argument('user_c') - parser.add_argument('server_c') - parser.add_argument('user_h') - parser.add_argument('server_h') - parsed = parser.parse_args(args) + parsed = mig_gen.parse_args(args) - command = ['mig', - '-user', parsed.user_c, - '-server', parsed.server_c, - '-header', parsed.user_h, - '-sheader', parsed.server_h, - ] - if parsed.developer_dir is not None: - os.environ['DEVELOPER_DIR'] = parsed.developer_dir - if parsed.sdk is not None: - command.extend(['-isysroot', parsed.sdk]) - for include in parsed.include: - command.extend(['-I' + include]) - command.append(parsed.defs) - subprocess.check_call(command) - FixUserImplementation(parsed.user_c) - server_declarations = FixServerImplementation(parsed.server_c) - FixHeader(parsed.user_h) - FixHeader(parsed.server_h, server_declarations) + interface = mig_gen.MigInterface(parsed.user_c, parsed.server_c, + parsed.user_h, parsed.server_h) + mig_gen.generate_interface(parsed.defs, interface, parsed.include, + parsed.developer_dir, parsed.sdk) + mig_fix.fix_interface(interface) if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) + sys.exit(main(sys.argv[1:])) diff --git a/util/mach/mig_fix.py b/util/mach/mig_fix.py new file mode 100755 index 00000000..8cd5e4f6 --- /dev/null +++ b/util/mach/mig_fix.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python +# coding: utf-8 + +# Copyright 2019 The Crashpad Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import os +import re +import sys + +from mig_gen import MigInterface + +def _fix_user_implementation(implementation, fixed_implementation, header, + fixed_header): + """Rewrites a MIG-generated user implementation (.c) file. + + Rewrites the file at |implementation| by adding + “__attribute__((unused))” to the definition of any structure typedefed + as “__Reply” by searching for the pattern unique to those structure + definitions. These structures are in fact unused in the user + implementation file, and this will trigger a -Wunused-local-typedefs + warning in gcc unless removed or marked with the “unused” attribute. + Also changes header references to point to the new header filename, if + changed. + + If |fixed_implementation| is None, overwrites the original; otherwise, puts + the result in the file at |fixed_implementation|. + """ + + file = open(implementation, 'r+' if fixed_implementation is None else 'r') + contents = file.read() + + pattern = re.compile('^(\t} __Reply);$', re.MULTILINE) + contents = pattern.sub(r'\1 __attribute__((unused));', contents) + + if fixed_header is not None: + contents = contents.replace( + '#include "%s"' % os.path.basename(header), + '#include "%s"' % os.path.basename(fixed_header)) + + if fixed_implementation is None: + file.seek(0) + file.truncate() + else: + file.close() + file = open(fixed_implementation, 'w') + file.write(contents) + file.close() + +def _fix_server_implementation(implementation, fixed_implementation, header, + fixed_header): + """Rewrites a MIG-generated server implementation (.c) file. + + Rewrites the file at |implementation| by replacing “mig_internal” with + “mig_external” on functions that begin with “__MIG_check__”. This makes + these functions available to other callers outside this file from a linkage + perspective. It then returns, as a list of lines, declarations that can be + added to a header file, so that other files that include that header file + will have access to these declarations from a compilation perspective. Also + changes header references to point to the new header filename, if changed. + + If |fixed_implementation| is None or not provided, overwrites the original; + otherwise, puts the result in the file at |fixed_implementation|. + """ + + file = open(implementation, 'r+' if fixed_implementation is None else 'r') + contents = file.read() + + # Find interesting declarations. + declaration_pattern = \ + re.compile('^mig_internal (kern_return_t __MIG_check__.*)$', + re.MULTILINE) + declarations = declaration_pattern.findall(contents) + + # Remove “__attribute__((__unused__))” from the declarations, and call them + # “mig_external” or “extern” depending on whether “mig_external” is defined. + attribute_pattern = re.compile(r'__attribute__\(\(__unused__\)\) ') + declarations = ['''\ +#ifdef mig_external +mig_external +#else +extern +#endif +''' + attribute_pattern.sub('', x) + ';\n' for x in declarations] + + # Rewrite the declarations in this file as “mig_external”. + contents = declaration_pattern.sub(r'mig_external \1', contents); + + # Crashpad never implements the mach_msg_server() MIG callouts. To avoid + # needing to provide stub implementations, set KERN_FAILURE as the RetCode + # and abort(). + routine_callout_pattern = re.compile( + r'OutP->RetCode = (([a-zA-Z0-9_]+)\(.+\));') + routine_callouts = routine_callout_pattern.findall(contents) + for routine in routine_callouts: + contents = contents.replace(routine[0], 'KERN_FAILURE; abort()') + + # Include the header for abort(). + contents = '#include \n' + contents + + if fixed_header is not None: + contents = contents.replace( + '#include "%s"' % os.path.basename(header), + '#include "%s"' % os.path.basename(fixed_header)) + + if fixed_implementation is None: + file.seek(0) + file.truncate() + else: + file.close() + file = open(fixed_implementation, 'w') + file.write(contents) + file.close() + return declarations + +def _fix_header(header, fixed_header, declarations=[]): + """Rewrites a MIG-generated header (.h) file. + + Rewrites the file at |header| by placing it inside an “extern "C"” block, so + that it declares things properly when included by a C++ compilation unit. + |declarations| can be a list of additional declarations to place inside the + “extern "C"” block after the original contents of |header|. + + If |fixed_header| is None or not provided, overwrites the original; + otherwise, puts the result in the file at |fixed_header|. + """ + + file = open(header, 'r+' if fixed_header is None else 'r') + contents = file.read() + declarations_text = ''.join(declarations) + contents = '''\ +#ifdef __cplusplus +extern "C" { +#endif + +%s +%s +#ifdef __cplusplus +} +#endif +''' % (contents, declarations_text) + + if fixed_header is None: + file.seek(0) + file.truncate() + else: + file.close() + file = open(fixed_header, 'w') + file.write(contents) + file.close() + +def fix_interface(interface, fixed_interface=None): + if fixed_interface is None: + fixed_interface = MigInterface(None, None, None, None) + + _fix_user_implementation(interface.user_c, fixed_interface.user_c, + interface.user_h, fixed_interface.user_h) + server_declarations = _fix_server_implementation(interface.server_c, + fixed_interface.server_c, + interface.server_h, + fixed_interface.server_h) + _fix_header(interface.user_h, fixed_interface.user_h) + _fix_header(interface.server_h, fixed_interface.server_h, + server_declarations) + +def main(args): + parser = argparse.ArgumentParser() + parser.add_argument('user_c') + parser.add_argument('--fixed_user_c', default=None) + parser.add_argument('server_c') + parser.add_argument('--fixed_server_c', default=None) + parser.add_argument('user_h') + parser.add_argument('--fixed_user_h', default=None) + parser.add_argument('server_h') + parser.add_argument('--fixed_server_h', default=None) + parsed = parser.parse_args(args) + + interface = MigInterface(parsed.user_c, parsed.server_c, + parsed.user_h, parsed.server_h) + fixed_interface = MigInterface(parsed.fixed_user_c, parsed.fixed_server_c, + parsed.fixed_user_h, parsed.fixed_server_h) + fix_interface(interface, fixed_interface) + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/util/mach/mig_gen.py b/util/mach/mig_gen.py new file mode 100755 index 00000000..d71029a4 --- /dev/null +++ b/util/mach/mig_gen.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# coding: utf-8 + +# Copyright 2019 The Crashpad Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import collections +import os +import subprocess +import sys + +MigInterface = collections.namedtuple('MigInterface', ['user_c', 'server_c', + 'user_h', 'server_h']) + +def generate_interface(defs, interface, includes=[], + developer_dir=None, sdk=None): + command = ['mig', + '-user', interface.user_c, + '-server', interface.server_c, + '-header', interface.user_h, + '-sheader', interface.server_h, + ] + if developer_dir is not None: + os.environ['DEVELOPER_DIR'] = developer_dir + if sdk is not None: + command.extend(['-isysroot', sdk]) + for include in includes: + command.extend(['-I' + include]) + command.append(defs) + subprocess.check_call(command) + +def parse_args(args): + parser = argparse.ArgumentParser() + parser.add_argument('--developer-dir', help='Path to Xcode') + parser.add_argument('--sdk', help='Path to SDK') + parser.add_argument('--include', + default=[], + action='append', + help='Additional include directory') + parser.add_argument('defs') + parser.add_argument('user_c') + parser.add_argument('server_c') + parser.add_argument('user_h') + parser.add_argument('server_h') + return parser.parse_args(args) + +def main(args): + parsed = parse_args(args) + interface = MigInterface(parsed.user_c, parsed.server_c, + parsed.user_h, parsed.server_h) + generate_interface(parsed.defs, interface, parsed.include, + parsed.developer_dir, parsed.sdk) + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:]))