Skip to content

Commit

Permalink
Fix handling of unicode arguments on windows (#5)
Browse files Browse the repository at this point in the history
This commit tweaks the `as.exe` tool to handle passing of file paths containing non ascii characters. This is done in the following steps:
- on windows, we don't use the `argc` and `argv` from main, since they only support ascii (unlike on linux where they contain utf8 directly). We use instead the windows api `CommandLineToArgvW` to get the arguments in utf16 and then convert it to utf8.
- when parsing the arguments and creating `std::filesystem::path`, we cast the `optarg` char* to the C++20 type char8_t* so the path constructor knows the string is in utf8 encoding.
- when creating the process, we convert the args from u8 to utf16 and use the wide char version of `CreateProcess`
  • Loading branch information
BrzVlad authored Sep 19, 2023
1 parent b38b5ba commit 83a894f
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 6 deletions.
5 changes: 3 additions & 2 deletions src/gas/gas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,8 @@ Gas::ParseArgsResult Gas::parse_arguments (int argc, char **argv, std::unique_pt
// Ignore the arch hack parameter
const char *ret = strstr (optarg, Constants::arch_hack_param);
if (ret == nullptr || ret != optarg) {
input_files.emplace_back (optarg);
// char8_t* cast treats path string as utf8
input_files.emplace_back (reinterpret_cast<char8_t*>(optarg));
}
}
break;
Expand All @@ -267,7 +268,7 @@ Gas::ParseArgsResult Gas::parse_arguments (int argc, char **argv, std::unique_pt
break;

case OPTION_O:
_gas_output_file = optarg;
_gas_output_file = reinterpret_cast<char8_t*>(optarg);
break;

case OPTION_MFPU:
Expand Down
2 changes: 2 additions & 0 deletions src/gas/gas.hh
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ namespace xamarin::android::gas
~Gas ()
{}

void get_command_line (int &argc, char **&argv);

int run (int argc, char **argv);

const std::string& program_name () const noexcept
Expand Down
4 changes: 4 additions & 0 deletions src/gas/gas.posix.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

using namespace xamarin::android::gas;

void Gas::get_command_line ([[maybe_unused]] int &argc, [[maybe_unused]] char **&argv)
{
}

void Gas::determine_program_dir ([[maybe_unused]] int argc, char **argv)
{
fs::path program_path { argv[0] };
Expand Down
14 changes: 14 additions & 0 deletions src/gas/gas.windows.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@

using namespace xamarin::android::gas;

void Gas::get_command_line (int &argc, char **&argv)
{
LPWSTR *argvw = CommandLineToArgvW (GetCommandLineW (), &argc);
argv = new char*[argc + 1];

for (int i = 0; i < argc; i++) {
int size = WideCharToMultiByte (CP_UTF8, 0, argvw [i], -1, NULL, 0, NULL, NULL);
argv [i] = new char [size];
WideCharToMultiByte (CP_UTF8, 0, argvw [i], -1, argv [i], size, NULL, NULL);
}

argv [argc] = NULL;
}

void Gas::determine_program_dir ([[maybe_unused]] int argc, [[maybe_unused]] char **argv)
{
TCHAR buffer[MAX_PATH + 1]{};
Expand Down
2 changes: 2 additions & 0 deletions src/gas/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ xamarin::android::gas::Gas app;

int main (int argc, char **argv)
{
// On windows this obtains the utf8 version of args
app.get_command_line (argc, argv);
// TODO: handle exceptions here (use backward for stacktrace perhaps?)
return app.run (argc, argv);
}
19 changes: 15 additions & 4 deletions src/gas/process.windows.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,22 @@ int Process::run (bool print_command_line)
args.append (escape_argument (a));
}

int size = MultiByteToWideChar (CP_UTF8, 0, args.c_str (), -1, NULL , 0);
wchar_t* wargs = new wchar_t [size];
MultiByteToWideChar (CP_UTF8, 0, args.c_str (), -1, wargs, size);

size = MultiByteToWideChar (CP_UTF8, 0, binary.c_str (), -1, NULL , 0);
wchar_t* wbinary = new wchar_t [size];
MultiByteToWideChar (CP_UTF8, 0, binary.c_str (), -1, wbinary, size);

PROCESS_INFORMATION pi {};
STARTUPINFO si {};
STARTUPINFOW si {};
si.cb = sizeof(si);

DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT;
BOOL success = CreateProcess (
binary.c_str (),
const_cast<LPSTR>(args.c_str ()),
BOOL success = CreateProcessW (
wbinary,
wargs,
nullptr, // process security attributes
nullptr, // primary thread security attributes
TRUE, // inherit handles
Expand All @@ -76,6 +84,9 @@ int Process::run (bool print_command_line)
&pi
);

delete[] wargs;
delete[] wbinary;

if (!success) {
return Constants::wrapper_exec_failed_error_code;
}
Expand Down

0 comments on commit 83a894f

Please sign in to comment.