2 * Copyright (c) 2011 The Native Client Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
9 #include "redirector.h"
11 #pragma comment(linker, "/entry:entry")
13 #define FILE_NOT_FOUND_ERROR_MESSAGE "Can not find filename to execute!\r\n"
16 * The toolchain redirector reads it's name and invokes the appropriate binary
17 * from a location with cygwin1.dll nearby so that all cygwin-oriented
18 * toolchain programs can start (this works for Cygwin version >= 1.7).
20 * Supports arbitrary unicode names in it's base directory name.
22 * The executable must be placed in nacl/bin or nacl64/bin or just bin (in the
23 * latter case the name of the binary should be prefixed with nacl- or nacl64-)
25 * The binary should be built by visual studio (not cygwin) since when it starts
26 * cygwin has not been located yet.
28 * Use the following commands:
29 * cl /c /O2 /GS- redirector.c
30 * link /subsystem:console /MERGE:.rdata=.text /NODEFAULTLIB
31 * redirector.obj kernel32.lib user32.lib
35 * Wraps GetModuleFileNameW to always succeed in finding a buffer of
39 int get_module_name_safely(wchar_t **oldpath) {
46 GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(wchar_t) * length);
47 if (result == NULL) return 0;
48 real_size = GetModuleFileNameW(NULL, result, length);
50 HeapFree(GetProcessHeap(), 0, result);
53 if (real_size < length - 1) {
59 HeapFree(GetProcessHeap(), 0, result);
65 const wchar_t* find_last_slash_or_backslash_delimiter(const wchar_t *start) {
66 const wchar_t *delimiter = start + lstrlenW(start);
67 while (delimiter > start && *delimiter != L'/' && *delimiter != L'\\')
73 wchar_t* find_program_arguments() {
76 arguments = GetCommandLineW();
77 if (!arguments) return NULL;
78 if (arguments[0] == L'\"') {
80 while (arguments[0] && arguments[0] != L'\"') {
81 if (arguments[0] == L'\\' &&
82 (arguments[1] == L'\"' || arguments[1] == L'\\'))
88 while (arguments[0] && arguments[0] != L' ') arguments++;
89 if (arguments[0]) while (arguments[1] == ' ') arguments++;
94 wchar_t reduce_wchar(wchar_t c) {
97 return (wchar_t)CharLowerW((LPWSTR)c);
101 * Returns whether path ends with redirect
104 int check_path (const wchar_t *path, const wchar_t *redirect) {
105 int path_len = lstrlenW(path);
106 int redirect_len = lstrlenW(redirect);
109 if (path_len < redirect_len)
111 path_offset = path_len - redirect_len;
112 for (i = 0; i < redirect_len; i++) {
113 if (reduce_wchar(path[i+path_offset]) != reduce_wchar(redirect[i]))
120 int is_driver(const wchar_t *option) {
121 return lstrcmpW(option, L"-m32") == 0 || lstrcmpW(option, L"-m64") == 0;
125 void println_redirect(const redirect_t *redirect) {
129 output = GetStdHandle(STD_OUTPUT_HANDLE);
130 for (str = redirect->from; *str; ++str)
131 WriteFile(output, str, 1, &tmp, NULL);
132 WriteFile(output, L"|", 1, &tmp, NULL);
133 for (str = redirect->to; *str; ++str)
134 WriteFile(output, str, 1, &tmp, NULL);
135 WriteFile(output, L"|", 1, &tmp, NULL);
136 for (str = redirect->args; *str; ++str)
137 WriteFile(output, str, 1, &tmp, NULL);
138 WriteFile(output, L"\n", 1, &tmp, NULL);
142 wchar_t *newpath = NULL, *oldpath = NULL;
143 const wchar_t *cmdline, *arguments, *selector;
151 int n_redirects = sizeof(redirects)/sizeof(redirect_t);
152 static STARTUPINFOW si = {sizeof(STARTUPINFOW), 0};
153 static PROCESS_INFORMATION pi;
155 length = get_module_name_safely(&oldpath);
156 if (length == 0) goto ShowErrorMessage;
157 /* If redirector is called as "redirector.exe", dump redirector table. */
158 if (check_path(oldpath, L"redirector.exe")) {
159 output = GetStdHandle(STD_OUTPUT_HANDLE);
160 for (redirect_index = 0;
161 redirect_index < n_redirects;
163 println_redirect(redirects + redirect_index);
167 for (redirect_index = 0;
168 redirect_index < n_redirects;
170 if (check_path(oldpath, redirects[redirect_index].from))
173 if (redirect_index >= n_redirects) goto ShowErrorMessage;
174 newpath = HeapAlloc(GetProcessHeap(), 0, sizeof(wchar_t)*(length + 64));
175 if (!newpath) goto ShowErrorMessage;
176 length_from = lstrlenW(redirects[redirect_index].from);
177 length_to = lstrlenW(redirects[redirect_index].to);
178 lstrcpynW(newpath, oldpath, length - length_from + 1);
179 lstrcpyW(newpath + length - length_from, redirects[redirect_index].to);
181 selector = redirects[redirect_index].args;
182 cmdline = redirects[redirect_index].to;
183 if (!is_driver(selector)) {
184 cmdline = find_last_slash_or_backslash_delimiter (cmdline);
188 HeapFree(GetProcessHeap(), 0, oldpath);
190 arguments = find_program_arguments();
191 if (!arguments) goto ShowErrorMessage;
192 length = lstrlenW(cmdline);
193 done = lstrlenW(selector);
194 oldpath = HeapAlloc(GetProcessHeap(), 0,
195 (lstrlenW(arguments) + length + done + 2) * sizeof(wchar_t));
196 if (!oldpath) goto ShowErrorMessage;
197 lstrcpyW(oldpath, cmdline);
199 * cmdline == newpath means it's some kind of gcc driver and we need to
200 * handle this case specially: we need to put the -V option to be the first.
202 if (is_driver(selector) && arguments[1] == L'-' && arguments[2] == L'V') {
203 oldpath[length++] = (arguments++)[0];
204 oldpath[length++] = (arguments++)[0];
205 oldpath[length++] = (arguments++)[0];
206 if (arguments[0] == L' ')
207 while (arguments[1] == ' ')
209 if (arguments[0] != 0) {
210 oldpath[length++] = (arguments++)[0];
211 while (arguments[0] && arguments[0] != L' ')
212 oldpath[length++] = (arguments++)[0];
218 lstrcpyW(oldpath + length, L" ");
219 lstrcpyW(oldpath + length + 1, selector);
220 lstrcpyW(oldpath + length + done + 1, arguments);
221 if (CreateProcessW(newpath, oldpath, NULL, NULL, TRUE,
222 CREATE_UNICODE_ENVIRONMENT, NULL, NULL, &si, &pi) != 0) {
223 WaitForSingleObject(pi.hProcess, INFINITE);
224 HeapFree(GetProcessHeap(), 0, newpath);
225 HeapFree(GetProcessHeap(), 0, oldpath);
226 if (GetExitCodeProcess(pi.hProcess, &done)) {
227 CloseHandle(pi.hProcess);
228 CloseHandle(pi.hThread);
231 CloseHandle(pi.hProcess);
232 CloseHandle(pi.hThread);
235 if (newpath) HeapFree(GetProcessHeap(), 0, newpath);
236 if (oldpath) HeapFree(GetProcessHeap(), 0, oldpath);
237 WriteFile(GetStdHandle(STD_ERROR_HANDLE), FILE_NOT_FOUND_ERROR_MESSAGE,
238 sizeof(FILE_NOT_FOUND_ERROR_MESSAGE) - 1, &length, NULL);