Chromium Code Reviews| 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. | |
|
M-A Ruel
2013/05/14 00:39:09
What's the use exactly? I'm asking because I'd lik
scottmg
2013/05/14 03:35:07
split_link.cc/exe are the replacement for link.exe
| |
| 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 |