std.os.execvpeZ_expandArg0: fix not restoring argv[0]
This function expands argv[0] into the absolute path resolved with PATH environment variable before making the execve syscall. However, in case the execve fails, e.g. with ENOENT, it did not restore argv to how it was before it was passed in. This resulted in the caller performing an invalid free. This commit also adds verbose debug info when native system C compiler detection fails. See #4521.master
parent
dca19b6757
commit
0cd89e9176
|
@ -955,6 +955,7 @@ pub const Arg0Expand = enum {
|
||||||
|
|
||||||
/// Like `execvpeZ` except if `arg0_expand` is `.expand`, then `argv` is mutable,
|
/// Like `execvpeZ` except if `arg0_expand` is `.expand`, then `argv` is mutable,
|
||||||
/// and `argv[0]` is expanded to be the same absolute path that is passed to the execve syscall.
|
/// and `argv[0]` is expanded to be the same absolute path that is passed to the execve syscall.
|
||||||
|
/// If this function returns with an error, `argv[0]` will be restored to the value it was when it was passed in.
|
||||||
pub fn execvpeZ_expandArg0(
|
pub fn execvpeZ_expandArg0(
|
||||||
comptime arg0_expand: Arg0Expand,
|
comptime arg0_expand: Arg0Expand,
|
||||||
file: [*:0]const u8,
|
file: [*:0]const u8,
|
||||||
|
@ -972,6 +973,14 @@ pub fn execvpeZ_expandArg0(
|
||||||
var it = mem.tokenize(PATH, ":");
|
var it = mem.tokenize(PATH, ":");
|
||||||
var seen_eacces = false;
|
var seen_eacces = false;
|
||||||
var err: ExecveError = undefined;
|
var err: ExecveError = undefined;
|
||||||
|
|
||||||
|
// In case of expanding arg0 we must put it back if we return with an error.
|
||||||
|
const prev_arg0 = child_argv[0];
|
||||||
|
defer switch (arg0_expand) {
|
||||||
|
.expand => child_argv[0] = prev_arg0,
|
||||||
|
.no_expand => {},
|
||||||
|
};
|
||||||
|
|
||||||
while (it.next()) |search_path| {
|
while (it.next()) |search_path| {
|
||||||
if (path_buf.len < search_path.len + file_slice.len + 1) return error.NameTooLong;
|
if (path_buf.len < search_path.len + file_slice.len + 1) return error.NameTooLong;
|
||||||
mem.copy(u8, &path_buf, search_path);
|
mem.copy(u8, &path_buf, search_path);
|
||||||
|
|
|
@ -174,16 +174,23 @@ pub const LibCInstallation = struct {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const FindNativeOptions = struct {
|
||||||
|
allocator: *Allocator,
|
||||||
|
|
||||||
|
/// If enabled, will print human-friendly errors to stderr.
|
||||||
|
verbose: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
/// Finds the default, native libc.
|
/// Finds the default, native libc.
|
||||||
pub fn findNative(allocator: *Allocator) FindError!LibCInstallation {
|
pub fn findNative(args: FindNativeOptions) FindError!LibCInstallation {
|
||||||
var self: LibCInstallation = .{};
|
var self: LibCInstallation = .{};
|
||||||
|
|
||||||
if (is_windows) {
|
if (is_windows) {
|
||||||
if (is_gnu) {
|
if (is_gnu) {
|
||||||
var batch = Batch(FindError!void, 3, .auto_async).init();
|
var batch = Batch(FindError!void, 3, .auto_async).init();
|
||||||
batch.add(&async self.findNativeIncludeDirPosix(allocator));
|
batch.add(&async self.findNativeIncludeDirPosix(args));
|
||||||
batch.add(&async self.findNativeCrtDirPosix(allocator));
|
batch.add(&async self.findNativeCrtDirPosix(args));
|
||||||
batch.add(&async self.findNativeStaticCrtDirPosix(allocator));
|
batch.add(&async self.findNativeStaticCrtDirPosix(args));
|
||||||
try batch.wait();
|
try batch.wait();
|
||||||
} else {
|
} else {
|
||||||
var sdk: *ZigWindowsSDK = undefined;
|
var sdk: *ZigWindowsSDK = undefined;
|
||||||
|
@ -192,11 +199,11 @@ pub const LibCInstallation = struct {
|
||||||
defer zig_free_windows_sdk(sdk);
|
defer zig_free_windows_sdk(sdk);
|
||||||
|
|
||||||
var batch = Batch(FindError!void, 5, .auto_async).init();
|
var batch = Batch(FindError!void, 5, .auto_async).init();
|
||||||
batch.add(&async self.findNativeMsvcIncludeDir(allocator, sdk));
|
batch.add(&async self.findNativeMsvcIncludeDir(args, sdk));
|
||||||
batch.add(&async self.findNativeMsvcLibDir(allocator, sdk));
|
batch.add(&async self.findNativeMsvcLibDir(args, sdk));
|
||||||
batch.add(&async self.findNativeKernel32LibDir(allocator, sdk));
|
batch.add(&async self.findNativeKernel32LibDir(args, sdk));
|
||||||
batch.add(&async self.findNativeIncludeDirWindows(allocator, sdk));
|
batch.add(&async self.findNativeIncludeDirWindows(args, sdk));
|
||||||
batch.add(&async self.findNativeCrtDirWindows(allocator, sdk));
|
batch.add(&async self.findNativeCrtDirWindows(args, sdk));
|
||||||
try batch.wait();
|
try batch.wait();
|
||||||
},
|
},
|
||||||
.OutOfMemory => return error.OutOfMemory,
|
.OutOfMemory => return error.OutOfMemory,
|
||||||
|
@ -208,11 +215,11 @@ pub const LibCInstallation = struct {
|
||||||
try blk: {
|
try blk: {
|
||||||
var batch = Batch(FindError!void, 2, .auto_async).init();
|
var batch = Batch(FindError!void, 2, .auto_async).init();
|
||||||
errdefer batch.wait() catch {};
|
errdefer batch.wait() catch {};
|
||||||
batch.add(&async self.findNativeIncludeDirPosix(allocator));
|
batch.add(&async self.findNativeIncludeDirPosix(args));
|
||||||
if (is_freebsd or is_netbsd) {
|
if (is_freebsd or is_netbsd) {
|
||||||
self.crt_dir = try std.mem.dupeZ(allocator, u8, "/usr/lib");
|
self.crt_dir = try std.mem.dupeZ(args.allocator, u8, "/usr/lib");
|
||||||
} else if (is_linux or is_dragonfly) {
|
} else if (is_linux or is_dragonfly) {
|
||||||
batch.add(&async self.findNativeCrtDirPosix(allocator));
|
batch.add(&async self.findNativeCrtDirPosix(args));
|
||||||
}
|
}
|
||||||
break :blk batch.wait();
|
break :blk batch.wait();
|
||||||
};
|
};
|
||||||
|
@ -231,7 +238,8 @@ pub const LibCInstallation = struct {
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn findNativeIncludeDirPosix(self: *LibCInstallation, allocator: *Allocator) FindError!void {
|
fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
|
||||||
|
const allocator = args.allocator;
|
||||||
const dev_null = if (is_windows) "nul" else "/dev/null";
|
const dev_null = if (is_windows) "nul" else "/dev/null";
|
||||||
const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;
|
const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;
|
||||||
const argv = [_][]const u8{
|
const argv = [_][]const u8{
|
||||||
|
@ -252,15 +260,24 @@ pub const LibCInstallation = struct {
|
||||||
.expand_arg0 = .expand,
|
.expand_arg0 = .expand,
|
||||||
}) catch |err| switch (err) {
|
}) catch |err| switch (err) {
|
||||||
error.OutOfMemory => return error.OutOfMemory,
|
error.OutOfMemory => return error.OutOfMemory,
|
||||||
else => return error.UnableToSpawnCCompiler,
|
else => {
|
||||||
|
printVerboseInvocation(&argv, null, args.verbose, null);
|
||||||
|
return error.UnableToSpawnCCompiler;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
defer {
|
defer {
|
||||||
allocator.free(exec_res.stdout);
|
allocator.free(exec_res.stdout);
|
||||||
allocator.free(exec_res.stderr);
|
allocator.free(exec_res.stderr);
|
||||||
}
|
}
|
||||||
switch (exec_res.term) {
|
switch (exec_res.term) {
|
||||||
.Exited => |code| if (code != 0) return error.CCompilerExitCode,
|
.Exited => |code| if (code != 0) {
|
||||||
else => return error.CCompilerCrashed,
|
printVerboseInvocation(&argv, null, args.verbose, exec_res.stderr);
|
||||||
|
return error.CCompilerExitCode;
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
printVerboseInvocation(&argv, null, args.verbose, exec_res.stderr);
|
||||||
|
return error.CCompilerCrashed;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var it = std.mem.tokenize(exec_res.stderr, "\n\r");
|
var it = std.mem.tokenize(exec_res.stderr, "\n\r");
|
||||||
|
@ -322,9 +339,11 @@ pub const LibCInstallation = struct {
|
||||||
|
|
||||||
fn findNativeIncludeDirWindows(
|
fn findNativeIncludeDirWindows(
|
||||||
self: *LibCInstallation,
|
self: *LibCInstallation,
|
||||||
allocator: *Allocator,
|
args: FindNativeOptions,
|
||||||
sdk: *ZigWindowsSDK,
|
sdk: *ZigWindowsSDK,
|
||||||
) FindError!void {
|
) FindError!void {
|
||||||
|
const allocator = args.allocator;
|
||||||
|
|
||||||
var search_buf: [2]Search = undefined;
|
var search_buf: [2]Search = undefined;
|
||||||
const searches = fillSearch(&search_buf, sdk);
|
const searches = fillSearch(&search_buf, sdk);
|
||||||
|
|
||||||
|
@ -358,7 +377,13 @@ pub const LibCInstallation = struct {
|
||||||
return error.LibCStdLibHeaderNotFound;
|
return error.LibCStdLibHeaderNotFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn findNativeCrtDirWindows(self: *LibCInstallation, allocator: *Allocator, sdk: *ZigWindowsSDK) FindError!void {
|
fn findNativeCrtDirWindows(
|
||||||
|
self: *LibCInstallation,
|
||||||
|
args: FindNativeOptions,
|
||||||
|
sdk: *ZigWindowsSDK,
|
||||||
|
) FindError!void {
|
||||||
|
const allocator = args.allocator;
|
||||||
|
|
||||||
var search_buf: [2]Search = undefined;
|
var search_buf: [2]Search = undefined;
|
||||||
const searches = fillSearch(&search_buf, sdk);
|
const searches = fillSearch(&search_buf, sdk);
|
||||||
|
|
||||||
|
@ -398,15 +423,31 @@ pub const LibCInstallation = struct {
|
||||||
return error.LibCRuntimeNotFound;
|
return error.LibCRuntimeNotFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn findNativeCrtDirPosix(self: *LibCInstallation, allocator: *Allocator) FindError!void {
|
fn findNativeCrtDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
|
||||||
self.crt_dir = try ccPrintFileName(allocator, "crt1.o", .only_dir);
|
self.crt_dir = try ccPrintFileName(.{
|
||||||
|
.allocator = args.allocator,
|
||||||
|
.search_basename = "crt1.o",
|
||||||
|
.want_dirname = .only_dir,
|
||||||
|
.verbose = args.verbose,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn findNativeStaticCrtDirPosix(self: *LibCInstallation, allocator: *Allocator) FindError!void {
|
fn findNativeStaticCrtDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
|
||||||
self.static_crt_dir = try ccPrintFileName(allocator, "crtbegin.o", .only_dir);
|
self.static_crt_dir = try ccPrintFileName(.{
|
||||||
|
.allocator = args.allocator,
|
||||||
|
.search_basename = "crtbegin.o",
|
||||||
|
.want_dirname = .only_dir,
|
||||||
|
.verbose = args.verbose,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn findNativeKernel32LibDir(self: *LibCInstallation, allocator: *Allocator, sdk: *ZigWindowsSDK) FindError!void {
|
fn findNativeKernel32LibDir(
|
||||||
|
self: *LibCInstallation,
|
||||||
|
args: FindNativeOptions,
|
||||||
|
sdk: *ZigWindowsSDK,
|
||||||
|
) FindError!void {
|
||||||
|
const allocator = args.allocator;
|
||||||
|
|
||||||
var search_buf: [2]Search = undefined;
|
var search_buf: [2]Search = undefined;
|
||||||
const searches = fillSearch(&search_buf, sdk);
|
const searches = fillSearch(&search_buf, sdk);
|
||||||
|
|
||||||
|
@ -448,9 +489,11 @@ pub const LibCInstallation = struct {
|
||||||
|
|
||||||
fn findNativeMsvcIncludeDir(
|
fn findNativeMsvcIncludeDir(
|
||||||
self: *LibCInstallation,
|
self: *LibCInstallation,
|
||||||
allocator: *Allocator,
|
args: FindNativeOptions,
|
||||||
sdk: *ZigWindowsSDK,
|
sdk: *ZigWindowsSDK,
|
||||||
) FindError!void {
|
) FindError!void {
|
||||||
|
const allocator = args.allocator;
|
||||||
|
|
||||||
const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCStdLibHeaderNotFound;
|
const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCStdLibHeaderNotFound;
|
||||||
const msvc_lib_dir = msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len];
|
const msvc_lib_dir = msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len];
|
||||||
const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound;
|
const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound;
|
||||||
|
@ -481,9 +524,10 @@ pub const LibCInstallation = struct {
|
||||||
|
|
||||||
fn findNativeMsvcLibDir(
|
fn findNativeMsvcLibDir(
|
||||||
self: *LibCInstallation,
|
self: *LibCInstallation,
|
||||||
allocator: *Allocator,
|
args: FindNativeOptions,
|
||||||
sdk: *ZigWindowsSDK,
|
sdk: *ZigWindowsSDK,
|
||||||
) FindError!void {
|
) FindError!void {
|
||||||
|
const allocator = args.allocator;
|
||||||
const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCRuntimeNotFound;
|
const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCRuntimeNotFound;
|
||||||
self.msvc_lib_dir = try std.mem.dupeZ(allocator, u8, msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]);
|
self.msvc_lib_dir = try std.mem.dupeZ(allocator, u8, msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]);
|
||||||
}
|
}
|
||||||
|
@ -491,14 +535,19 @@ pub const LibCInstallation = struct {
|
||||||
|
|
||||||
const default_cc_exe = if (is_windows) "cc.exe" else "cc";
|
const default_cc_exe = if (is_windows) "cc.exe" else "cc";
|
||||||
|
|
||||||
/// caller owns returned memory
|
pub const CCPrintFileNameOptions = struct {
|
||||||
fn ccPrintFileName(
|
|
||||||
allocator: *Allocator,
|
allocator: *Allocator,
|
||||||
o_file: []const u8,
|
search_basename: []const u8,
|
||||||
want_dirname: enum { full_path, only_dir },
|
want_dirname: enum { full_path, only_dir },
|
||||||
) ![:0]u8 {
|
verbose: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// caller owns returned memory
|
||||||
|
fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 {
|
||||||
|
const allocator = args.allocator;
|
||||||
|
|
||||||
const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;
|
const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;
|
||||||
const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={}", .{o_file});
|
const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={}", .{args.search_basename});
|
||||||
defer allocator.free(arg1);
|
defer allocator.free(arg1);
|
||||||
const argv = [_][]const u8{ cc_exe, arg1 };
|
const argv = [_][]const u8{ cc_exe, arg1 };
|
||||||
|
|
||||||
|
@ -520,16 +569,22 @@ fn ccPrintFileName(
|
||||||
allocator.free(exec_res.stderr);
|
allocator.free(exec_res.stderr);
|
||||||
}
|
}
|
||||||
switch (exec_res.term) {
|
switch (exec_res.term) {
|
||||||
.Exited => |code| if (code != 0) return error.CCompilerExitCode,
|
.Exited => |code| if (code != 0) {
|
||||||
else => return error.CCompilerCrashed,
|
printVerboseInvocation(&argv, args.search_basename, args.verbose, exec_res.stderr);
|
||||||
|
return error.CCompilerExitCode;
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
printVerboseInvocation(&argv, args.search_basename, args.verbose, exec_res.stderr);
|
||||||
|
return error.CCompilerCrashed;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var it = std.mem.tokenize(exec_res.stdout, "\n\r");
|
var it = std.mem.tokenize(exec_res.stdout, "\n\r");
|
||||||
const line = it.next() orelse return error.LibCRuntimeNotFound;
|
const line = it.next() orelse return error.LibCRuntimeNotFound;
|
||||||
// When this command fails, it returns exit code 0 and duplicates the input file name.
|
// When this command fails, it returns exit code 0 and duplicates the input file name.
|
||||||
// So we detect failure by checking if the output matches exactly the input.
|
// So we detect failure by checking if the output matches exactly the input.
|
||||||
if (std.mem.eql(u8, line, o_file)) return error.LibCRuntimeNotFound;
|
if (std.mem.eql(u8, line, args.search_basename)) return error.LibCRuntimeNotFound;
|
||||||
switch (want_dirname) {
|
switch (args.want_dirname) {
|
||||||
.full_path => return std.mem.dupeZ(allocator, u8, line),
|
.full_path => return std.mem.dupeZ(allocator, u8, line),
|
||||||
.only_dir => {
|
.only_dir => {
|
||||||
const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound;
|
const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound;
|
||||||
|
@ -538,6 +593,29 @@ fn ccPrintFileName(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn printVerboseInvocation(
|
||||||
|
argv: []const []const u8,
|
||||||
|
search_basename: ?[]const u8,
|
||||||
|
verbose: bool,
|
||||||
|
stderr: ?[]const u8,
|
||||||
|
) void {
|
||||||
|
if (!verbose) return;
|
||||||
|
|
||||||
|
if (search_basename) |s| {
|
||||||
|
std.debug.warn("Zig attempted to find the file '{}' by executing this command:\n", .{s});
|
||||||
|
} else {
|
||||||
|
std.debug.warn("Zig attempted to find the path to native system libc headers by executing this command:\n", .{});
|
||||||
|
}
|
||||||
|
for (argv) |arg, i| {
|
||||||
|
if (i != 0) std.debug.warn(" ", .{});
|
||||||
|
std.debug.warn("{}", .{arg});
|
||||||
|
}
|
||||||
|
std.debug.warn("\n", .{});
|
||||||
|
if (stderr) |s| {
|
||||||
|
std.debug.warn("Output:\n==========\n{}\n==========\n", .{s});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Caller owns returned memory.
|
/// Caller owns returned memory.
|
||||||
pub fn detectNativeDynamicLinker(allocator: *Allocator) error{
|
pub fn detectNativeDynamicLinker(allocator: *Allocator) error{
|
||||||
OutOfMemory,
|
OutOfMemory,
|
||||||
|
@ -617,7 +695,11 @@ pub fn detectNativeDynamicLinker(allocator: *Allocator) error{
|
||||||
for (ld_info_list.toSlice()) |ld_info| {
|
for (ld_info_list.toSlice()) |ld_info| {
|
||||||
const standard_ld_basename = fs.path.basename(ld_info.ld_path);
|
const standard_ld_basename = fs.path.basename(ld_info.ld_path);
|
||||||
|
|
||||||
const full_ld_path = ccPrintFileName(allocator, standard_ld_basename, .full_path) catch |err| switch (err) {
|
const full_ld_path = ccPrintFileName(.{
|
||||||
|
.allocator = allocator,
|
||||||
|
.search_basename = standard_ld_basename,
|
||||||
|
.want_dirname = .full_path,
|
||||||
|
}) catch |err| switch (err) {
|
||||||
error.OutOfMemory => return error.OutOfMemory,
|
error.OutOfMemory => return error.OutOfMemory,
|
||||||
error.LibCRuntimeNotFound,
|
error.LibCRuntimeNotFound,
|
||||||
error.CCompilerExitCode,
|
error.CCompilerExitCode,
|
||||||
|
|
|
@ -899,7 +899,10 @@ export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [
|
||||||
|
|
||||||
// ABI warning
|
// ABI warning
|
||||||
export fn stage2_libc_find_native(stage1_libc: *Stage2LibCInstallation) Error {
|
export fn stage2_libc_find_native(stage1_libc: *Stage2LibCInstallation) Error {
|
||||||
var libc = LibCInstallation.findNative(std.heap.c_allocator) catch |err| switch (err) {
|
var libc = LibCInstallation.findNative(.{
|
||||||
|
.allocator = std.heap.c_allocator,
|
||||||
|
.verbose = true,
|
||||||
|
}) catch |err| switch (err) {
|
||||||
error.OutOfMemory => return .OutOfMemory,
|
error.OutOfMemory => return .OutOfMemory,
|
||||||
error.FileSystem => return .FileSystem,
|
error.FileSystem => return .FileSystem,
|
||||||
error.UnableToSpawnCCompiler => return .UnableToSpawnCCompiler,
|
error.UnableToSpawnCCompiler => return .UnableToSpawnCCompiler,
|
||||||
|
|
Loading…
Reference in New Issue