| #!/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 <stdlib.h>\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:])) |