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.
16 #ifndef SPLIT_LINK_SCRIPT_PATH
17 #error SPLIT_LINK_SCRIPT_PATH must be defined as the path to "split_link.py".
21 #error PYTHON_PATH must be defined to be the path to the python binary.
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)
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
);
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
,
44 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
45 reinterpret_cast<LPTSTR
>(&msg_buf
),
49 return L
"unknown error";
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
);
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
'\\') {
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
'\\');
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
);
79 // Backslashes aren't special here.
80 command_line
->append(num_backslashes
, L
'\\');
81 command_line
->push_back(*it
);
84 command_line
->push_back(L
'"');
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/everyone-quotes-arguments-the-wrong-way.aspx
91 static wstring
BuildCommandLine(const vector
<wstring
>& args
) {
93 for (size_t i
= 0; i
< args
.size(); ++i
) {
94 ArgvQuote(args
[i
], &result
);
95 if (i
< args
.size() - 1) {
102 static void RunLinker(const vector
<wstring
>& prefix
, const wchar_t* msg
) {
104 wprintf(L
"split_link failed (%s), trying to fallback to standard link.\n",
106 wprintf(L
"Original command line: %s\n", GetCommandLine());
110 STARTUPINFO startup_info
= { sizeof(STARTUPINFO
) };
111 PROCESS_INFORMATION process_info
;
114 GetStartupInfo(&startup_info
);
116 if (getenv("SPLIT_LINK_DEBUG")) {
117 wprintf(L
" original command line '%s'\n", GetCommandLine());
122 LPWSTR
* args
= CommandLineToArgvW(GetCommandLine(), &num_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
]);
131 wstring cmd
= BuildCommandLine(argv
);
133 if (getenv("SPLIT_LINK_DEBUG")) {
134 wprintf(L
" running '%s'\n", cmd
.c_str());
137 if (!CreateProcess(NULL
,
145 &startup_info
, &process_info
)) {
146 wstring error
= ErrorMessageToString(GetLastError());
147 Fatal(error
.c_str());
149 CloseHandle(process_info
.hThread
);
150 WaitForSingleObject(process_info
.hProcess
, INFINITE
);
151 GetExitCodeProcess(process_info
.hProcess
, &exit_code
);
152 CloseHandle(process_info
.hProcess
);
156 static void Fallback(const wchar_t* msg
) {
157 wchar_t original_link
[1024];
159 DWORD size
= sizeof(original_link
);
160 if (SHGetValue(HKEY_CURRENT_USER
,
161 L
"Software\\Chromium\\split_link_installed",
165 &size
) != ERROR_SUCCESS
|| type
!= REG_SZ
) {
166 Fatal(L
"Couldn't retrieve linker location from "
167 L
"HKCU\\Software\\Chromium\\split_link_installed.");
169 if (getenv("SPLIT_LINK_DEBUG")) {
170 wprintf(L
" got original linker '%s'\n", original_link
);
173 vector
<wstring
> link_binary
;
174 link_binary
.push_back(original_link
);
175 RunLinker(link_binary
, msg
);
178 static void Fallback() {
182 static unsigned char* SlurpFile(const wchar_t* path
, size_t* length
) {
183 HANDLE file
= CreateFile(
184 path
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
185 if (file
== INVALID_HANDLE_VALUE
)
186 Fallback(L
"couldn't open file");
187 LARGE_INTEGER file_size
;
188 if (!GetFileSizeEx(file
, &file_size
))
189 Fallback(L
"couldn't get file size");
190 *length
= static_cast<size_t>(file_size
.QuadPart
);
191 unsigned char* buffer
= static_cast<unsigned char*>(malloc(*length
));
192 DWORD bytes_read
= 0;
193 if (!ReadFile(file
, buffer
, *length
, &bytes_read
, NULL
))
194 Fallback(L
"couldn't read file");
198 static bool SplitLinkRequested(const wchar_t* rsp_path
) {
200 unsigned char* data
= SlurpFile(rsp_path
, &length
);
201 bool flag_found
= false;
202 if (data
[0] == 0xff && data
[1] == 0xfe) {
204 wstring
wide(reinterpret_cast<wchar_t*>(&data
[2]),
205 length
/ sizeof(wchar_t) - 1);
206 flag_found
= wide
.find(L
"/splitlink") != wide
.npos
;
208 string
narrow(reinterpret_cast<char*>(data
), length
);
209 flag_found
= narrow
.find("/splitlink") != narrow
.npos
;
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;
223 for (int i
= 1; i
< argc
; ++i
) {
224 if (argv
[i
][0] == L
'@') {
230 if (rsp_file_index
== -1)
231 Fallback(L
"couldn't find a response file in argv");
233 if (getenv("SPLIT_LINK_DEBUG")) {
234 wstring
backup_copy(&argv
[rsp_file_index
][1]);
235 backup_copy
+= L
".copy";
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());
245 wprintf(L
"'%s' failed.", buf
);
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
);
255 // Otherwise, run regular linker silently.