diff options
31 files changed, 579 insertions, 144 deletions
diff --git a/build/common.gypi b/build/common.gypi index 89824b3..b6b179a 100644 --- a/build/common.gypi +++ b/build/common.gypi @@ -1679,16 +1679,20 @@ 'postbuilds': [ { # Arranges for data (heap) pages to be protected against - # code execution when running on Mac OS X 10.7 ("Lion"). + # code execution when running on Mac OS X 10.7 ("Lion"), and + # ensures that the position-independent executable (PIE) bit + # is set for ASLR when running on Mac OS X 10.5 ("Leopard"). 'variables': { - # Define make_heap_non_executable in a variable ending in - # _path so that gyp understands it's a path and performs - # proper relativization during dict merging. - 'make_heap_non_executable_path': - 'mac/make_heap_non_executable_from_xcode.sh', + # Define change_mach_o_flags in a variable ending in _path + # so that GYP understands it's a path and performs proper + # relativization during dict merging. + 'change_mach_o_flags_path': + 'mac/change_mach_o_flags_from_xcode.sh', }, - 'postbuild_name': 'Make Heap Non-Executable', - 'action': ['<(make_heap_non_executable_path)'], + 'postbuild_name': 'Change Mach-O Flags', + 'action': [ + '<(change_mach_o_flags_path)', + ], }, ], }], diff --git a/build/mac/make_heap_non_executable.py b/build/mac/change_mach_o_flags.py index 507403a..1dfe07f 100755 --- a/build/mac/make_heap_non_executable.py +++ b/build/mac/change_mach_o_flags.py @@ -4,10 +4,20 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# Usage: make_heap_non_executable.py <executable_path> +# Usage: change_mach_o_flags.py [--executable-heap] [--no-pie] <executable_path> # # Arranges for the executable at |executable_path| to have its data (heap) -# pages protected to prevent execution on Mac OS X 10.7 ("Lion"). +# pages protected to prevent execution on Mac OS X 10.7 ("Lion"), and to have +# the PIE (position independent executable) bit set to enable ASLR (address +# space layout randomization). With --executable-heap or --no-pie, the +# respective bits are cleared instead of set, making the heap executable or +# disabling PIE/ASLR. +# +# This script is able to operate on thin (single-architecture) Mach-O files +# and fat (universal, multi-architecture) files. When operating on fat files, +# it will set or clear the bits for each architecture contained therein. +# +# NON-EXECUTABLE HEAP # # Traditionally in Mac OS X, 32-bit processes did not have data pages set to # prohibit execution. Although user programs could call mprotect and @@ -47,12 +57,24 @@ # with appropriate protection even when vm.allow_data_exec has been tampered # with. # -# This script is able to operate on thin (single-architecture) Mach-O files -# and fat (universal, multi-architecture) files. When operating on fat files, -# it will set the MH_NO_HEAP_EXECUTION bit for each architecture contained -# therein. +# POSITION-INDEPENDENT EXECUTABLES/ADDRESS SPACE LAYOUT RANDOMIZATION +# +# This script sets or clears the MH_PIE bit in an executable's Mach-O header, +# enabling or disabling position independence on Mac OS X 10.5 and later. +# Processes running position-independent executables have varying levels of +# ASLR protection depending on the OS release. The main executable's load +# address, shared library load addresess, and the heap and stack base +# addresses may be randomized. Position-independent executables are produced +# by supplying the -pie flag to the linker (or defeated by supplying -no_pie). +# Executables linked with a deployment target of 10.7 or higher have PIE on +# by default. +# +# This script is never strictly needed during the build to enable PIE, as all +# linkers used are recent enough to support -pie. However, it's used to +# disable the PIE bit as needed on already-linked executables. +import optparse import os import struct import sys @@ -68,7 +90,8 @@ MH_CIGAM = 0xcefaedfe MH_MAGIC_64 = 0xfeedfacf MH_CIGAM_64 = 0xcffaedfe MH_EXECUTE = 0x2 -MH_NO_HEAP_EXECUTION = 0x1000000 +MH_PIE = 0x00200000 +MH_NO_HEAP_EXECUTION = 0x01000000 class MachOError(Exception): @@ -150,14 +173,15 @@ def WriteUInt32(file, uint32, endian): file.write(bytes) -def HandleMachOFile(file, offset=0): +def HandleMachOFile(file, options, offset=0): """Seeks the file-like |file| object to |offset|, reads its |mach_header|, and rewrites the header's |flags| field if appropriate. The header's endianness is detected. Both 32-bit and 64-bit Mach-O headers are supported (mach_header and mach_header_64). Raises MachOError if used on a header that does not have a known magic number or is not of type MH_EXECUTE. The - MH_NO_HEAP_EXECUTION is set in the |flags| field and written to |file| if - not already set. If already set, nothing is written.""" + MH_PIE and MH_NO_HEAP_EXECUTION bits are set or cleared in the |flags| field + according to |options| and written to |file| if any changes need to be made. + If already set or clear as specified by |options|, nothing is written.""" CheckedSeek(file, offset) magic = ReadUInt32(file, '<') @@ -178,13 +202,24 @@ def HandleMachOFile(file, offset=0): 'Mach-O file at offset %d is type 0x%x, expected MH_EXECUTE' % \ (offset, filetype) - if not flags & MH_NO_HEAP_EXECUTION: + original_flags = flags + + if options.no_heap_execution: flags |= MH_NO_HEAP_EXECUTION + else: + flags &= ~MH_NO_HEAP_EXECUTION + + if options.pie: + flags |= MH_PIE + else: + flags &= ~MH_PIE + + if flags != original_flags: CheckedSeek(file, offset + 24) WriteUInt32(file, flags, endian) -def HandleFatFile(file, fat_offset=0): +def HandleFatFile(file, options, fat_offset=0): """Seeks the file-like |file| object to |offset| and loops over its |fat_header| entries, calling HandleMachOFile for each.""" @@ -201,25 +236,33 @@ def HandleFatFile(file, fat_offset=0): # HandleMachOFile will seek around. Come back here after calling it, in # case it sought. fat_arch_offset = file.tell() - HandleMachOFile(file, offset) + HandleMachOFile(file, options, offset) CheckedSeek(file, fat_arch_offset) def main(me, args): - if len(args) != 1: - print >>sys.stderr, 'usage: %s <executable_path>' % me + parser = optparse.OptionParser('%prog [options] <executable_path>') + parser.add_option('--executable-heap', action='store_false', + dest='no_heap_execution', default=True, + help='Clear the MH_NO_HEAP_EXECUTION bit') + parser.add_option('--no-pie', action='store_false', + dest='pie', default=True, + help='Clear the MH_PIE bit') + (options, loose_args) = parser.parse_args(args) + if len(loose_args) != 1: + parser.print_usage() return 1 - executable_path = args[0] + executable_path = loose_args[0] executable_file = open(executable_path, 'rb+') magic = ReadUInt32(executable_file, '<') if magic == FAT_CIGAM: # Check FAT_CIGAM and not FAT_MAGIC because the read was little-endian. - HandleFatFile(executable_file) + HandleFatFile(executable_file, options) elif magic == MH_MAGIC or magic == MH_CIGAM or \ magic == MH_MAGIC_64 or magic == MH_CIGAM_64: - HandleMachOFile(executable_file) + HandleMachOFile(executable_file, options) else: raise MachOError, '%s is not a Mach-O or fat file' % executable_file diff --git a/build/mac/change_mach_o_flags_from_xcode.sh b/build/mac/change_mach_o_flags_from_xcode.sh new file mode 100755 index 0000000..ab3ee49 --- /dev/null +++ b/build/mac/change_mach_o_flags_from_xcode.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# Copyright (c) 2011 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 is a small wrapper script around change_mach_o_flags.py allowing it to +# be invoked easily from Xcode. change_mach_o_flags.py expects its arguments +# on the command line, but Xcode puts its parameters in the environment. + +set -e + +exec "$(dirname "${0}")/change_mach_o_flags.py" \ + "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}" diff --git a/build/mac/make_heap_non_executable_from_xcode.sh b/build/mac/make_heap_non_executable_from_xcode.sh deleted file mode 100755 index adfc25d..0000000 --- a/build/mac/make_heap_non_executable_from_xcode.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh - -# Copyright (c) 2011 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 is a small wrapper script around make_heap_non_executable.py allowing -# it to be invoked easily from Xcode. make_heap_non_executable.py expects its -# argument on the command line, and Xcode puts all of its parameters in the -# environment. - -set -e - -exec "$(dirname "${0}")/make_heap_non_executable.py" \ - "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}" diff --git a/chrome/app/chrome_main.cc b/chrome/app/chrome_main.cc index a1d635e..9c680df 100644 --- a/chrome/app/chrome_main.cc +++ b/chrome/app/chrome_main.cc @@ -764,11 +764,37 @@ int ChromeMain(int argc, char** argv) { // The helper's Info.plist marks it as a background only app. if (base::mac::IsBackgroundOnlyProcess()) { - CHECK(command_line.HasSwitch(switches::kProcessType)) + CHECK(command_line.HasSwitch(switches::kProcessType) && + !process_type.empty()) << "Helper application requires --type."; + + // In addition, some helper flavors only work with certain process types. + FilePath executable; + if (PathService::Get(base::FILE_EXE, &executable) && + executable.value().size() >= 3) { + std::string last_three = + executable.value().substr(executable.value().size() - 3); + + if (last_three == " EH") { + CHECK_EQ(switches::kPluginProcess, process_type) + << "Executable-heap process requires --type=" + << switches::kPluginProcess << ", saw " << process_type; + } else if (last_three == " NP") { + CHECK_EQ(switches::kNaClLoaderProcess, process_type) + << "Non-PIE process requires --type=" + << switches::kNaClLoaderProcess << ", saw " << process_type; + } else { + CHECK(process_type != switches::kPluginProcess && + process_type != switches::kNaClLoaderProcess) + << "Non-executable-heap PIE process is intolerant of --type=" + << switches::kPluginProcess << " and " + << switches::kNaClLoaderProcess << ", saw " << process_type; + } + } } else { - CHECK(!command_line.HasSwitch(switches::kProcessType)) - << "Main application forbids --type, saw \"" << process_type << "\"."; + CHECK(!command_line.HasSwitch(switches::kProcessType) && + process_type.empty()) + << "Main application forbids --type, saw " << process_type; } if (IsCrashReporterEnabled()) diff --git a/chrome/app/helper-Info.plist b/chrome/app/helper-Info.plist index 3e057b1..86f2f05 100644 --- a/chrome/app/helper-Info.plist +++ b/chrome/app/helper-Info.plist @@ -20,8 +20,6 @@ <string>????</string> <key>LSFileQuarantineEnabled</key> <true/> - <key>LSHasLocalizedDisplayName</key> - <string>1</string> <key>LSMinimumSystemVersion</key> <string>10.5.0</string> <key>LSUIElement</key> diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc index 233cc0e..32aa04d 100644 --- a/chrome/browser/automation/testing_automation_provider.cc +++ b/chrome/browser/automation/testing_automation_provider.cc @@ -2774,8 +2774,14 @@ void TestingAutomationProvider::GetBrowserInfo( } return_value->Set("windows", windows); +#if defined(OS_LINUX) + int flags = ChildProcessHost::CHILD_ALLOW_SELF; +#else + int flags = ChildProcessHost::CHILD_NORMAL; +#endif + return_value->SetString("child_process_path", - ChildProcessHost::GetChildPath(true).value()); + ChildProcessHost::GetChildPath(flags).value()); // Child processes are the processes for plugins and other workers. // Add all child processes in a list of dictionaries, one dictionary item // per child process. @@ -5818,7 +5824,7 @@ void TestingAutomationProvider::ExecuteJavascriptInRenderView( RenderViewHost* rvh = RenderViewHost::FromID(render_process_id, render_view_id); - if(!rvh) { + if (!rvh) { AutomationJSONReply(this, reply_message).SendError( "A RenderViewHost object was not found with the given view ID."); return; diff --git a/chrome/browser/importer/profile_import_process_host.cc b/chrome/browser/importer/profile_import_process_host.cc index 745b5f3..70350cf 100644 --- a/chrome/browser/importer/profile_import_process_host.cc +++ b/chrome/browser/importer/profile_import_process_host.cc @@ -76,7 +76,12 @@ bool ProfileImportProcessHost::ReportImportItemFinished( } FilePath ProfileImportProcessHost::GetProfileImportProcessCmd() { - return GetChildPath(true); +#if defined(OS_LINUX) + int flags = CHILD_ALLOW_SELF; +#else + int flags = CHILD_NORMAL; +#endif + return GetChildPath(flags); } bool ProfileImportProcessHost::StartProcess() { diff --git a/chrome/browser/memory_details_mac.cc b/chrome/browser/memory_details_mac.cc index c03c8c9..037d527 100644 --- a/chrome/browser/memory_details_mac.cc +++ b/chrome/browser/memory_details_mac.cc @@ -113,11 +113,24 @@ void MemoryDetails::CollectProcessData( } } + // The helper might show up as these different flavors depending on the + // executable flags required. + std::vector<std::string> helper_names; + helper_names.push_back(chrome::kHelperProcessExecutableName); + for (const char* const* suffix = chrome::kHelperFlavorSuffixes; + *suffix; + ++suffix) { + std::string helper_name = chrome::kHelperProcessExecutableName; + helper_name.append(1, ' '); + helper_name.append(*suffix); + helper_names.push_back(helper_name); + } + // Get PIDs of helpers. std::vector<base::ProcessId> helper_pids; - { - base::NamedProcessIterator helper_it(chrome::kHelperProcessExecutableName, - NULL); + for (size_t i = 0; i < helper_names.size(); ++i) { + std::string helper_name = helper_names[i]; + base::NamedProcessIterator helper_it(helper_name, NULL); while (const base::ProcessEntry* entry = helper_it.NextProcessEntry()) { helper_pids.push_back(entry->pid()); all_pids.push_back(entry->pid()); diff --git a/chrome/browser/nacl_host/nacl_process_host.cc b/chrome/browser/nacl_host/nacl_process_host.cc index 39a2f37..cb2f3a9 100644 --- a/chrome/browser/nacl_host/nacl_process_host.cc +++ b/chrome/browser/nacl_host/nacl_process_host.cc @@ -145,7 +145,22 @@ bool NaClProcessHost::LaunchSelLdr() { #endif // defined(OS_POSIX) // Build command line for nacl. - FilePath exe_path = GetChildPath(nacl_loader_prefix.empty()); + +#if defined(OS_MACOSX) + // The Native Client process needs to be able to allocate a 1GB contiguous + // region to use as the client environment's virtual address space. ASLR + // (PIE) interferes with this by making it possible that no gap large enough + // to accomodate this request will exist in the child process' address + // space. Disable PIE for NaCl processes. See http://crbug.com/90221 and + // http://code.google.com/p/nativeclient/issues/detail?id=2043. + int flags = CHILD_NO_PIE; +#elif defined(OS_LINUX) + int flags = nacl_loader_prefix.empty() ? CHILD_ALLOW_SELF : CHILD_NORMAL; +#else + int flags = CHILD_NORMAL; +#endif + + FilePath exe_path = GetChildPath(flags); if (exe_path.empty()) return false; diff --git a/chrome/browser/service/service_process_control.cc b/chrome/browser/service/service_process_control.cc index 9557af2..40de028 100644 --- a/chrome/browser/service/service_process_control.cc +++ b/chrome/browser/service/service_process_control.cc @@ -118,7 +118,14 @@ void ServiceProcessControl::Launch(Task* success_task, Task* failure_task) { // A service process should have a different mechanism for starting, but now // we start it as if it is a child process. - FilePath exe_path = ChildProcessHost::GetChildPath(true); + +#if defined(OS_LINUX) + int flags = ChildProcessHost::CHILD_ALLOW_SELF; +#else + int flags = ChildProcessHost::CHILD_NORMAL; +#endif + + FilePath exe_path = ChildProcessHost::GetChildPath(flags); if (exe_path.empty()) { NOTREACHED() << "Unable to get service process binary name."; } diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 42540b1..3195abf 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -940,7 +940,6 @@ 'mac_bundle': 1, 'dependencies': [ 'chrome_dll', - 'interpose_dependency_shim', 'infoplist_strings_tool', ], 'sources': [ @@ -972,68 +971,24 @@ 'CHROMIUM_SHORT_NAME': '<(branding)', 'CHROMIUM_STRIP_SAVE_FILE': 'app/app.saves', 'INFOPLIST_FILE': 'app/helper-Info.plist', - # Stop the helper executable from being position-independent - # since that turns on ASLR, which breaks NaCl. ASLR breaks - # NaCl's ability to reliably allocate 1GB of address space for - # untrusted code to run in. - # See http://code.google.com/p/nativeclient/issues/detail?id=2043 - # TODO(mseaborn): Create a separate helper executable for NaCl - # so that the renderer process can still use ASLR. - 'OTHER_LDFLAGS!': ['-Wl,-pie'], }, - 'copies': [ - { - 'destination': '<(PRODUCT_DIR)/<(mac_product_name) Helper.app/Contents/MacOS', - 'files': [ - '<(PRODUCT_DIR)/libplugin_carbon_interpose.dylib', - ], - }, - ], - 'actions': [ + 'postbuilds': [ { - # Generate the InfoPlist.strings file - 'action_name': 'Generate InfoPlist.strings files', + # The helper doesn't have real localizations, it just has + # empty .lproj directories, which is enough to convince Cocoa + # that anything running out of the helper .app supports those + # languages. + 'postbuild_name': 'Make Empty Localizations', 'variables': { - 'tool_path': '<(PRODUCT_DIR)/infoplist_strings_tool', - # Unique dir to write to so the [lang].lproj/InfoPlist.strings - # for the main app and the helper app don't name collide. - 'output_path': '<(INTERMEDIATE_DIR)/helper_infoplist_strings', + 'locale_dirs': [ + '>!@(<(apply_locales_cmd) -d ZZLOCALE.lproj <(locales))', + ], }, - 'conditions': [ - [ 'branding == "Chrome"', { - 'variables': { - 'branding_name': 'google_chrome_strings', - }, - }, { # else branding!="Chrome" - 'variables': { - 'branding_name': 'chromium_strings', - }, - }], - ], - 'inputs': [ - '<(tool_path)', - '<(version_path)', - # TODO: remove this helper when we have loops in GYP - '>!@(<(apply_locales_cmd) \'<(grit_out_dir)/<(branding_name)_ZZLOCALE.pak\' <(locales))', - ], - 'outputs': [ - # TODO: remove this helper when we have loops in GYP - '>!@(<(apply_locales_cmd) -d \'<(output_path)/ZZLOCALE.lproj/InfoPlist.strings\' <(locales))', - ], 'action': [ - '<(tool_path)', - '-b', '<(branding_name)', - '-v', '<(version_path)', - '-g', '<(grit_out_dir)', - '-o', '<(output_path)', - '-t', 'helper', - '<@(locales)', + 'tools/build/mac/make_locale_dirs.sh', + '<@(locale_dirs)', ], - 'message': 'Generating the language InfoPlist.strings files', - 'process_outputs_as_mac_bundle_resources': 1, }, - ], - 'postbuilds': [ { # The framework (chrome_dll) defines its load-time path # (DYLIB_INSTALL_NAME_BASE) relative to the main executable @@ -1170,7 +1125,7 @@ 'xcode_settings': { 'DYLIB_COMPATIBILITY_VERSION': '<(version_mac_dylib)', 'DYLIB_CURRENT_VERSION': '<(version_mac_dylib)', - 'DYLIB_INSTALL_NAME_BASE': '@executable_path', + 'DYLIB_INSTALL_NAME_BASE': '@executable_path/../../..', }, 'postbuilds': [ { diff --git a/chrome/chrome_exe.gypi b/chrome/chrome_exe.gypi index 83b6e98..79d98f7 100644 --- a/chrome/chrome_exe.gypi +++ b/chrome/chrome_exe.gypi @@ -273,6 +273,7 @@ 'dependencies': [ 'helper_app', 'infoplist_strings_tool', + 'interpose_dependency_shim', 'chrome_manifest_bundle', ], 'mac_bundle_resources': [ @@ -327,6 +328,7 @@ 'destination': '<(PRODUCT_DIR)/<(mac_product_name).app/Contents/Versions/<(version_full)', 'files': [ '<(PRODUCT_DIR)/<(mac_product_name) Helper.app', + '<(PRODUCT_DIR)/libplugin_carbon_interpose.dylib', ], }, ], @@ -366,6 +368,38 @@ '<(version_full)' ], }, + { + # This postbuid step is responsible for creating the following + # helpers: + # + # For unofficial Chromium branding, Chromium Helper EH.app and + # Chromium Helper NP.app are created from Chromium Helper.app. + # For official Google Chrome branding, Google Chrome Helper + # EH.app and Google Chrome Helper NP.app are created from + # Google Chrome Helper.app. + # + # The EH helper is marked for an executable heap. The NP helper + # is marked for no PIE (ASLR). + # + # Normally, applications shipping as part of offical builds with + # Google Chrome branding have dsymutil (dwarf-with-dsym, + # mac_real_dsym) and dump_syms (mac_breakpad) run on them to + # produce a .dSYM bundle and a Breakpad .sym file. This is + # unnecessary for the "More Helpers" because they're identical + # to the original helper except for the bits in their Mach-O + # headers that change to enable or disable special features. + # Each .dSYM and Breakpad symbol file is identified by UUID + # stored in a Mach-O file's LC_UUID load command. Because the + # "More Helpers" share a UUID with the original helper, there's + # no need to run dsymutil or dump_syms again. All helpers can + # share the same .dSYM and Breakpad symbol file. + 'postbuild_name': 'Make More Helpers', + 'action': [ + 'tools/build/mac/make_more_helpers.sh', + '<(version_full)', + '<(mac_product_name)', + ], + }, ], # postbuilds }], ['OS=="linux"', { diff --git a/chrome/common/chrome_constants.cc b/chrome/common/chrome_constants.cc index 844ef3b..731739e 100644 --- a/chrome/common/chrome_constants.cc +++ b/chrome/common/chrome_constants.cc @@ -93,6 +93,12 @@ const FilePath::CharType kHelperProcessExecutablePath[] = FPL("chrome"); #if defined(OS_MACOSX) const FilePath::CharType kFrameworkName[] = FPL(PRODUCT_STRING " Framework.framework"); + +const char* const kHelperFlavorSuffixes[] = { + FPL("EH"), // Executable heap + FPL("NP"), // No PIE + NULL +}; #endif // OS_MACOSX const wchar_t kNaClAppName[] = L"nacl64"; diff --git a/chrome/common/chrome_constants.h b/chrome/common/chrome_constants.h index fb05878..ca0b456 100644 --- a/chrome/common/chrome_constants.h +++ b/chrome/common/chrome_constants.h @@ -26,7 +26,17 @@ extern const FilePath::CharType kBrowserProcessExecutablePathChromium[]; extern const FilePath::CharType kHelperProcessExecutablePathChromium[]; #if defined(OS_MACOSX) extern const FilePath::CharType kFrameworkName[]; -#endif + +// The helper .app bundle name and executable name may have one of these +// suffixes to identify specific features, or it may have no suffix at all. +// This is a NULL-terminated array of strings. If kHelperFlavorSuffixes +// contains "EN", "MF", and NULL, it indicates that if the normal helper is +// named Chromium Helper.app, helper executables could show up at any of +// Chromium Helper.app/Contents/MacOS/Chromium Helper, +// Chromium Helper EN.app/Contents/MacOS/Chromium Helper EN, and +// Chromium Helper MF.app/Contents/MacOS/Chromium Helper MF. +extern const FilePath::CharType* const kHelperFlavorSuffixes[]; +#endif // OS_MACOSX extern const wchar_t kBrowserAppName[]; #if defined(OS_WIN) extern const wchar_t kStatusTrayWindowClass[]; diff --git a/chrome/common/service_process_util.cc b/chrome/common/service_process_util.cc index 636b1ea..de9d013 100644 --- a/chrome/common/service_process_util.cc +++ b/chrome/common/service_process_util.cc @@ -252,7 +252,8 @@ IPC::ChannelHandle ServiceProcessState::GetServiceProcessChannel() { #endif // !OS_MACOSX void ServiceProcessState::CreateAutoRunCommandLine() { - FilePath exe_path = ChildProcessHost::GetChildPath(false); + FilePath exe_path = + ChildProcessHost::GetChildPath(ChildProcessHost::CHILD_NORMAL); if (exe_path.empty()) { NOTREACHED() << "Unable to get service process binary name."; } diff --git a/chrome/installer/mac/sign_app.sh.in b/chrome/installer/mac/sign_app.sh.in index 64ab064..3023806 100644 --- a/chrome/installer/mac/sign_app.sh.in +++ b/chrome/installer/mac/sign_app.sh.in @@ -1,6 +1,6 @@ #!/bin/bash -p -# Copyright (c) 2009 The Chromium Authors. All rights reserved. +# Copyright (c) 2011 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. @@ -42,12 +42,16 @@ versioned_dir="${app_path}/Contents/Versions/@VERSION@" browser_app="${app_path}" framework="${versioned_dir}/@MAC_PRODUCT_NAME@ Framework.framework" helper_app="${versioned_dir}/@MAC_PRODUCT_NAME@ Helper.app" +helper_eh_app="${versioned_dir}/@MAC_PRODUCT_NAME@ Helper EH.app" +helper_np_app="${versioned_dir}/@MAC_PRODUCT_NAME@ Helper NP.app" codesign -s "${codesign_id}" --keychain "${codesign_keychain}" \ "${browser_app}" --resource-rules "${browser_app_rules}" -# Verify everything. Check the framework and helper app to make sure that the +# Verify everything. Check the framework and helper apps to make sure that the # signatures are present and weren't altered by the signing process. codesign -v "${framework}" codesign -v "${helper_app}" +codesign -v "${helper_eh_app}" +codesign -v "${helper_np_app}" codesign -v "${browser_app}" diff --git a/chrome/installer/mac/sign_versioned_dir.sh.in b/chrome/installer/mac/sign_versioned_dir.sh.in index 4affd5e..82c7fb7 100644 --- a/chrome/installer/mac/sign_versioned_dir.sh.in +++ b/chrome/installer/mac/sign_versioned_dir.sh.in @@ -42,10 +42,18 @@ versioned_dir="${app_path}/Contents/Versions/@VERSION@" framework="${versioned_dir}/@MAC_PRODUCT_NAME@ Framework.framework" helper_app="${versioned_dir}/@MAC_PRODUCT_NAME@ Helper.app" +helper_eh_app="${versioned_dir}/@MAC_PRODUCT_NAME@ Helper EH.app" +helper_np_app="${versioned_dir}/@MAC_PRODUCT_NAME@ Helper NP.app" codesign -s "${codesign_id}" --keychain "${codesign_keychain}" "${framework}" codesign -s "${codesign_id}" --keychain "${codesign_keychain}" "${helper_app}" +codesign -s "${codesign_id}" --keychain "${codesign_keychain}" \ + "${helper_eh_app}" +codesign -s "${codesign_id}" --keychain "${codesign_keychain}" \ + "${helper_np_app}" # Verify everything. codesign -v "${framework}" codesign -v "${helper_app}" +codesign -v "${helper_eh_app}" +codesign -v "${helper_np_app}" diff --git a/chrome/service/service_utility_process_host.cc b/chrome/service/service_utility_process_host.cc index c633773..0c4e88c 100644 --- a/chrome/service/service_utility_process_host.cc +++ b/chrome/service/service_utility_process_host.cc @@ -116,7 +116,12 @@ bool ServiceUtilityProcessHost::StartProcess(bool no_sandbox, } FilePath ServiceUtilityProcessHost::GetUtilityProcessCmd() { - return GetChildPath(true); +#if defined(OS_LINUX) + int flags = CHILD_ALLOW_SELF; +#else + int flags = CHILD_NORMAL; +#endif + return GetChildPath(flags); } bool ServiceUtilityProcessHost::CanShutdown() { diff --git a/chrome/test/base/chrome_process_util.cc b/chrome/test/base/chrome_process_util.cc index 7bb93f7..4c046e2 100644 --- a/chrome/test/base/chrome_process_util.cc +++ b/chrome/test/base/chrome_process_util.cc @@ -4,8 +4,9 @@ #include "chrome/test/base/chrome_process_util.h" -#include <vector> #include <set> +#include <string> +#include <vector> #include "base/command_line.h" #include "base/process_util.h" @@ -59,11 +60,32 @@ const FilePath::CharType* GetRunningBrowserExecutableName() { return chrome::kBrowserProcessExecutableName; } -const FilePath::CharType* GetRunningHelperExecutableName() { +std::vector<FilePath::StringType> GetRunningHelperExecutableNames() { + FilePath::StringType name; const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); - if (cmd_line->HasSwitch(switches::kEnableChromiumBranding)) - return chrome::kHelperProcessExecutableNameChromium; - return chrome::kHelperProcessExecutableName; + if (cmd_line->HasSwitch(switches::kEnableChromiumBranding)) { + name = chrome::kHelperProcessExecutableNameChromium; + } else { + name = chrome::kHelperProcessExecutableName; + } + + std::vector<FilePath::StringType> names; + names.push_back(name); + +#if defined(OS_MACOSX) + // The helper might show up as these different flavors depending on the + // executable flags required. + for (const char* const* suffix = chrome::kHelperFlavorSuffixes; + *suffix; + ++suffix) { + std::string flavor_name(name); + flavor_name.append(1, ' '); + flavor_name.append(*suffix); + names.push_back(flavor_name); + } +#endif + + return names; } ChromeProcessList GetRunningChromeProcesses(base::ProcessId browser_pid) { @@ -95,12 +117,16 @@ ChromeProcessList GetRunningChromeProcesses(base::ProcessId browser_pid) { // on Linux via /proc/self/exe, so they end up with a different // name. We must collect them in a second pass. { - ChildProcessFilter filter(browser_pid); - base::NamedProcessIterator it(GetRunningHelperExecutableName(), &filter); - while (const base::ProcessEntry* process_entry = it.NextProcessEntry()) - result.push_back(process_entry->pid()); + std::vector<FilePath::StringType> names = GetRunningHelperExecutableNames(); + for (size_t i = 0; i < names.size(); ++i) { + FilePath::StringType name = names[i]; + ChildProcessFilter filter(browser_pid); + base::NamedProcessIterator it(name, &filter); + while (const base::ProcessEntry* process_entry = it.NextProcessEntry()) + result.push_back(process_entry->pid()); + } } -#endif // defined(OS_MACOSX) +#endif // defined(OS_POSIX) result.push_back(browser_pid); diff --git a/chrome/tools/build/mac/make_locale_dirs.sh b/chrome/tools/build/mac/make_locale_dirs.sh new file mode 100755 index 0000000..7636b15 --- /dev/null +++ b/chrome/tools/build/mac/make_locale_dirs.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# Copyright (c) 2011 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. + +# usage: make_locale_dirs.sh locale_dir [...] +# +# This script creates the Resources directory for the bundle being built by +# the Xcode target that calls it if the directory does not yet exist. It then +# changes to that directory and creates subdirectories for each locale_dir +# passed on the command line. +# +# This script is intended to create empty locale directories (.lproj) in a +# Cocoa .app bundle. The presence of these empty directories is sufficient to +# convince Cocoa that the application supports the named localization, even if +# an InfoPlist.strings file is not provided. Chrome uses these empty locale +# directoires for its helper executable bundles, which do not otherwise +# require any direct Cocoa locale support. + +set -eu + +if [[ ${#} -eq 0 ]]; then + echo "usage: ${0} locale_dir [...]" >& 2 + exit 1 +fi + +RESOURCES_DIR="${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +if [[ ! -d "${RESOURCES_DIR}" ]]; then + mkdir "${RESOURCES_DIR}" +fi + +cd "${RESOURCES_DIR}" + +for dir in "${@}"; do + if [[ ! -d "${dir}" ]]; then + mkdir "${dir}" + fi +done diff --git a/chrome/tools/build/mac/make_more_helpers.sh b/chrome/tools/build/mac/make_more_helpers.sh new file mode 100755 index 0000000..631c11b --- /dev/null +++ b/chrome/tools/build/mac/make_more_helpers.sh @@ -0,0 +1,89 @@ +#!/bin/bash + +# Copyright (c) 2011 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. + +# Usage: make_more_helpers.sh <version> <app_name> +# +# This script creates additional helper .app bundles for Chromium, based on +# the existing helper .app bundle, changing their Mach-O header's flags to +# enable and disable various features. Based on Chromium Helper.app, it will +# create Chromium Helper EH.app, which has the MH_NO_HEAP_EXECUTION bit +# cleared to support Chromium child processes that require an executable heap, +# and Chromium Helper NP.app, which has the MH_PIE bit cleared to support +# Chromium child processes that cannot tolerate ASLR. +# +# This script expects to be called from the chrome_exe target as a postbuild, +# and operates directly within the built-up browser app's versioned directory. +# +# Each helper is adjusted by giving it the proper bundle name, renaming the +# executable, adjusting several Info.plist keys, and changing the executable's +# Mach-O flags. + +set -eu + +make_helper() { + local versioned_dir="${1}" + local app_name="${2}" + local feature="${3}" + local flags="${4}" + + local helper_name="${app_name} Helper" + local helper_stem="${versioned_dir}/${helper_name}" + local original_helper="${helper_stem}.app" + if [[ ! -d "${original_helper}" ]]; then + echo "${0}: error: ${original_helper} is a required directory" >& 2 + exit 1 + fi + local original_helper_exe="${original_helper}/Contents/MacOS/${helper_name}" + if [[ ! -f "${original_helper_exe}" ]]; then + echo "${0}: error: ${original_helper_exe} is a required file" >& 2 + exit 1 + fi + + local feature_helper="${helper_stem} ${feature}.app" + + rsync -acC --delete --include '*.so' "${original_helper}/" "${feature_helper}" + + local helper_feature="${helper_name} ${feature}" + local helper_feature_exe="${feature_helper}/Contents/MacOS/${helper_feature}" + mv "${feature_helper}/Contents/MacOS/${helper_name}" "${helper_feature_exe}" + + local change_flags="\ +$(dirname "${0}")/../../../../build/mac/change_mach_o_flags.py" + "${change_flags}" ${flags} "${helper_feature_exe}" + + local feature_info="${feature_helper}/Contents/Info" + local feature_info_plist="${feature_info}.plist" + + defaults write "${feature_info}" "CFBundleDisplayName" "${helper_feature}" + defaults write "${feature_info}" "CFBundleExecutable" "${helper_feature}" + + cfbundleid="$(defaults read "${feature_info}" "CFBundleIdentifier")" + feature_cfbundleid="${cfbundleid}.${feature}" + defaults write "${feature_info}" "CFBundleIdentifier" "${feature_cfbundleid}" + + cfbundlename="$(defaults read "${feature_info}" "CFBundleName")" + feature_cfbundlename="${cfbundlename} ${feature}" + defaults write "${feature_info}" "CFBundleName" "${feature_cfbundlename}" + + # As usual, defaults might have put the plist into whatever format excites + # it, but Info.plists get converted back to the expected XML format. + plutil -convert xml1 "${feature_info_plist}" +} + +if [[ ${#} -ne 2 ]]; then + echo "usage: ${0} <version> <app_name>" >& 2 + exit 1 +fi + +VERSION="${1}" +APP_NAME="${2}" + +CONTENTS_DIR="${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}" +VERSIONED_DIR="${CONTENTS_DIR}/Versions" +CURRENT_VERSIONED_DIR="${VERSIONED_DIR}/${VERSION}" + +make_helper "${CURRENT_VERSIONED_DIR}" "${APP_NAME}" "EH" "--executable-heap" +make_helper "${CURRENT_VERSIONED_DIR}" "${APP_NAME}" "NP" "--no-pie" diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc index 73184dd..48f3a74 100644 --- a/content/browser/gpu/gpu_process_host.cc +++ b/content/browser/gpu/gpu_process_host.cc @@ -519,7 +519,14 @@ bool GpuProcessHost::LaunchGpuProcess() { CommandLine::StringType gpu_launcher = browser_command_line.GetSwitchValueNative(switches::kGpuLauncher); - FilePath exe_path = ChildProcessHost::GetChildPath(gpu_launcher.empty()); +#if defined(OS_LINUX) + int child_flags = gpu_launcher.empty() ? ChildProcessHost::CHILD_ALLOW_SELF : + ChildProcessHost::CHILD_NORMAL; +#else + int child_flags = ChildProcessHost::CHILD_NORMAL; +#endif + + FilePath exe_path = ChildProcessHost::GetChildPath(child_flags); if (exe_path.empty()) return false; diff --git a/content/browser/plugin_process_host.cc b/content/browser/plugin_process_host.cc index 722f046..c8fa8a4 100644 --- a/content/browser/plugin_process_host.cc +++ b/content/browser/plugin_process_host.cc @@ -175,7 +175,19 @@ bool PluginProcessHost::Init(const webkit::WebPluginInfo& info, const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); CommandLine::StringType plugin_launcher = browser_command_line.GetSwitchValueNative(switches::kPluginLauncher); - FilePath exe_path = GetChildPath(plugin_launcher.empty()); + +#if defined(OS_MACOSX) + // Run the plug-in process in a mode tolerant of heap execution without + // explicit mprotect calls. Some plug-ins still rely on this quaint and + // archaic "feature." See http://crbug.com/93551. + int flags = CHILD_ALLOW_HEAP_EXECUTION; +#elif defined(OS_LINUX) + int flags = plugin_launcher.empty() ? CHILD_ALLOW_SELF : CHILD_NORMAL; +#else + int flags = CHILD_NORMAL; +#endif + + FilePath exe_path = GetChildPath(flags); if (exe_path.empty()) return false; diff --git a/content/browser/ppapi_broker_process_host.cc b/content/browser/ppapi_broker_process_host.cc index 387f943..9f94a67 100644 --- a/content/browser/ppapi_broker_process_host.cc +++ b/content/browser/ppapi_broker_process_host.cc @@ -36,7 +36,14 @@ bool PpapiBrokerProcessHost::Init(const PepperPluginInfo& info) { CommandLine::StringType plugin_launcher = browser_command_line.GetSwitchValueNative(switches::kPpapiPluginLauncher); - FilePath exe_path = ChildProcessHost::GetChildPath(plugin_launcher.empty()); +#if defined(OS_LINUX) + int flags = plugin_launcher.empty() ? ChildProcessHost::CHILD_ALLOW_SELF : + ChildProcessHost::CHILD_NORMAL; +#else + int flags = ChildProcessHost::CHILD_NORMAL; +#endif + + FilePath exe_path = ChildProcessHost::GetChildPath(flags); if (exe_path.empty()) return false; diff --git a/content/browser/renderer_host/browser_render_process_host.cc b/content/browser/renderer_host/browser_render_process_host.cc index 256fb91..493ddc8 100644 --- a/content/browser/renderer_host/browser_render_process_host.cc +++ b/content/browser/renderer_host/browser_render_process_host.cc @@ -270,10 +270,16 @@ bool BrowserRenderProcessHost::Init(bool is_accessibility_enabled) { browser_command_line.GetSwitchValueNative(switches::kRendererCmdPrefix); #endif // defined(OS_POSIX) +#if defined(OS_LINUX) + int flags = renderer_prefix.empty() ? ChildProcessHost::CHILD_ALLOW_SELF : + ChildProcessHost::CHILD_NORMAL; +#else + int flags = ChildProcessHost::CHILD_NORMAL; +#endif + // Find the renderer before creating the channel so if this fails early we // return without creating the channel. - FilePath renderer_path = - ChildProcessHost::GetChildPath(renderer_prefix.empty()); + FilePath renderer_path = ChildProcessHost::GetChildPath(flags); if (renderer_path.empty()) return false; diff --git a/content/browser/utility_process_host.cc b/content/browser/utility_process_host.cc index 44a7ef5..c62ec3e 100644 --- a/content/browser/utility_process_host.cc +++ b/content/browser/utility_process_host.cc @@ -61,7 +61,12 @@ void UtilityProcessHost::EndBatchMode() { } FilePath UtilityProcessHost::GetUtilityProcessCmd() { - return GetChildPath(true); +#if defined(OS_LINUX) + int flags = CHILD_ALLOW_SELF; +#else + int flags = CHILD_NORMAL; +#endif + return GetChildPath(flags); } bool UtilityProcessHost::StartProcess() { diff --git a/content/browser/worker_host/worker_process_host.cc b/content/browser/worker_host/worker_process_host.cc index f51aed6..6e47140 100644 --- a/content/browser/worker_host/worker_process_host.cc +++ b/content/browser/worker_host/worker_process_host.cc @@ -118,7 +118,13 @@ bool WorkerProcessHost::Init(int render_process_id) { if (!CreateChannel()) return false; - FilePath exe_path = GetChildPath(true); +#if defined(OS_LINUX) + int flags = CHILD_ALLOW_SELF; +#else + int flags = CHILD_NORMAL; +#endif + + FilePath exe_path = GetChildPath(flags); if (exe_path.empty()) return false; diff --git a/content/common/child_process_host.cc b/content/common/child_process_host.cc index 6a14750..02a5bd6 100644 --- a/content/common/child_process_host.cc +++ b/content/common/child_process_host.cc @@ -6,6 +6,7 @@ #include "base/command_line.h" #include "base/file_path.h" +#include "base/logging.h" #include "base/metrics/histogram.h" #include "base/path_service.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" @@ -19,6 +20,52 @@ #include "base/linux_util.h" #endif // OS_LINUX +#if defined(OS_MACOSX) +namespace { + +// Given |path| identifying a Mac-style child process executable path, adjusts +// it to correspond to |feature|. For a child process path such as +// ".../Chromium Helper.app/Contents/MacOS/Chromium Helper", the transformed +// path for feature "NP" would be +// ".../Chromium Helper NP.app/Contents/MacOS/Chromium Helper NP". The new +// path is returned. +FilePath TransformPathForFeature(const FilePath& path, + const std::string& feature) { + std::string basename = path.BaseName().value(); + + FilePath macos_path = path.DirName(); + const char kMacOSName[] = "MacOS"; + DCHECK_EQ(kMacOSName, macos_path.BaseName().value()); + + FilePath contents_path = macos_path.DirName(); + const char kContentsName[] = "Contents"; + DCHECK_EQ(kContentsName, contents_path.BaseName().value()); + + FilePath helper_app_path = contents_path.DirName(); + const char kAppExtension[] = ".app"; + std::string basename_app = basename; + basename_app.append(kAppExtension); + DCHECK_EQ(basename_app, helper_app_path.BaseName().value()); + + FilePath root_path = helper_app_path.DirName(); + + std::string new_basename = basename; + new_basename.append(1, ' '); + new_basename.append(feature); + std::string new_basename_app = new_basename; + new_basename_app.append(kAppExtension); + + FilePath new_path = root_path.Append(new_basename_app) + .Append(kContentsName) + .Append(kMacOSName) + .Append(new_basename); + + return new_path; +} + +} // namespace +#endif // OS_MACOSX + ChildProcessHost::ChildProcessHost() : ALLOW_THIS_IN_INITIALIZER_LIST(listener_(this)), opening_channel_(false) { @@ -39,7 +86,7 @@ void ChildProcessHost::AddFilter(IPC::ChannelProxy::MessageFilter* filter) { } // static -FilePath ChildProcessHost::GetChildPath(bool allow_self) { +FilePath ChildProcessHost::GetChildPath(int flags) { FilePath child_path; child_path = CommandLine::ForCurrentProcess()->GetSwitchValuePath( @@ -53,13 +100,33 @@ FilePath ChildProcessHost::GetChildPath(bool allow_self) { // When running under Valgrind, forking /proc/self/exe ends up forking the // Valgrind executable, which then crashes. However, it's almost safe to // assume that the updates won't happen while testing with Valgrind tools. - if (allow_self && !RunningOnValgrind()) + if (flags & CHILD_ALLOW_SELF && !RunningOnValgrind()) return FilePath("/proc/self/exe"); #endif // On most platforms, the child executable is the same as the current // executable. PathService::Get(content::CHILD_PROCESS_EXE, &child_path); + +#if defined(OS_MACOSX) + DCHECK(!(flags & CHILD_NO_PIE && flags & CHILD_ALLOW_HEAP_EXECUTION)); + + // If needed, choose an executable with special flags set that inform the + // kernel to enable or disable specific optional process-wide features. + if (flags & CHILD_NO_PIE) { + // "NP" is "No PIE". This results in Chromium Helper NP.app or + // Google Chrome Helper NP.app. + child_path = TransformPathForFeature(child_path, "NP"); + } else if (flags & CHILD_ALLOW_HEAP_EXECUTION) { + // "EH" is "Executable Heap". A non-executable heap is only available to + // 32-bit processes on Mac OS X 10.7. Most code can and should run with a + // non-executable heap, but the "EH" feature is provided to allow code + // intolerant of a non-executable heap to work properly on 10.7. This + // results in Chromium Helper EH.app or Google Chrome Helper EH.app. + child_path = TransformPathForFeature(child_path, "EH"); + } +#endif + return child_path; } diff --git a/content/common/child_process_host.h b/content/common/child_process_host.h index 7fd7082..8637c18 100644 --- a/content/common/child_process_host.h +++ b/content/common/child_process_host.h @@ -33,6 +33,42 @@ class Message; class ChildProcessHost : public IPC::Channel::Listener, public IPC::Message::Sender { public: + + // These flags may be passed to GetChildPath in order to alter its behavior, + // causing it to return a child path more suited to a specific task. + enum { + // No special behavior requested. + CHILD_NORMAL = 0, + +#if defined(OS_LINUX) + // Indicates that the child execed after forking may be execced from + // /proc/self/exe rather than using the "real" app path. This prevents + // autoupdate from confusing us if it changes the file out from under us. + // You will generally want to set this on Linux, except when there is an + // override to the command line (for example, we're forking a renderer in + // gdb). In this case, you'd use GetChildPath to get the real executable + // file name, and then prepend the GDB command to the command line. + CHILD_ALLOW_SELF = 1 << 0, +#elif defined(OS_MACOSX) + + // Requests that the child run in a process that does not have the + // PIE (position-independent executable) bit set, effectively disabling + // ASLR. For process types that need to allocate a large contiguous + // region, ASLR may not leave a large enough "hole" for the purpose. This + // option should be used sparingly, and only when absolutely necessary. + // This option is currently incompatible with CHILD_ALLOW_HEAP_EXECUTION. + CHILD_NO_PIE = 1 << 1, + + // Requests that the child run in a process that does not protect the + // heap against execution. Normally, heap pages may be made executable + // with mprotect, so this mode should be used sparingly. It is intended + // for processes that may host plug-ins that expect an executable heap + // without having to call mprotect. This option is currently incompatible + // with CHILD_NO_PIE. + CHILD_ALLOW_HEAP_EXECUTION = 1 << 2, +#endif + }; + virtual ~ChildProcessHost(); // Returns the pathname to be used for a child process. If a subprocess @@ -40,16 +76,12 @@ class ChildProcessHost : public IPC::Channel::Listener, // the default child process pathname will be returned. On most platforms, // this will be the same as the currently-executing process. // - // The argument allow_self is used on Linux to indicate that we allow us to - // fork from /proc/self/exe rather than using the "real" app path. This - // prevents autoupdate from confusing us if it changes the file out from - // under us. You will generally want to set this to true, except when there - // is an override to the command line (for example, we're forking a renderer - // in gdb). In this case, you'd use GetChildPath to get the real executable - // file name, and then prepend the GDB command to the command line. + // The |flags| argument accepts one or more flags such as CHILD_ALLOW_SELF + // and CHILD_ALLOW_HEAP_EXECUTION as defined above. Pass only CHILD_NORMAL + // if none of these special behaviors are required. // // On failure, returns an empty FilePath. - static FilePath GetChildPath(bool allow_self); + static FilePath GetChildPath(int flags); #if defined(OS_WIN) // See comments in the cc file. This is a common hack needed for a process diff --git a/content/common/plugin_carbon_interpose_constants_mac.cc b/content/common/plugin_carbon_interpose_constants_mac.cc index 956d6fc..2ff5dd0 100644 --- a/content/common/plugin_carbon_interpose_constants_mac.cc +++ b/content/common/plugin_carbon_interpose_constants_mac.cc @@ -10,7 +10,7 @@ namespace plugin_interpose_strings { const char kDYLDInsertLibrariesKey[] = "DYLD_INSERT_LIBRARIES"; const char kInterposeLibraryPath[] = - "@executable_path/libplugin_carbon_interpose.dylib"; + "@executable_path/../../../libplugin_carbon_interpose.dylib"; } // namespace plugin_interpose_strings |