OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include <windows.h> |
| 6 #include <shlwapi.h> |
| 7 |
| 8 #include <stdio.h> |
| 9 #include <stdlib.h> |
| 10 |
| 11 #include <algorithm> |
| 12 #include <iterator> |
| 13 #include <string> |
| 14 #include <vector> |
| 15 |
| 16 #ifndef SPLIT_LINK_SCRIPT_PATH |
| 17 #error SPLIT_LINK_SCRIPT_PATH must be defined as the path to "split_link.py". |
| 18 #endif |
| 19 |
| 20 #ifndef PYTHON_PATH |
| 21 #error PYTHON_PATH must be defined to be the path to the python binary. |
| 22 #endif |
| 23 |
| 24 #define WIDEN2(x) L ## x |
| 25 #define WIDEN(x) WIDEN2(x) |
| 26 #define WPYTHON_PATH WIDEN(PYTHON_PATH) |
| 27 #define WSPLIT_LINK_SCRIPT_PATH WIDEN(SPLIT_LINK_SCRIPT_PATH) |
| 28 |
| 29 using namespace std; |
| 30 |
| 31 // Don't use stderr for errors because VS has large buffers on them, leading |
| 32 // to confusing error output. |
| 33 static void Fatal(const wchar_t* msg) { |
| 34 wprintf(L"split_link fatal error: %s\n", msg); |
| 35 exit(1); |
| 36 } |
| 37 |
| 38 static wstring ErrorMessageToString(DWORD err) { |
| 39 wchar_t* msg_buf = NULL; |
| 40 DWORD rc = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | |
| 41 FORMAT_MESSAGE_FROM_SYSTEM, |
| 42 NULL, |
| 43 err, |
| 44 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
| 45 reinterpret_cast<LPTSTR>(&msg_buf), |
| 46 0, |
| 47 NULL); |
| 48 if (!rc) |
| 49 return L"unknown error"; |
| 50 wstring ret(msg_buf); |
| 51 LocalFree(msg_buf); |
| 52 return ret; |
| 53 } |
| 54 |
| 55 static void ArgvQuote(const std::wstring& argument, |
| 56 std::wstring* command_line) { |
| 57 // Don't quote unless we actually need to. |
| 58 if (!argument.empty() && |
| 59 argument.find_first_of(L" \t\n\v\"") == argument.npos) { |
| 60 command_line->append(argument); |
| 61 } else { |
| 62 command_line->push_back(L'"'); |
| 63 for (std::wstring::const_iterator it = argument.begin();; ++it) { |
| 64 int num_backslashes = 0; |
| 65 while (it != argument.end() && *it == L'\\') { |
| 66 ++it; |
| 67 ++num_backslashes; |
| 68 } |
| 69 if (it == argument.end()) { |
| 70 // Escape all backslashes, but let the terminating double quotation |
| 71 // mark we add below be interpreted as a metacharacter. |
| 72 command_line->append(num_backslashes * 2, L'\\'); |
| 73 break; |
| 74 } else if (*it == L'"') { |
| 75 // Escape all backslashes and the following double quotation mark. |
| 76 command_line->append(num_backslashes * 2 + 1, L'\\'); |
| 77 command_line->push_back(*it); |
| 78 } else { |
| 79 // Backslashes aren't special here. |
| 80 command_line->append(num_backslashes, L'\\'); |
| 81 command_line->push_back(*it); |
| 82 } |
| 83 } |
| 84 command_line->push_back(L'"'); |
| 85 } |
| 86 } |
| 87 |
| 88 // Does the opposite of CommandLineToArgvW. Suitable for CreateProcess, but |
| 89 // not for cmd.exe. |args| should include the program name as argv[0]. |
| 90 // See http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/e
veryone-quotes-arguments-the-wrong-way.aspx |
| 91 static wstring BuildCommandLine(const vector<wstring>& args) { |
| 92 std::wstring result; |
| 93 for (size_t i = 0; i < args.size(); ++i) { |
| 94 ArgvQuote(args[i], &result); |
| 95 if (i < args.size() - 1) { |
| 96 result += L" "; |
| 97 } |
| 98 } |
| 99 return result; |
| 100 } |
| 101 |
| 102 static void RunLinker(const vector<wstring>& prefix, const wchar_t* msg) { |
| 103 if (msg) { |
| 104 wprintf(L"split_link failed (%s), trying to fallback to standard link.\n", |
| 105 msg); |
| 106 wprintf(L"Original command line: %s\n", GetCommandLine()); |
| 107 fflush(stdout); |
| 108 } |
| 109 |
| 110 STARTUPINFO startup_info = { sizeof(STARTUPINFO) }; |
| 111 PROCESS_INFORMATION process_info; |
| 112 DWORD exit_code; |
| 113 |
| 114 GetStartupInfo(&startup_info); |
| 115 |
| 116 if (getenv("SPLIT_LINK_DEBUG")) { |
| 117 wprintf(L" original command line '%s'\n", GetCommandLine()); |
| 118 fflush(stdout); |
| 119 } |
| 120 |
| 121 int num_args; |
| 122 LPWSTR* args = CommandLineToArgvW(GetCommandLine(), &num_args); |
| 123 if (!args) |
| 124 Fatal(L"Couldn't parse command line."); |
| 125 vector<wstring> argv; |
| 126 argv.insert(argv.end(), prefix.begin(), prefix.end()); |
| 127 for (int i = 1; i < num_args; ++i) // Skip old argv[0]. |
| 128 argv.push_back(args[i]); |
| 129 LocalFree(args); |
| 130 |
| 131 wstring cmd = BuildCommandLine(argv); |
| 132 |
| 133 if (getenv("SPLIT_LINK_DEBUG")) { |
| 134 wprintf(L" running '%s'\n", cmd.c_str()); |
| 135 fflush(stdout); |
| 136 } |
| 137 if (!CreateProcess(NULL, |
| 138 reinterpret_cast<LPWSTR>(const_cast<wchar_t *>( |
| 139 cmd.c_str())), |
| 140 NULL, |
| 141 NULL, |
| 142 TRUE, |
| 143 0, |
| 144 NULL, |
| 145 NULL, |
| 146 &startup_info, &process_info)) { |
| 147 wstring error = ErrorMessageToString(GetLastError()); |
| 148 Fatal(error.c_str()); |
| 149 } |
| 150 CloseHandle(process_info.hThread); |
| 151 WaitForSingleObject(process_info.hProcess, INFINITE); |
| 152 GetExitCodeProcess(process_info.hProcess, &exit_code); |
| 153 CloseHandle(process_info.hProcess); |
| 154 exit(exit_code); |
| 155 } |
| 156 |
| 157 static void Fallback(const wchar_t* msg) { |
| 158 wchar_t original_link[1024]; |
| 159 DWORD type; |
| 160 DWORD size = sizeof(original_link); |
| 161 if (SHGetValue(HKEY_CURRENT_USER, |
| 162 L"Software\\Chromium\\split_link_installed", |
| 163 NULL, |
| 164 &type, |
| 165 original_link, |
| 166 &size) != ERROR_SUCCESS || type != REG_SZ) { |
| 167 Fatal(L"Couldn't retrieve linker location from " |
| 168 L"HKCU\\Software\\Chromium\\split_link_installed."); |
| 169 } |
| 170 if (getenv("SPLIT_LINK_DEBUG")) { |
| 171 wprintf(L" got original linker '%s'\n", original_link); |
| 172 fflush(stdout); |
| 173 } |
| 174 vector<wstring> link_binary; |
| 175 link_binary.push_back(original_link); |
| 176 RunLinker(link_binary, msg); |
| 177 } |
| 178 |
| 179 static void Fallback() { |
| 180 Fallback(NULL); |
| 181 } |
| 182 |
| 183 static unsigned char* SlurpFile(const wchar_t* path, size_t* length) { |
| 184 HANDLE file = CreateFile( |
| 185 path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, NULL); |
| 186 if (file == INVALID_HANDLE_VALUE) |
| 187 Fallback(L"couldn't open file"); |
| 188 LARGE_INTEGER file_size; |
| 189 if (!GetFileSizeEx(file, &file_size)) |
| 190 Fallback(L"couldn't get file size"); |
| 191 *length = static_cast<size_t>(file_size.QuadPart); |
| 192 unsigned char* buffer = static_cast<unsigned char*>(malloc(*length)); |
| 193 if (!ReadFile(file, buffer, *length, NULL, NULL)) |
| 194 Fallback(L"couldn't read file"); |
| 195 return buffer; |
| 196 } |
| 197 |
| 198 static bool SplitLinkRequested(const wchar_t* rsp_path) { |
| 199 size_t length; |
| 200 unsigned char* data = SlurpFile(rsp_path, &length); |
| 201 bool flag_found = false; |
| 202 if (data[0] == 0xff && data[1] == 0xfe) { |
| 203 // UTF-16LE |
| 204 wstring wide(reinterpret_cast<wchar_t*>(&data[2]), |
| 205 length / sizeof(wchar_t) - 1); |
| 206 flag_found = wide.find(L"/splitlink") != wide.npos; |
| 207 } else { |
| 208 string narrow(reinterpret_cast<char*>(data), length); |
| 209 flag_found = narrow.find("/splitlink") != narrow.npos; |
| 210 } |
| 211 free(data); |
| 212 return flag_found; |
| 213 } |
| 214 |
| 215 // If /splitlink is on the command line, delegate to split_link.py, otherwise |
| 216 // fallback to standard linker. |
| 217 int wmain(int argc, wchar_t** argv) { |
| 218 int rsp_file_index = -1; |
| 219 |
| 220 if (argc < 2) |
| 221 Fallback(); |
| 222 |
| 223 for (int i = 1; i < argc; ++i) { |
| 224 if (argv[i][0] == L'@') { |
| 225 rsp_file_index = i; |
| 226 break; |
| 227 } |
| 228 } |
| 229 |
| 230 if (rsp_file_index == -1) |
| 231 Fallback(L"couldn't find a response file in argv"); |
| 232 |
| 233 if (getenv("SPLIT_LINK_DEBUG")) { |
| 234 wstring backup_copy(&argv[rsp_file_index][1]); |
| 235 backup_copy += L".copy"; |
| 236 wchar_t buf[1024]; |
| 237 swprintf(buf, |
| 238 sizeof(buf), |
| 239 L"copy %s %s", |
| 240 &argv[rsp_file_index][1], |
| 241 backup_copy.c_str()); |
| 242 if (_wsystem(buf) == 0) |
| 243 wprintf(L"Saved original rsp as %s\n", backup_copy.c_str()); |
| 244 else |
| 245 wprintf(L"'%s' failed.", buf); |
| 246 } |
| 247 |
| 248 if (SplitLinkRequested(&argv[rsp_file_index][1])) { |
| 249 vector<wstring> link_binary; |
| 250 link_binary.push_back(WPYTHON_PATH); |
| 251 link_binary.push_back(WSPLIT_LINK_SCRIPT_PATH); |
| 252 RunLinker(link_binary, NULL); |
| 253 } |
| 254 |
| 255 // Otherwise, run regular linker silently. |
| 256 Fallback(); |
| 257 } |
OLD | NEW |