| Index: tools/win/split_link/split_link.cc
|
| diff --git a/tools/win/split_link/split_link.cc b/tools/win/split_link/split_link.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..639f1c55eac771b38db5f146c3e910206c390c9a
|
| --- /dev/null
|
| +++ b/tools/win/split_link/split_link.cc
|
| @@ -0,0 +1,257 @@
|
| +// 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>
|
| +#include <shlwapi.h>
|
| +
|
| +#include <stdio.h>
|
| +#include <stdlib.h>
|
| +
|
| +#include <algorithm>
|
| +#include <iterator>
|
| +#include <string>
|
| +#include <vector>
|
| +
|
| +#ifndef SPLIT_LINK_SCRIPT_PATH
|
| +#error SPLIT_LINK_SCRIPT_PATH must be defined as the path to "split_link.py".
|
| +#endif
|
| +
|
| +#ifndef PYTHON_PATH
|
| +#error PYTHON_PATH must be defined to be the path to the python binary.
|
| +#endif
|
| +
|
| +#define WIDEN2(x) L ## x
|
| +#define WIDEN(x) WIDEN2(x)
|
| +#define WPYTHON_PATH WIDEN(PYTHON_PATH)
|
| +#define WSPLIT_LINK_SCRIPT_PATH WIDEN(SPLIT_LINK_SCRIPT_PATH)
|
| +
|
| +using namespace std;
|
| +
|
| +// Don't use stderr for errors because VS has large buffers on them, leading
|
| +// to confusing error output.
|
| +static void Fatal(const wchar_t* msg) {
|
| + wprintf(L"split_link fatal error: %s\n", msg);
|
| + exit(1);
|
| +}
|
| +
|
| +static wstring ErrorMessageToString(DWORD err) {
|
| + wchar_t* msg_buf = NULL;
|
| + DWORD rc = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
| + FORMAT_MESSAGE_FROM_SYSTEM,
|
| + NULL,
|
| + err,
|
| + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
| + reinterpret_cast<LPTSTR>(&msg_buf),
|
| + 0,
|
| + NULL);
|
| + if (!rc)
|
| + return L"unknown error";
|
| + wstring ret(msg_buf);
|
| + LocalFree(msg_buf);
|
| + return ret;
|
| +}
|
| +
|
| +static void ArgvQuote(const std::wstring& argument,
|
| + std::wstring* command_line) {
|
| + // Don't quote unless we actually need to.
|
| + if (!argument.empty() &&
|
| + argument.find_first_of(L" \t\n\v\"") == argument.npos) {
|
| + command_line->append(argument);
|
| + } else {
|
| + command_line->push_back(L'"');
|
| + for (std::wstring::const_iterator it = argument.begin();; ++it) {
|
| + int num_backslashes = 0;
|
| + while (it != argument.end() && *it == L'\\') {
|
| + ++it;
|
| + ++num_backslashes;
|
| + }
|
| + if (it == argument.end()) {
|
| + // Escape all backslashes, but let the terminating double quotation
|
| + // mark we add below be interpreted as a metacharacter.
|
| + command_line->append(num_backslashes * 2, L'\\');
|
| + break;
|
| + } else if (*it == L'"') {
|
| + // Escape all backslashes and the following double quotation mark.
|
| + command_line->append(num_backslashes * 2 + 1, L'\\');
|
| + command_line->push_back(*it);
|
| + } else {
|
| + // Backslashes aren't special here.
|
| + command_line->append(num_backslashes, L'\\');
|
| + command_line->push_back(*it);
|
| + }
|
| + }
|
| + command_line->push_back(L'"');
|
| + }
|
| +}
|
| +
|
| +// Does the opposite of CommandLineToArgvW. Suitable for CreateProcess, but
|
| +// not for cmd.exe. |args| should include the program name as argv[0].
|
| +// See http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
|
| +static wstring BuildCommandLine(const vector<wstring>& args) {
|
| + std::wstring result;
|
| + for (size_t i = 0; i < args.size(); ++i) {
|
| + ArgvQuote(args[i], &result);
|
| + if (i < args.size() - 1) {
|
| + result += L" ";
|
| + }
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +static void RunLinker(const vector<wstring>& prefix, const wchar_t* msg) {
|
| + if (msg) {
|
| + wprintf(L"split_link failed (%s), trying to fallback to standard link.\n",
|
| + msg);
|
| + wprintf(L"Original command line: %s\n", GetCommandLine());
|
| + fflush(stdout);
|
| + }
|
| +
|
| + STARTUPINFO startup_info = { sizeof(STARTUPINFO) };
|
| + PROCESS_INFORMATION process_info;
|
| + DWORD exit_code;
|
| +
|
| + GetStartupInfo(&startup_info);
|
| +
|
| + if (getenv("SPLIT_LINK_DEBUG")) {
|
| + wprintf(L" original command line '%s'\n", GetCommandLine());
|
| + fflush(stdout);
|
| + }
|
| +
|
| + int num_args;
|
| + LPWSTR* args = CommandLineToArgvW(GetCommandLine(), &num_args);
|
| + if (!args)
|
| + Fatal(L"Couldn't parse command line.");
|
| + vector<wstring> argv;
|
| + argv.insert(argv.end(), prefix.begin(), prefix.end());
|
| + for (int i = 1; i < num_args; ++i) // Skip old argv[0].
|
| + argv.push_back(args[i]);
|
| + LocalFree(args);
|
| +
|
| + wstring cmd = BuildCommandLine(argv);
|
| +
|
| + if (getenv("SPLIT_LINK_DEBUG")) {
|
| + wprintf(L" running '%s'\n", cmd.c_str());
|
| + fflush(stdout);
|
| + }
|
| + if (!CreateProcess(NULL,
|
| + reinterpret_cast<LPWSTR>(const_cast<wchar_t *>(
|
| + cmd.c_str())),
|
| + NULL,
|
| + NULL,
|
| + TRUE,
|
| + 0,
|
| + NULL,
|
| + NULL,
|
| + &startup_info, &process_info)) {
|
| + wstring error = ErrorMessageToString(GetLastError());
|
| + Fatal(error.c_str());
|
| + }
|
| + CloseHandle(process_info.hThread);
|
| + WaitForSingleObject(process_info.hProcess, INFINITE);
|
| + GetExitCodeProcess(process_info.hProcess, &exit_code);
|
| + CloseHandle(process_info.hProcess);
|
| + exit(exit_code);
|
| +}
|
| +
|
| +static void Fallback(const wchar_t* msg) {
|
| + wchar_t original_link[1024];
|
| + DWORD type;
|
| + DWORD size = sizeof(original_link);
|
| + if (SHGetValue(HKEY_CURRENT_USER,
|
| + L"Software\\Chromium\\split_link_installed",
|
| + NULL,
|
| + &type,
|
| + original_link,
|
| + &size) != ERROR_SUCCESS || type != REG_SZ) {
|
| + Fatal(L"Couldn't retrieve linker location from "
|
| + L"HKCU\\Software\\Chromium\\split_link_installed.");
|
| + }
|
| + if (getenv("SPLIT_LINK_DEBUG")) {
|
| + wprintf(L" got original linker '%s'\n", original_link);
|
| + fflush(stdout);
|
| + }
|
| + vector<wstring> link_binary;
|
| + link_binary.push_back(original_link);
|
| + RunLinker(link_binary, msg);
|
| +}
|
| +
|
| +static void Fallback() {
|
| + Fallback(NULL);
|
| +}
|
| +
|
| +static unsigned char* SlurpFile(const wchar_t* path, size_t* length) {
|
| + HANDLE file = CreateFile(
|
| + path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, NULL);
|
| + if (file == INVALID_HANDLE_VALUE)
|
| + Fallback(L"couldn't open file");
|
| + LARGE_INTEGER file_size;
|
| + if (!GetFileSizeEx(file, &file_size))
|
| + Fallback(L"couldn't get file size");
|
| + *length = static_cast<size_t>(file_size.QuadPart);
|
| + unsigned char* buffer = static_cast<unsigned char*>(malloc(*length));
|
| + if (!ReadFile(file, buffer, *length, NULL, NULL))
|
| + Fallback(L"couldn't read file");
|
| + return buffer;
|
| +}
|
| +
|
| +static bool SplitLinkRequested(const wchar_t* rsp_path) {
|
| + size_t length;
|
| + unsigned char* data = SlurpFile(rsp_path, &length);
|
| + bool flag_found = false;
|
| + if (data[0] == 0xff && data[1] == 0xfe) {
|
| + // UTF-16LE
|
| + wstring wide(reinterpret_cast<wchar_t*>(&data[2]),
|
| + length / sizeof(wchar_t) - 1);
|
| + flag_found = wide.find(L"/splitlink") != wide.npos;
|
| + } else {
|
| + string narrow(reinterpret_cast<char*>(data), length);
|
| + flag_found = narrow.find("/splitlink") != narrow.npos;
|
| + }
|
| + free(data);
|
| + return flag_found;
|
| +}
|
| +
|
| +// If /splitlink is on the command line, delegate to split_link.py, otherwise
|
| +// fallback to standard linker.
|
| +int wmain(int argc, wchar_t** argv) {
|
| + int rsp_file_index = -1;
|
| +
|
| + if (argc < 2)
|
| + Fallback();
|
| +
|
| + for (int i = 1; i < argc; ++i) {
|
| + if (argv[i][0] == L'@') {
|
| + rsp_file_index = i;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + if (rsp_file_index == -1)
|
| + Fallback(L"couldn't find a response file in argv");
|
| +
|
| + if (getenv("SPLIT_LINK_DEBUG")) {
|
| + wstring backup_copy(&argv[rsp_file_index][1]);
|
| + backup_copy += L".copy";
|
| + wchar_t buf[1024];
|
| + swprintf(buf,
|
| + sizeof(buf),
|
| + L"copy %s %s",
|
| + &argv[rsp_file_index][1],
|
| + backup_copy.c_str());
|
| + if (_wsystem(buf) == 0)
|
| + wprintf(L"Saved original rsp as %s\n", backup_copy.c_str());
|
| + else
|
| + wprintf(L"'%s' failed.", buf);
|
| + }
|
| +
|
| + if (SplitLinkRequested(&argv[rsp_file_index][1])) {
|
| + vector<wstring> link_binary;
|
| + link_binary.push_back(WPYTHON_PATH);
|
| + link_binary.push_back(WSPLIT_LINK_SCRIPT_PATH);
|
| + RunLinker(link_binary, NULL);
|
| + }
|
| +
|
| + // Otherwise, run regular linker silently.
|
| + Fallback();
|
| +}
|
|
|