diff options
-rw-r--r-- | build/win/importlibs/create_import_lib.gypi | 55 | ||||
-rwxr-xr-x | build/win/importlibs/create_importlib_win.py | 218 | ||||
-rwxr-xr-x | build/win/importlibs/filter_export_list.py | 85 | ||||
-rw-r--r-- | build/win/importlibs/x86/user32.winxp.imports | 670 | ||||
-rw-r--r-- | chrome/app/delay_load_hook_unittest_win.cc | 90 | ||||
-rw-r--r-- | chrome/app/delay_load_hook_win.cc | 84 | ||||
-rw-r--r-- | chrome/app/delay_load_hook_win.h | 17 | ||||
-rw-r--r-- | chrome/chrome.gyp | 21 | ||||
-rw-r--r-- | chrome/chrome.user32.delay.imports | 29 | ||||
-rw-r--r-- | chrome/chrome_dll.gypi | 39 | ||||
-rw-r--r-- | chrome/chrome_tests_unit.gypi | 3 |
11 files changed, 1311 insertions, 0 deletions
diff --git a/build/win/importlibs/create_import_lib.gypi b/build/win/importlibs/create_import_lib.gypi new file mode 100644 index 0000000..ad281fb --- /dev/null +++ b/build/win/importlibs/create_import_lib.gypi @@ -0,0 +1,55 @@ +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This file is meant to be included into a target to provide a rule +# to create import libraries from an import description file in a consistent +# manner. +# +# To use this, create a gyp target with the following form: +# { +# 'target_name': 'my_proto_lib', +# 'type': 'none', +# 'sources': [ +# 'foo.imports', +# 'bar.imports', +# ], +# 'variables': { +# # Optional, see below: 'proto_in_dir': '.' +# 'create_importlib': 'path-to-script', +# 'lib_dir': 'path-to-output-directory', +# }, +# 'includes': ['path/to/this/gypi/file'], +# } +# +# This will generate import libraries named 'foo.lib' and 'bar.lib' in the +# specified lib directory. + +{ + 'variables': { + 'create_importlib': '<(DEPTH)/build/win/importlibs/create_importlib_win.py', + 'lib_dir': '<(PRODUCT_DIR)/lib', + }, + 'rules': [ + { + 'rule_name': 'create_import_lib', + 'extension': 'imports', + 'inputs': [ + '<(create_importlib)', + ], + 'outputs': [ + '<(lib_dir)/<(RULE_INPUT_ROOT).lib', + ], + 'action': [ + 'python', + '<(create_importlib)', + '--verbose', + '--output-file', '<@(_outputs)', + '<(RULE_INPUT_PATH)', + ], + 'msvs_cygwin_shell': 0, + 'message': 'Generating import library from <(RULE_INPUT_PATH)', + 'process_outputs_as_sources': 0, + }, + ], +} diff --git a/build/win/importlibs/create_importlib_win.py b/build/win/importlibs/create_importlib_win.py new file mode 100755 index 0000000..e71bfe4 --- /dev/null +++ b/build/win/importlibs/create_importlib_win.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +"""Creates an import library from an import description file.""" +import ast +import logging +import optparse +import os +import os.path +import shutil +import subprocess +import sys +import tempfile + + +_USAGE = """\ +Usage: %prog [options] [imports-file] + +Creates an import library from imports-file. + +Note: this script uses the microsoft assembler (ml.exe) and the library tool + (lib.exe), both of which must be in path. +""" + + +_ASM_STUB_HEADER = """\ +; This file is autogenerated by create_importlib_win.py, do not edit. +.386 +.MODEL FLAT, C +.CODE + +; Stubs to provide mangled names to lib.exe for the +; correct generation of import libs. +""" + + +_DEF_STUB_HEADER = """\ +; This file is autogenerated by create_importlib_win.py, do not edit. + +; Export declarations for generating import libs. +""" + + +_LOGGER = logging.getLogger() + + + +class _Error(Exception): + pass + + +class _ImportLibraryGenerator(object): + def __init__(self, temp_dir): + self._temp_dir = temp_dir + + def _Shell(self, cmd, **kw): + ret = subprocess.call(cmd, **kw) + _LOGGER.info('Running "%s" returned %d.', cmd, ret) + if ret != 0: + raise _Error('Command "%s" returned %d.' % (cmd, ret)) + + def _ReadImportsFile(self, imports_file): + # Slurp the imports file. + return ast.literal_eval(open(imports_file).read()) + + def _WriteStubsFile(self, import_names, output_file): + output_file.write(_ASM_STUB_HEADER) + + for name in import_names: + output_file.write('%s PROC\n' % name) + output_file.write('%s ENDP\n' % name) + + output_file.write('END\n') + + def _WriteDefFile(self, dll_name, import_names, output_file): + output_file.write(_DEF_STUB_HEADER) + output_file.write('NAME %s\n' % dll_name) + output_file.write('EXPORTS\n') + for name in import_names: + name = name.split('@')[0] + output_file.write(' %s\n' % name) + + def _CreateObj(self, dll_name, imports): + """Writes an assembly file containing empty declarations. + + For each imported function of the form: + + AddClipboardFormatListener@4 PROC + AddClipboardFormatListener@4 ENDP + + The resulting object file is then supplied to lib.exe with a .def file + declaring the corresponding non-adorned exports as they appear on the + exporting DLL, e.g. + + EXPORTS + AddClipboardFormatListener + + In combination, the .def file and the .obj file cause lib.exe to generate + an x86 import lib with public symbols named like + "__imp__AddClipboardFormatListener@4", binding to exports named like + "AddClipboardFormatListener". + + All of this is perpetrated in a temporary directory, as the intermediate + artifacts are quick and easy to produce, and of no interest to anyone + after the fact.""" + + # Create an .asm file to provide stdcall-like stub names to lib.exe. + asm_name = dll_name + '.asm' + _LOGGER.info('Writing asm file "%s".', asm_name) + with open(os.path.join(self._temp_dir, asm_name), 'wb') as stubs_file: + self._WriteStubsFile(imports, stubs_file) + + # Invoke on the assembler to compile it to .obj. + obj_name = dll_name + '.obj' + cmdline = ['ml.exe', '/nologo', '/c', asm_name, '/Fo', obj_name] + self._Shell(cmdline, cwd=self._temp_dir) + + return obj_name + + def _CreateImportLib(self, dll_name, imports, architecture, output_file): + """Creates an import lib binding imports to dll_name for architecture. + + On success, writes the import library to output file. + """ + obj_file = None + + # For x86 architecture we have to provide an object file for correct + # name mangling between the import stubs and the exported functions. + if architecture == 'x86': + obj_file = self._CreateObj(dll_name, imports) + + # Create the corresponding .def file. This file has the non stdcall-adorned + # names, as exported by the destination DLL. + def_name = dll_name + '.def' + _LOGGER.info('Writing def file "%s".', def_name) + with open(os.path.join(self._temp_dir, def_name), 'wb') as def_file: + self._WriteDefFile(dll_name, imports, def_file) + + # Invoke on lib.exe to create the import library. + # We generate everything into the temporary directory, as the .exp export + # files will be generated at the same path as the import library, and we + # don't want those files potentially gunking the works. + dll_base_name, ext = os.path.splitext(dll_name) + lib_name = dll_base_name + '.lib' + cmdline = ['lib.exe', + '/machine:%s' % architecture, + '/def:%s' % def_name, + '/out:%s' % lib_name] + if obj_file: + cmdline.append(obj_file) + + self._Shell(cmdline, cwd=self._temp_dir) + + # Copy the .lib file to the output directory. + shutil.copyfile(os.path.join(self._temp_dir, lib_name), output_file) + _LOGGER.info('Created "%s".', output_file) + + def CreateImportLib(self, imports_file, output_file): + # Read the imports file. + imports = self._ReadImportsFile(imports_file) + + # Creates the requested import library in the output directory. + self._CreateImportLib(imports['dll_name'], + imports['imports'], + imports.get('architecture', 'x86'), + output_file) + + +def main(): + parser = optparse.OptionParser(usage=_USAGE) + parser.add_option('-o', '--output-file', + help='Specifies the output file path.') + parser.add_option('-k', '--keep-temp-dir', + action='store_true', + help='Keep the temporary directory.') + parser.add_option('-v', '--verbose', + action='store_true', + help='Verbose logging.') + + options, args = parser.parse_args() + + print args + if len(args) != 1: + parser.error('You must provide an imports file.') + + if not options.output_file: + parser.error('You must provide an output file.') + + options.output_file = os.path.abspath(options.output_file) + + if options.verbose: + logging.basicConfig(level=logging.INFO) + else: + logging.basicConfig(level=logging.WARN) + + + temp_dir = tempfile.mkdtemp() + _LOGGER.info('Created temporary directory "%s."', temp_dir) + try: + # Create a generator and create the import lib. + generator = _ImportLibraryGenerator(temp_dir) + + ret = generator.CreateImportLib(args[0], options.output_file) + except Exception, e: + _LOGGER.exception('Failed to create imports.') + ret = 1 + finally: + if not options.keep_temp_dir: + shutil.rmtree(temp_dir) + _LOGGER.info('Deleted temporary directory "%s."', temp_dir) + + return ret + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/build/win/importlibs/filter_export_list.py b/build/win/importlibs/filter_export_list.py new file mode 100755 index 0000000..c2489a9 --- /dev/null +++ b/build/win/importlibs/filter_export_list.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +"""Help maintaining DLL import lists.""" +import ast +import optparse +import re +import sys + + +_EXPORT_RE = re.compile(r""" + ^\s*(?P<ordinal>[0-9]+) # The ordinal field. + \s+(?P<hint>[0-9A-F]+) # The hint field. + \s(?P<rva>........) # The RVA field. + \s+(?P<name>[^ ]+) # And finally the name we're really after. +""", re.VERBOSE) + + +_USAGE = r"""\ +Usage: %prog [options] [master-file] + +This script filters a list of exports from a DLL, generated from something +like the following command line: + +C:\> dumpbin /exports user32.dll + +against a master list of imports built from e.g. + +C:\> dumpbin /exports user32.lib + +The point of this is to trim non-public exports from the list, and to +normalize the names to their stdcall-mangled form for the generation of +import libraries. +Note that the export names from the latter incanatation are stdcall-mangled, +e.g. they are suffixed with "@" and the number of argument bytes to the +function. +""" + +def _ReadMasterFile(master_file): + # Slurp the master file. + with open(master_file) as f: + master_exports = ast.literal_eval(f.read()) + + master_mapping = {} + for export in master_exports: + name = export.split('@')[0] + master_mapping[name] = export + + return master_mapping + + +def main(): + parser = optparse.OptionParser(usage=_USAGE) + parser.add_option('-r', '--reverse', + action='store_true', + help='Reverse the matching, e.g. return the functions ' + 'in the master list that aren\'t in the input.') + + options, args = parser.parse_args() + if len(args) != 1: + parser.error('Must provide a master file.') + + master_mapping = _ReadMasterFile(args[0]) + + found_exports = [] + for line in sys.stdin: + match = _EXPORT_RE.match(line) + if match: + export_name = master_mapping.get(match.group('name'), None) + if export_name: + found_exports.append(export_name) + + if options.reverse: + # Invert the found_exports list. + found_exports = set(master_mapping.values()) - set(found_exports) + + # Sort the found exports for tidy output. + print '\n'.join(sorted(found_exports)) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/build/win/importlibs/x86/user32.winxp.imports b/build/win/importlibs/x86/user32.winxp.imports new file mode 100644 index 0000000..24403a8 --- /dev/null +++ b/build/win/importlibs/x86/user32.winxp.imports @@ -0,0 +1,670 @@ +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# This file is used to create a custom import library for Chrome's use of +# user32.dll exports. The set of exports defined below +{ + 'architecture': 'x86', + + # The DLL to bind to. + 'dll_name': 'user32.dll', + + # Name of the generated import library. + 'importlib_name': 'user32.winxp.lib', + + # This is the set of exports observed on a user32.dll from Windows XP SP2. + # The version of the DLL where these were observed is 5.1.2600.2180. + # Incidentally this set of exports also coincides with Windows XP SP3, where + # the version of the DLL is 5.1.2600.5512. + # Don't add new imports here unless and until the minimal supported + # Windows version has been bumped past Windows XP SP2+. + 'imports': [ + 'ActivateKeyboardLayout@8', + 'AdjustWindowRect@12', + 'AdjustWindowRectEx@16', + 'AllowSetForegroundWindow@4', + 'AnimateWindow@12', + 'AnyPopup@0', + 'AppendMenuA@16', + 'AppendMenuW@16', + 'ArrangeIconicWindows@4', + 'AttachThreadInput@12', + 'BeginDeferWindowPos@4', + 'BeginPaint@8', + 'BlockInput@4', + 'BringWindowToTop@4', + 'BroadcastSystemMessage@20', + 'BroadcastSystemMessageA@20', + 'BroadcastSystemMessageExA@24', + 'BroadcastSystemMessageExW@24', + 'BroadcastSystemMessageW@20', + 'CallMsgFilter@8', + 'CallMsgFilterA@8', + 'CallMsgFilterW@8', + 'CallNextHookEx@16', + 'CallWindowProcA@20', + 'CallWindowProcW@20', + 'CascadeChildWindows@8', + 'CascadeWindows@20', + 'ChangeClipboardChain@8', + 'ChangeDisplaySettingsA@8', + 'ChangeDisplaySettingsExA@20', + 'ChangeDisplaySettingsExW@20', + 'ChangeDisplaySettingsW@8', + 'ChangeMenuA@20', + 'ChangeMenuW@20', + 'CharLowerA@4', + 'CharLowerBuffA@8', + 'CharLowerBuffW@8', + 'CharLowerW@4', + 'CharNextA@4', + 'CharNextExA@12', + 'CharNextW@4', + 'CharPrevA@8', + 'CharPrevExA@16', + 'CharPrevW@8', + 'CharToOemA@8', + 'CharToOemBuffA@12', + 'CharToOemBuffW@12', + 'CharToOemW@8', + 'CharUpperA@4', + 'CharUpperBuffA@8', + 'CharUpperBuffW@8', + 'CharUpperW@4', + 'CheckDlgButton@12', + 'CheckMenuItem@12', + 'CheckMenuRadioItem@20', + 'CheckRadioButton@16', + 'ChildWindowFromPoint@12', + 'ChildWindowFromPointEx@16', + 'ClientToScreen@8', + 'ClipCursor@4', + 'CloseClipboard@0', + 'CloseDesktop@4', + 'CloseWindow@4', + 'CloseWindowStation@4', + 'CopyAcceleratorTableA@12', + 'CopyAcceleratorTableW@12', + 'CopyIcon@4', + 'CopyImage@20', + 'CopyRect@8', + 'CountClipboardFormats@0', + 'CreateAcceleratorTableA@8', + 'CreateAcceleratorTableW@8', + 'CreateCaret@16', + 'CreateCursor@28', + 'CreateDesktopA@24', + 'CreateDesktopW@24', + 'CreateDialogIndirectParamA@20', + 'CreateDialogIndirectParamW@20', + 'CreateDialogParamA@20', + 'CreateDialogParamW@20', + 'CreateIcon@28', + 'CreateIconFromResource@16', + 'CreateIconFromResourceEx@28', + 'CreateIconIndirect@4', + 'CreateMDIWindowA@40', + 'CreateMDIWindowW@40', + 'CreateMenu@0', + 'CreatePopupMenu@0', + 'CreateWindowExA@48', + 'CreateWindowExW@48', + 'CreateWindowStationA@16', + 'CreateWindowStationW@16', + 'DdeAbandonTransaction@12', + 'DdeAccessData@8', + 'DdeAddData@16', + 'DdeClientTransaction@32', + 'DdeCmpStringHandles@8', + 'DdeConnect@16', + 'DdeConnectList@20', + 'DdeCreateDataHandle@28', + 'DdeCreateStringHandleA@12', + 'DdeCreateStringHandleW@12', + 'DdeDisconnect@4', + 'DdeDisconnectList@4', + 'DdeEnableCallback@12', + 'DdeFreeDataHandle@4', + 'DdeFreeStringHandle@8', + 'DdeGetData@16', + 'DdeGetLastError@4', + 'DdeImpersonateClient@4', + 'DdeInitializeA@16', + 'DdeInitializeW@16', + 'DdeKeepStringHandle@8', + 'DdeNameService@16', + 'DdePostAdvise@12', + 'DdeQueryConvInfo@12', + 'DdeQueryNextServer@8', + 'DdeQueryStringA@20', + 'DdeQueryStringW@20', + 'DdeReconnect@4', + 'DdeSetQualityOfService@12', + 'DdeSetUserHandle@12', + 'DdeUnaccessData@4', + 'DdeUninitialize@4', + 'DefDlgProcA@16', + 'DefDlgProcW@16', + 'DefFrameProcA@20', + 'DefFrameProcW@20', + 'DefMDIChildProcA@16', + 'DefMDIChildProcW@16', + 'DefRawInputProc@12', + 'DefWindowProcA@16', + 'DefWindowProcW@16', + 'DeferWindowPos@32', + 'DeleteMenu@12', + 'DeregisterShellHookWindow@4', + 'DestroyAcceleratorTable@4', + 'DestroyCaret@0', + 'DestroyCursor@4', + 'DestroyIcon@4', + 'DestroyMenu@4', + 'DestroyWindow@4', + 'DialogBoxIndirectParamA@20', + 'DialogBoxIndirectParamW@20', + 'DialogBoxParamA@20', + 'DialogBoxParamW@20', + 'DisableProcessWindowsGhosting@0', + 'DispatchMessageA@4', + 'DispatchMessageW@4', + 'DlgDirListA@20', + 'DlgDirListComboBoxA@20', + 'DlgDirListComboBoxW@20', + 'DlgDirListW@20', + 'DlgDirSelectComboBoxExA@16', + 'DlgDirSelectComboBoxExW@16', + 'DlgDirSelectExA@16', + 'DlgDirSelectExW@16', + 'DragDetect@12', + 'DragObject@20', + 'DrawAnimatedRects@16', + 'DrawCaption@16', + 'DrawEdge@16', + 'DrawFocusRect@8', + 'DrawFrame@16', + 'DrawFrameControl@16', + 'DrawIcon@16', + 'DrawIconEx@36', + 'DrawMenuBar@4', + 'DrawStateA@40', + 'DrawStateW@40', + 'DrawTextA@20', + 'DrawTextExA@24', + 'DrawTextExW@24', + 'DrawTextW@20', + 'EditWndProc@16', + 'EmptyClipboard@0', + 'EnableMenuItem@12', + 'EnableScrollBar@12', + 'EnableWindow@8', + 'EndDeferWindowPos@4', + 'EndDialog@8', + 'EndMenu@0', + 'EndPaint@8', + 'EndTask@12', + 'EnumChildWindows@12', + 'EnumClipboardFormats@4', + 'EnumDesktopWindows@12', + 'EnumDesktopsA@12', + 'EnumDesktopsW@12', + 'EnumDisplayDevicesA@16', + 'EnumDisplayDevicesW@16', + 'EnumDisplayMonitors@16', + 'EnumDisplaySettingsA@12', + 'EnumDisplaySettingsExA@16', + 'EnumDisplaySettingsExW@16', + 'EnumDisplaySettingsW@12', + 'EnumPropsA@8', + 'EnumPropsExA@12', + 'EnumPropsExW@12', + 'EnumPropsW@8', + 'EnumThreadWindows@12', + 'EnumWindowStationsA@8', + 'EnumWindowStationsW@8', + 'EnumWindows@8', + 'EqualRect@8', + 'ExcludeUpdateRgn@8', + 'ExitWindowsEx@8', + 'FillRect@12', + 'FindWindowA@8', + 'FindWindowExA@16', + 'FindWindowExW@16', + 'FindWindowW@8', + 'FlashWindow@8', + 'FlashWindowEx@4', + 'FrameRect@12', + 'FreeDDElParam@8', + 'GetActiveWindow@0', + 'GetAltTabInfo@20', + 'GetAltTabInfoA@20', + 'GetAltTabInfoW@20', + 'GetAncestor@8', + 'GetAsyncKeyState@4', + 'GetCapture@0', + 'GetCaretBlinkTime@0', + 'GetCaretPos@4', + 'GetClassInfoA@12', + 'GetClassInfoExA@12', + 'GetClassInfoExW@12', + 'GetClassInfoW@12', + 'GetClassLongA@8', + 'GetClassLongW@8', + 'GetClassNameA@12', + 'GetClassNameW@12', + 'GetClassWord@8', + 'GetClientRect@8', + 'GetClipCursor@4', + 'GetClipboardData@4', + 'GetClipboardFormatNameA@12', + 'GetClipboardFormatNameW@12', + 'GetClipboardOwner@0', + 'GetClipboardSequenceNumber@0', + 'GetClipboardViewer@0', + 'GetComboBoxInfo@8', + 'GetCursor@0', + 'GetCursorInfo@4', + 'GetCursorPos@4', + 'GetDC@4', + 'GetDCEx@12', + 'GetDesktopWindow@0', + 'GetDialogBaseUnits@0', + 'GetDlgCtrlID@4', + 'GetDlgItem@8', + 'GetDlgItemInt@16', + 'GetDlgItemTextA@16', + 'GetDlgItemTextW@16', + 'GetDoubleClickTime@0', + 'GetFocus@0', + 'GetForegroundWindow@0', + 'GetGUIThreadInfo@8', + 'GetGuiResources@8', + 'GetIconInfo@8', + 'GetInputDesktop@0', + 'GetInputState@0', + 'GetKBCodePage@0', + 'GetKeyNameTextA@12', + 'GetKeyNameTextW@12', + 'GetKeyState@4', + 'GetKeyboardLayout@4', + 'GetKeyboardLayoutList@8', + 'GetKeyboardLayoutNameA@4', + 'GetKeyboardLayoutNameW@4', + 'GetKeyboardState@4', + 'GetKeyboardType@4', + 'GetLastActivePopup@4', + 'GetLastInputInfo@4', + 'GetLayeredWindowAttributes@16', + 'GetListBoxInfo@4', + 'GetMenu@4', + 'GetMenuBarInfo@16', + 'GetMenuCheckMarkDimensions@0', + 'GetMenuContextHelpId@4', + 'GetMenuDefaultItem@12', + 'GetMenuInfo@8', + 'GetMenuItemCount@4', + 'GetMenuItemID@8', + 'GetMenuItemInfoA@16', + 'GetMenuItemInfoW@16', + 'GetMenuItemRect@16', + 'GetMenuState@12', + 'GetMenuStringA@20', + 'GetMenuStringW@20', + 'GetMessageA@16', + 'GetMessageExtraInfo@0', + 'GetMessagePos@0', + 'GetMessageTime@0', + 'GetMessageW@16', + 'GetMonitorInfoA@8', + 'GetMonitorInfoW@8', + 'GetMouseMovePointsEx@20', + 'GetNextDlgGroupItem@12', + 'GetNextDlgTabItem@12', + 'GetOpenClipboardWindow@0', + 'GetParent@4', + 'GetPriorityClipboardFormat@8', + 'GetProcessDefaultLayout@4', + 'GetProcessWindowStation@0', + 'GetPropA@8', + 'GetPropW@8', + 'GetQueueStatus@4', + 'GetRawInputBuffer@12', + 'GetRawInputData@20', + 'GetRawInputDeviceInfoA@16', + 'GetRawInputDeviceInfoW@16', + 'GetRawInputDeviceList@12', + 'GetRegisteredRawInputDevices@12', + 'GetScrollBarInfo@12', + 'GetScrollInfo@12', + 'GetScrollPos@8', + 'GetScrollRange@16', + 'GetShellWindow@0', + 'GetSubMenu@8', + 'GetSysColor@4', + 'GetSysColorBrush@4', + 'GetSystemMenu@8', + 'GetSystemMetrics@4', + 'GetTabbedTextExtentA@20', + 'GetTabbedTextExtentW@20', + 'GetThreadDesktop@4', + 'GetTitleBarInfo@8', + 'GetTopWindow@4', + 'GetUpdateRect@12', + 'GetUpdateRgn@12', + 'GetUserObjectInformationA@20', + 'GetUserObjectInformationW@20', + 'GetUserObjectSecurity@20', + 'GetWindow@8', + 'GetWindowContextHelpId@4', + 'GetWindowDC@4', + 'GetWindowInfo@8', + 'GetWindowLongA@8', + 'GetWindowLongW@8', + 'GetWindowModuleFileName@12', + 'GetWindowModuleFileNameA@12', + 'GetWindowModuleFileNameW@12', + 'GetWindowPlacement@8', + 'GetWindowRect@8', + 'GetWindowRgn@8', + 'GetWindowRgnBox@8', + 'GetWindowTextA@12', + 'GetWindowTextLengthA@4', + 'GetWindowTextLengthW@4', + 'GetWindowTextW@12', + 'GetWindowThreadProcessId@8', + 'GetWindowWord@8', + 'GrayStringA@36', + 'GrayStringW@36', + 'HideCaret@4', + 'HiliteMenuItem@16', + 'IMPGetIMEA@8', + 'IMPGetIMEW@8', + 'IMPQueryIMEA@4', + 'IMPQueryIMEW@4', + 'IMPSetIMEA@8', + 'IMPSetIMEW@8', + 'ImpersonateDdeClientWindow@8', + 'InSendMessage@0', + 'InSendMessageEx@4', + 'InflateRect@12', + 'InsertMenuA@20', + 'InsertMenuItemA@16', + 'InsertMenuItemW@16', + 'InsertMenuW@20', + 'InternalGetWindowText@12', + 'IntersectRect@12', + 'InvalidateRect@12', + 'InvalidateRgn@12', + 'InvertRect@8', + 'IsCharAlphaA@4', + 'IsCharAlphaNumericA@4', + 'IsCharAlphaNumericW@4', + 'IsCharAlphaW@4', + 'IsCharLowerA@4', + 'IsCharLowerW@4', + 'IsCharUpperA@4', + 'IsCharUpperW@4', + 'IsChild@8', + 'IsClipboardFormatAvailable@4', + 'IsDialogMessage@8', + 'IsDialogMessageA@8', + 'IsDialogMessageW@8', + 'IsDlgButtonChecked@8', + 'IsGUIThread@4', + 'IsHungAppWindow@4', + 'IsIconic@4', + 'IsMenu@4', + 'IsRectEmpty@4', + 'IsWinEventHookInstalled@4', + 'IsWindow@4', + 'IsWindowEnabled@4', + 'IsWindowUnicode@4', + 'IsWindowVisible@4', + 'IsZoomed@4', + 'KillTimer@8', + 'LoadAcceleratorsA@8', + 'LoadAcceleratorsW@8', + 'LoadBitmapA@8', + 'LoadBitmapW@8', + 'LoadCursorA@8', + 'LoadCursorFromFileA@4', + 'LoadCursorFromFileW@4', + 'LoadCursorW@8', + 'LoadIconA@8', + 'LoadIconW@8', + 'LoadImageA@24', + 'LoadImageW@24', + 'LoadKeyboardLayoutA@8', + 'LoadKeyboardLayoutW@8', + 'LoadMenuA@8', + 'LoadMenuIndirectA@4', + 'LoadMenuIndirectW@4', + 'LoadMenuW@8', + 'LoadStringA@16', + 'LoadStringW@16', + 'LockSetForegroundWindow@4', + 'LockWindowUpdate@4', + 'LockWorkStation@0', + 'LookupIconIdFromDirectory@8', + 'LookupIconIdFromDirectoryEx@20', + 'MapDialogRect@8', + 'MapVirtualKeyA@8', + 'MapVirtualKeyExA@12', + 'MapVirtualKeyExW@12', + 'MapVirtualKeyW@8', + 'MapWindowPoints@16', + 'MenuItemFromPoint@16', + 'MessageBeep@4', + 'MessageBoxA@16', + 'MessageBoxExA@20', + 'MessageBoxExW@20', + 'MessageBoxIndirectA@4', + 'MessageBoxIndirectW@4', + 'MessageBoxTimeoutA@24', + 'MessageBoxTimeoutW@24', + 'MessageBoxW@16', + 'ModifyMenuA@20', + 'ModifyMenuW@20', + 'MonitorFromPoint@12', + 'MonitorFromRect@8', + 'MonitorFromWindow@8', + 'MoveWindow@24', + 'MsgWaitForMultipleObjects@20', + 'MsgWaitForMultipleObjectsEx@20', + 'NotifyWinEvent@16', + 'OemKeyScan@4', + 'OemToCharA@8', + 'OemToCharBuffA@12', + 'OemToCharBuffW@12', + 'OemToCharW@8', + 'OffsetRect@12', + 'OpenClipboard@4', + 'OpenDesktopA@16', + 'OpenDesktopW@16', + 'OpenIcon@4', + 'OpenInputDesktop@12', + 'OpenWindowStationA@12', + 'OpenWindowStationW@12', + 'PackDDElParam@12', + 'PaintDesktop@4', + 'PeekMessageA@20', + 'PeekMessageW@20', + 'PostMessageA@16', + 'PostMessageW@16', + 'PostQuitMessage@4', + 'PostThreadMessageA@16', + 'PostThreadMessageW@16', + 'PrintWindow@12', + 'PrivateExtractIconsA@32', + 'PrivateExtractIconsW@32', + 'PtInRect@12', + 'RealChildWindowFromPoint@12', + 'RealGetWindowClass@12', + 'RealGetWindowClassA@12', + 'RealGetWindowClassW@12', + 'RedrawWindow@16', + 'RegisterClassA@4', + 'RegisterClassExA@4', + 'RegisterClassExW@4', + 'RegisterClassW@4', + 'RegisterClipboardFormatA@4', + 'RegisterClipboardFormatW@4', + 'RegisterDeviceNotificationA@12', + 'RegisterDeviceNotificationW@12', + 'RegisterHotKey@16', + 'RegisterRawInputDevices@12', + 'RegisterShellHookWindow@4', + 'RegisterWindowMessageA@4', + 'RegisterWindowMessageW@4', + 'ReleaseCapture@0', + 'ReleaseDC@8', + 'RemoveMenu@12', + 'RemovePropA@8', + 'RemovePropW@8', + 'ReplyMessage@4', + 'ReuseDDElParam@20', + 'ScreenToClient@8', + 'ScrollDC@28', + 'ScrollWindow@20', + 'ScrollWindowEx@32', + 'SendDlgItemMessageA@20', + 'SendDlgItemMessageW@20', + 'SendIMEMessageExA@8', + 'SendIMEMessageExW@8', + 'SendInput@12', + 'SendMessageA@16', + 'SendMessageCallbackA@24', + 'SendMessageCallbackW@24', + 'SendMessageTimeoutA@28', + 'SendMessageTimeoutW@28', + 'SendMessageW@16', + 'SendNotifyMessageA@16', + 'SendNotifyMessageW@16', + 'SetActiveWindow@4', + 'SetCapture@4', + 'SetCaretBlinkTime@4', + 'SetCaretPos@8', + 'SetClassLongA@12', + 'SetClassLongW@12', + 'SetClassWord@12', + 'SetClipboardData@8', + 'SetClipboardViewer@4', + 'SetCursor@4', + 'SetCursorPos@8', + 'SetDebugErrorLevel@4', + 'SetDeskWallpaper@4', + 'SetDlgItemInt@16', + 'SetDlgItemTextA@12', + 'SetDlgItemTextW@12', + 'SetDoubleClickTime@4', + 'SetFocus@4', + 'SetForegroundWindow@4', + 'SetKeyboardState@4', + 'SetLastErrorEx@8', + 'SetLayeredWindowAttributes@16', + 'SetMenu@8', + 'SetMenuContextHelpId@8', + 'SetMenuDefaultItem@12', + 'SetMenuInfo@8', + 'SetMenuItemBitmaps@20', + 'SetMenuItemInfoA@16', + 'SetMenuItemInfoW@16', + 'SetMessageExtraInfo@4', + 'SetMessageQueue@4', + 'SetParent@8', + 'SetProcessDefaultLayout@4', + 'SetProcessWindowStation@4', + 'SetPropA@12', + 'SetPropW@12', + 'SetRect@20', + 'SetRectEmpty@4', + 'SetScrollInfo@16', + 'SetScrollPos@16', + 'SetScrollRange@20', + 'SetShellWindow@4', + 'SetSysColors@12', + 'SetSystemCursor@8', + 'SetThreadDesktop@4', + 'SetTimer@16', + 'SetUserObjectInformationA@16', + 'SetUserObjectInformationW@16', + 'SetUserObjectSecurity@12', + 'SetWinEventHook@28', + 'SetWindowContextHelpId@8', + 'SetWindowLongA@12', + 'SetWindowLongW@12', + 'SetWindowPlacement@8', + 'SetWindowPos@28', + 'SetWindowRgn@12', + 'SetWindowTextA@8', + 'SetWindowTextW@8', + 'SetWindowWord@12', + 'SetWindowsHookA@8', + 'SetWindowsHookExA@16', + 'SetWindowsHookExW@16', + 'SetWindowsHookW@8', + 'ShowCaret@4', + 'ShowCursor@4', + 'ShowOwnedPopups@8', + 'ShowScrollBar@12', + 'ShowWindow@8', + 'ShowWindowAsync@8', + 'SubtractRect@12', + 'SwapMouseButton@4', + 'SwitchDesktop@4', + 'SwitchToThisWindow@8', + 'SystemParametersInfoA@16', + 'SystemParametersInfoW@16', + 'TabbedTextOutA@32', + 'TabbedTextOutW@32', + 'TileChildWindows@8', + 'TileWindows@20', + 'ToAscii@20', + 'ToAsciiEx@24', + 'ToUnicode@24', + 'ToUnicodeEx@28', + 'TrackMouseEvent@4', + 'TrackPopupMenu@28', + 'TrackPopupMenuEx@24', + 'TranslateAccelerator@12', + 'TranslateAcceleratorA@12', + 'TranslateAcceleratorW@12', + 'TranslateMDISysAccel@8', + 'TranslateMessage@4', + 'UnhookWinEvent@4', + 'UnhookWindowsHook@8', + 'UnhookWindowsHookEx@4', + 'UnionRect@12', + 'UnloadKeyboardLayout@4', + 'UnpackDDElParam@16', + 'UnregisterClassA@8', + 'UnregisterClassW@8', + 'UnregisterDeviceNotification@4', + 'UnregisterHotKey@8', + 'UpdateLayeredWindow@36', + 'UpdateWindow@4', + 'UserHandleGrantAccess@12', + 'ValidateRect@8', + 'ValidateRgn@8', + 'VkKeyScanA@4', + 'VkKeyScanExA@8', + 'VkKeyScanExW@8', + 'VkKeyScanW@4', + 'WINNLSEnableIME@8', + 'WINNLSGetEnableStatus@4', + 'WINNLSGetIMEHotkey@4', + 'WaitForInputIdle@8', + 'WaitMessage@0', + 'WinHelpA@16', + 'WinHelpW@16', + 'WindowFromDC@4', + 'WindowFromPoint@8', + 'keybd_event@16', + 'mouse_event@20', + 'wsprintfA', + 'wsprintfW', + 'wvsprintfA@12', + 'wvsprintfW@12', + ] +} diff --git a/chrome/app/delay_load_hook_unittest_win.cc b/chrome/app/delay_load_hook_unittest_win.cc new file mode 100644 index 0000000..e599e79 --- /dev/null +++ b/chrome/app/delay_load_hook_unittest_win.cc @@ -0,0 +1,90 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <windows.h> +#if defined(_WIN32_WINNT_WIN8) && _MSC_VER < 1700 +// The Windows 8 SDK defines FACILITY_VISUALCPP in winerror.h, and in +// delayimp.h previous to VS2012. +#undef FACILITY_VISUALCPP +#endif +#include <DelayIMP.h> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/scoped_native_library.h" +#include "chrome/app/delay_load_hook_win.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class ChromeDelayLoadHookTest : public testing::Test { + public: + ChromeDelayLoadHookTest() : proc_ptr_(NULL) { + } + + virtual void SetUp() OVERRIDE { + SetupInfo("kernel32.dll"); + } + + void SetupInfo(const char* dll_name) { + info_.cb = sizeof(info_); + info_.pidd = NULL; + info_.ppfn = &proc_ptr_; + info_.szDll = dll_name; + info_.dlp.fImportByName = TRUE; + info_.dlp.szProcName = "CreateFileA"; + info_.hmodCur = NULL; + info_.pfnCur = NULL; + info_.dwLastError = 0; + } + + FARPROC proc_ptr_; + DelayLoadInfo info_; +}; + +} // namespace + +TEST_F(ChromeDelayLoadHookTest, HooksAreSetAtLinkTime) { + // This test verifies that referencing the ChromeDelayLoadHook at link + // time results in overriding the delay loader's hook instances in the CRT + // ropriately. + EXPECT_TRUE(__pfnDliNotifyHook2 == ChromeDelayLoadHook); + EXPECT_TRUE(__pfnDliFailureHook2 == ChromeDelayLoadHook); +} + +TEST_F(ChromeDelayLoadHookTest, NonSuffixedDllsAreNotHandled) { + // The hook should ignore non-suffixed DLLs. + SetupInfo("kernel32.dll"); + + HMODULE none = reinterpret_cast<HMODULE>( + ChromeDelayLoadHook(dliNotePreLoadLibrary, &info_)); + // Make sure the library is released on exit. + base::ScopedNativeLibrary lib_holder(none); + + ASSERT_TRUE(none == NULL); +} + +TEST_F(ChromeDelayLoadHookTest, SuffixedDllsAreRedirected) { + // Check that a DLL name of the form "foo-delay.dll" gets redirected to + // the "foo.dll". + SetupInfo("kernel32-delay.dll"); + HMODULE kernel32 = reinterpret_cast<HMODULE>( + ChromeDelayLoadHook(dliNotePreLoadLibrary, &info_)); + + // Make sure the library is released on exit. + base::ScopedNativeLibrary lib_holder(kernel32); + + ASSERT_TRUE(kernel32 == ::GetModuleHandle(L"kernel32.dll")); +} + +TEST_F(ChromeDelayLoadHookTest, IgnoresUnhandledNotifications) { + SetupInfo("kernel32-delay.dll"); + + // The hook should ignore all notifications but the preload notifications. + EXPECT_TRUE(ChromeDelayLoadHook(dliNoteStartProcessing, &info_) == NULL); + EXPECT_TRUE(ChromeDelayLoadHook(dliNotePreGetProcAddress, &info_) == NULL); + EXPECT_TRUE(ChromeDelayLoadHook(dliNoteEndProcessing, &info_) == NULL); + EXPECT_TRUE(ChromeDelayLoadHook(dliFailLoadLib, &info_) == NULL); + EXPECT_TRUE(ChromeDelayLoadHook(dliFailGetProc, &info_) == NULL); +} diff --git a/chrome/app/delay_load_hook_win.cc b/chrome/app/delay_load_hook_win.cc new file mode 100644 index 0000000..e911cc4 --- /dev/null +++ b/chrome/app/delay_load_hook_win.cc @@ -0,0 +1,84 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/app/delay_load_hook_win.h" + +#if defined(_WIN32_WINNT_WIN8) && _MSC_VER < 1700 +// The Windows 8 SDK defines FACILITY_VISUALCPP in winerror.h, and in +// delayimp.h previous to VS2012. +#undef FACILITY_VISUALCPP +#endif +#include <DelayIMP.h> + +#include "base/logging.h" +#include "base/string_util.h" +#include "base/stringprintf.h" + +// So long as these symbols are supplied to the final binary through an +// object file (as opposed to indirectly through a library), these pointers +// will override the CRT's symbols and direct the notifications to our hook. +// Alternatively referencing the ChromeDelayLoadHook function somehow will +// cause this declaration of these variables to take preference to the delay +// load runtime's defaults (in delayimp.lib). +PfnDliHook __pfnDliNotifyHook2 = ChromeDelayLoadHook; +PfnDliHook __pfnDliFailureHook2 = ChromeDelayLoadHook; + + +namespace { + +FARPROC OnPreLoadLibrary(DelayLoadInfo* info) { + // If the DLL name ends with "-delay.dll", this call is about one of our + // custom import libraries. In this case we need to snip the suffix off, + // and bind to the real DLL. + std::string dll_name(info->szDll); + const char kDelaySuffix[] = "-delay.dll"; + if (EndsWith(dll_name, kDelaySuffix, false)) { + // Trim the "-delay.dll" suffix from the string. + dll_name.resize(dll_name.length() - (sizeof(kDelaySuffix) - 1)); + dll_name.append(".dll"); + + return reinterpret_cast<FARPROC>(::LoadLibraryA(dll_name.c_str())); + } + + return NULL; +} + +} // namespace + +// This function is a delay load notification hook. It is invoked by the +// delay load support in the visual studio runtime. +// See http://msdn.microsoft.com/en-us/library/z9h1h6ty(v=vs.100).aspx for +// details. +FARPROC WINAPI ChromeDelayLoadHook(unsigned reason, DelayLoadInfo* info) { + switch (reason) { + case dliNoteStartProcessing: + case dliNoteEndProcessing: + // Nothing to do here. + break; + + case dliNotePreLoadLibrary: + return OnPreLoadLibrary(info); + break; + + case dliNotePreGetProcAddress: + // Nothing to do here. + break; + + case dliFailLoadLib: + case dliFailGetProc: + // Returning NULL from error notifications will cause the delay load + // runtime to raise a VcppException structured exception, that some code + // might want to handle. + return NULL; + break; + + default: + NOTREACHED() << "Impossible delay load notification."; + break; + } + + // Returning NULL causes the delay load machinery to perform default + // processing for this notification. + return NULL; +} diff --git a/chrome/app/delay_load_hook_win.h b/chrome/app/delay_load_hook_win.h new file mode 100644 index 0000000..44174ee --- /dev/null +++ b/chrome/app/delay_load_hook_win.h @@ -0,0 +1,17 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_APP_DELAY_LOAD_HOOK_WIN_H_ +#define CHROME_APP_DELAY_LOAD_HOOK_WIN_H_ + +#include <windows.h> + +// Fowd. +struct DelayLoadInfo; + +// In release builds, the delay load hook redirects import entries to DLLs +// named "FOO-delay.dll" to "FOO.dll". +FARPROC WINAPI ChromeDelayLoadHook(unsigned reason, DelayLoadInfo* info); + +#endif // CHROME_APP_DELAY_LOAD_HOOK_WIN_H_ diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index f181d4d..be44f74 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -1040,6 +1040,27 @@ ['OS=="win" and target_arch=="ia32"', { 'targets': [ { + 'target_name': 'chrome_user32_delay_imports', + 'type': 'none', + 'variables': { + 'lib_dir': '<(INTERMEDIATE_DIR)', + }, + 'sources': [ + 'chrome.user32.delay.imports' + ], + 'includes': [ + '../build/win/importlibs/create_import_lib.gypi', + ], + 'direct_dependent_settings': { + 'msvs_settings': { + 'VCLinkerTool': { + 'AdditionalLibraryDirectories': ['<(lib_dir)', ], + 'AdditionalDependencies': ['chrome.user32.delay.lib', ], + }, + }, + }, + }, + { 'target_name': 'crash_service_win64', 'type': 'executable', 'product_name': 'crash_service64', diff --git a/chrome/chrome.user32.delay.imports b/chrome/chrome.user32.delay.imports new file mode 100644 index 0000000..baa1231 --- /dev/null +++ b/chrome/chrome.user32.delay.imports @@ -0,0 +1,29 @@ +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# This file is used to create a custom import library for Chrome.dll's use of +# user32.dll APIs added post-Windows XP to obviate the need for delay loading +# all of user32.dll. +{ + 'architecture': 'x86', + + # The DLL to bind to - we delay load these imports. + 'dll_name': 'user32-delay.dll', + + # Name of the generated import library. + 'importlib_name': 'chrome.user32.delay.lib', + + # Chrome.dll uses these post-Windows XP (SP2) exports, and so they must be + # delay loaded for Chrome.dll to load on Windows XP computers. + 'imports': [ + 'CloseGestureInfoHandle@4', + 'CloseTouchInputHandle@4', + 'GetGestureInfo@8', + 'GetTouchInputInfo@16', + 'IsTouchWindow@8', + 'RegisterTouchWindow@8', + 'SetGestureConfig@20', + 'UnregisterTouchWindow@4', + ], +} diff --git a/chrome/chrome_dll.gypi b/chrome/chrome_dll.gypi index 8f5aab4..fe6c2ee6 100644 --- a/chrome/chrome_dll.gypi +++ b/chrome/chrome_dll.gypi @@ -88,6 +88,13 @@ '<(SHARED_INTERMEDIATE_DIR)/ash/ash_resources/ash_wallpaper_resources.rc', ], }], + ['OS=="win" and target_arch=="ia32"', { + # Add a dependency to custom import library for user32 delay + # imports only in x86 builds. + 'dependencies': [ + 'chrome_user32_delay_imports', + ], + },], ['OS=="win"', { 'product_name': 'chrome', 'dependencies': [ @@ -112,6 +119,8 @@ 'app/chrome_main.cc', 'app/chrome_main_delegate.cc', 'app/chrome_main_delegate.h', + 'app/delay_load_hook_win.cc', + 'app/delay_load_hook_win.h', '<(SHARED_INTERMEDIATE_DIR)/chrome_version/chrome_dll_version.rc', '../base/win/dllmain.cc', @@ -169,6 +178,36 @@ 'OutputFile': '$(OutDir)\\initial\\chrome.dll', 'UseLibraryDependencyInputs': "true", }], + ['target_arch=="ia32"', { + # Link against the XP-constrained user32 import library + # instead of the platform-SDK provided one to avoid + # inadvertently taking dependencies on post-XP user32 + # exports. + 'AdditionalDependencies!': [ + 'user32.lib', + ], + 'IgnoreDefaultLibraryNames': [ + 'user32.lib', + ], + # Remove user32 delay load for chrome.dll. + 'DelayLoadDLLs!': [ + 'user32.dll', + ], + 'AdditionalDependencies': [ + 'user32.winxp.lib', + ], + 'DelayLoadDLLs': [ + 'user32-delay.dll', + ], + 'AdditionalLibraryDirectories': [ + '<(DEPTH)/build/win/importlibs/x86', + ], + 'ForceSymbolReferences': [ + # Force the inclusion of the delay load hook in this + # binary. + 'ChromeDelayLoadHook', + ], + }], ], 'DelayLoadDLLs': [ 'comdlg32.dll', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 9507b05..9350377 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -2340,6 +2340,9 @@ 'app/breakpad_field_trial_win.cc', 'app/breakpad_win.cc', 'app/breakpad_unittest_win.cc', + 'app/delay_load_hook_win.cc', + 'app/delay_load_hook_win.h', + 'app/delay_load_hook_unittest_win.cc', 'app/crash_analysis_win.cc', 'app/hard_error_handler_win.cc', 'app/run_all_unittests.cc', |