mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
Separates generating & fixing Mach interfaces with MIG.
Adds new scripts: mig_gen.py for using MIG to generate a Mach interface, mig_fix.py for fixing the resulting interface. mig.py now wraps both into the same user interface. mig_fix.py also has the option to write its fixed output to new files, rather than overwriting the existing output. This should increase compatibility with certain build configurations. Change-Id: I743ea1bab3f63c5b92f361948b544d498ed01cbc Reviewed-on: https://chromium-review.googlesource.com/c/1389095 Reviewed-by: Mark Mentovai <mark@chromium.org> Commit-Queue: Mark Mentovai <mark@chromium.org>
This commit is contained in:
parent
e5ff36fb95
commit
3678bff13f
144
util/mach/mig.py
144
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 <stdlib.h>\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:]))
|
||||
|
197
util/mach/mig_fix.py
Executable file
197
util/mach/mig_fix.py
Executable file
@ -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 <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:]))
|
67
util/mach/mig_gen.py
Executable file
67
util/mach/mig_gen.py
Executable file
@ -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:]))
|
Loading…
x
Reference in New Issue
Block a user