| ... | @@ -167,7 +167,15 @@ pub const NativePaths = struct { | ... | @@ -167,7 +167,15 @@ pub const NativePaths = struct { |
| 167 | | 167 | |
| 168 | pub const NativeTargetInfo = struct { | 168 | pub const NativeTargetInfo = struct { |
| 169 | target: Target, | 169 | target: Target, |
| 170 | dynamic_linker: ?[:0]u8, | 170 | |
| | 171 | /// Contains the memory used to store the dynamic linker path. This field should |
| | 172 | /// not be used directly. See `dynamicLinker` and `setDynamicLinker`. This field |
| | 173 | /// exists so that this API requires no allocator. |
| | 174 | dynamic_linker_buffer: [255]u8 = undefined, |
| | 175 | |
| | 176 | /// Used to construct the dynamic linker path. This field should not be used |
| | 177 | /// directly. See `dynamicLinker` and `setDynamicLinker`. |
| | 178 | dynamic_linker_max: ?u8 = null, |
| 171 | | 179 | |
| 172 | pub const DetectError = error{ | 180 | pub const DetectError = error{ |
| 173 | OutOfMemory, | 181 | OutOfMemory, |
| ... | @@ -181,6 +189,7 @@ pub const NativeTargetInfo = struct { | ... | @@ -181,6 +189,7 @@ pub const NativeTargetInfo = struct { |
| 181 | | 189 | |
| 182 | /// Detects the native CPU model & features, operating system & version, and C ABI & dynamic linker. | 190 | /// Detects the native CPU model & features, operating system & version, and C ABI & dynamic linker. |
| 183 | /// On Linux, this is additionally responsible for detecting the native glibc version when applicable. | 191 | /// On Linux, this is additionally responsible for detecting the native glibc version when applicable. |
| | 192 | /// TODO Remove the allocator requirement from this. |
| 184 | pub fn detect(allocator: *Allocator) DetectError!NativeTargetInfo { | 193 | pub fn detect(allocator: *Allocator) DetectError!NativeTargetInfo { |
| 185 | const arch = Target.current.cpu.arch; | 194 | const arch = Target.current.cpu.arch; |
| 186 | const os_tag = Target.current.os.tag; | 195 | const os_tag = Target.current.os.tag; |
| ... | @@ -188,8 +197,7 @@ pub const NativeTargetInfo = struct { | ... | @@ -188,8 +197,7 @@ pub const NativeTargetInfo = struct { |
| 188 | // TODO Detect native CPU model & features. Until that is implemented we hard code baseline. | 197 | // TODO Detect native CPU model & features. Until that is implemented we hard code baseline. |
| 189 | const cpu = Target.Cpu.baseline(arch); | 198 | const cpu = Target.Cpu.baseline(arch); |
| 190 | | 199 | |
| 191 | // TODO Detect native operating system version. Until that is implemented we use the minimum version | 200 | // TODO Detect native operating system version. Until that is implemented we use the default range. |
| 192 | // of the default range. | | |
| 193 | const os = Target.Os.defaultVersionRange(os_tag); | 201 | const os = Target.Os.defaultVersionRange(os_tag); |
| 194 | | 202 | |
| 195 | return detectAbiAndDynamicLinker(allocator, cpu, os); | 203 | return detectAbiAndDynamicLinker(allocator, cpu, os); |
| ... | @@ -201,6 +209,21 @@ pub const NativeTargetInfo = struct { | ... | @@ -201,6 +209,21 @@ pub const NativeTargetInfo = struct { |
| 201 | self.* = undefined; | 209 | self.* = undefined; |
| 202 | } | 210 | } |
| 203 | | 211 | |
| | 212 | /// The returned memory has the same lifetime as the `NativeTargetInfo`. |
| | 213 | pub fn dynamicLinker(self: *const NativeTargetInfo) ?[]const u8 { |
| | 214 | const m = self.dynamic_linker_max orelse return null; |
| | 215 | return self.dynamic_linker_buffer[0 .. m + 1]; |
| | 216 | } |
| | 217 | |
| | 218 | pub fn setDynamicLinker(self: *NativeTargetInfo, dl_or_null: ?[]const u8) void { |
| | 219 | if (dl_or_null) |dl| { |
| | 220 | mem.copy(u8, &self.dynamic_linker_buffer, dl); |
| | 221 | self.dynamic_linker_max = @intCast(u8, dl.len - 1); |
| | 222 | } else { |
| | 223 | self.dynamic_linker_max = null; |
| | 224 | } |
| | 225 | } |
| | 226 | |
| 204 | /// First we attempt to use the executable's own binary. If it is dynamically | 227 | /// First we attempt to use the executable's own binary. If it is dynamically |
| 205 | /// linked, then it should answer both the C ABI question and the dynamic linker question. | 228 | /// linked, then it should answer both the C ABI question and the dynamic linker question. |
| 206 | /// If it is statically linked, then we try /usr/bin/env. If that does not provide the answer, then | 229 | /// If it is statically linked, then we try /usr/bin/env. If that does not provide the answer, then |
| ... | @@ -245,10 +268,11 @@ pub const NativeTargetInfo = struct { | ... | @@ -245,10 +268,11 @@ pub const NativeTargetInfo = struct { |
| 245 | .os = os, | 268 | .os = os, |
| 246 | .abi = abi, | 269 | .abi = abi, |
| 247 | }; | 270 | }; |
| 248 | const standard_ld_path = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) { | 271 | var buf: [255]u8 = undefined; |
| 249 | error.OutOfMemory => return error.OutOfMemory, | 272 | const standard_ld_path = if (target.standardDynamicLinkerPath(&buf)) |s| |
| 250 | error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => continue, | 273 | try mem.dupe(allocator, u8, s) |
| 251 | }; | 274 | else |
| | 275 | continue; |
| 252 | errdefer allocator.free(standard_ld_path); | 276 | errdefer allocator.free(standard_ld_path); |
| 253 | try ld_info_list.append(.{ | 277 | try ld_info_list.append(.{ |
| 254 | .ld_path = standard_ld_path, | 278 | .ld_path = standard_ld_path, |
| ... | @@ -294,28 +318,29 @@ pub const NativeTargetInfo = struct { | ... | @@ -294,28 +318,29 @@ pub const NativeTargetInfo = struct { |
| 294 | } | 318 | } |
| 295 | } | 319 | } |
| 296 | | 320 | |
| 297 | return NativeTargetInfo{ | 321 | var result: NativeTargetInfo = .{ |
| 298 | .target = .{ | 322 | .target = .{ |
| 299 | .cpu = cpu, | 323 | .cpu = cpu, |
| 300 | .os = os_adjusted, | 324 | .os = os_adjusted, |
| 301 | .abi = found_ld_info.abi, | 325 | .abi = found_ld_info.abi, |
| 302 | }, | 326 | }, |
| 303 | .dynamic_linker = try mem.dupeZ(allocator, u8, found_ld_path), | | |
| 304 | }; | 327 | }; |
| | 328 | result.setDynamicLinker(found_ld_path); |
| | 329 | return result; |
| 305 | } | 330 | } |
| 306 | | 331 | |
| 307 | // If Zig is statically linked, such as via distributed binary static builds, the above | 332 | // If Zig is statically linked, such as via distributed binary static builds, the above |
| 308 | // trick won't work. The next thing we fall back to is the same thing, but for /usr/bin/env. | 333 | // trick won't work. The next thing we fall back to is the same thing, but for /usr/bin/env. |
| 309 | // Since that path is hard-coded into the shebang line of many portable scripts, it's a | 334 | // Since that path is hard-coded into the shebang line of many portable scripts, it's a |
| 310 | // reasonably reliable path to check for. | 335 | // reasonably reliable path to check for. |
| 311 | return abiAndDynamicLinkerFromUsrBinEnv(allocator, cpu, os) catch |err| switch (err) { | 336 | return abiAndDynamicLinkerFromUsrBinEnv(cpu, os) catch |err| switch (err) { |
| 312 | error.OutOfMemory => return error.OutOfMemory, | 337 | error.FileSystem, |
| 313 | error.FileSystem => return error.FileSystem, | 338 | error.SystemResources, |
| 314 | error.SystemResources => return error.SystemResources, | 339 | error.SymLinkLoop, |
| 315 | error.SymLinkLoop => return error.SymLinkLoop, | 340 | error.ProcessFdQuotaExceeded, |
| 316 | error.ProcessFdQuotaExceeded => return error.ProcessFdQuotaExceeded, | 341 | error.SystemFdQuotaExceeded, |
| 317 | error.SystemFdQuotaExceeded => return error.SystemFdQuotaExceeded, | 342 | error.DeviceBusy, |
| 318 | error.DeviceBusy => return error.DeviceBusy, | 343 | => |e| return e, |
| 319 | | 344 | |
| 320 | error.UnableToReadElfFile, | 345 | error.UnableToReadElfFile, |
| 321 | error.ElfNotADynamicExecutable, | 346 | error.ElfNotADynamicExecutable, |
| ... | @@ -327,6 +352,8 @@ pub const NativeTargetInfo = struct { | ... | @@ -327,6 +352,8 @@ pub const NativeTargetInfo = struct { |
| 327 | error.InvalidElfMagic, | 352 | error.InvalidElfMagic, |
| 328 | error.UsrBinEnvNotAvailable, | 353 | error.UsrBinEnvNotAvailable, |
| 329 | error.Unexpected, | 354 | error.Unexpected, |
| | 355 | error.UnexpectedEndOfFile, |
| | 356 | error.NameTooLong, |
| 330 | // Finally, we fall back on the standard path. | 357 | // Finally, we fall back on the standard path. |
| 331 | => defaultAbiAndDynamicLinker(allocator, cpu, os), | 358 | => defaultAbiAndDynamicLinker(allocator, cpu, os), |
| 332 | }; | 359 | }; |
| ... | @@ -362,11 +389,7 @@ pub const NativeTargetInfo = struct { | ... | @@ -362,11 +389,7 @@ pub const NativeTargetInfo = struct { |
| 362 | }; | 389 | }; |
| 363 | } | 390 | } |
| 364 | | 391 | |
| 365 | fn abiAndDynamicLinkerFromUsrBinEnv( | 392 | fn abiAndDynamicLinkerFromUsrBinEnv(cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { |
| 366 | allocator: *Allocator, | | |
| 367 | cpu: Target.Cpu, | | |
| 368 | os: Target.Os, | | |
| 369 | ) !NativeTargetInfo { | | |
| 370 | const env_file = std.fs.openFileAbsoluteC("/usr/bin/env", .{}) catch |err| switch (err) { | 393 | const env_file = std.fs.openFileAbsoluteC("/usr/bin/env", .{}) catch |err| switch (err) { |
| 371 | error.NoSpaceLeft => unreachable, | 394 | error.NoSpaceLeft => unreachable, |
| 372 | error.NameTooLong => unreachable, | 395 | error.NameTooLong => unreachable, |
| ... | @@ -409,6 +432,14 @@ pub const NativeTargetInfo = struct { | ... | @@ -409,6 +432,14 @@ pub const NativeTargetInfo = struct { |
| 409 | const phnum = elfInt(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum); | 432 | const phnum = elfInt(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum); |
| 410 | const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); | 433 | const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); |
| 411 | | 434 | |
| | 435 | var result: NativeTargetInfo = .{ |
| | 436 | .target = .{ |
| | 437 | .cpu = cpu, |
| | 438 | .os = os, |
| | 439 | .abi = Target.Abi.default(cpu.arch, os), |
| | 440 | }, |
| | 441 | }; |
| | 442 | |
| 412 | const ph_total_size = std.math.mul(u32, phentsize, phnum) catch |err| switch (err) { | 443 | const ph_total_size = std.math.mul(u32, phentsize, phnum) catch |err| switch (err) { |
| 413 | error.Overflow => return error.InvalidElfProgramHeaders, | 444 | error.Overflow => return error.InvalidElfProgramHeaders, |
| 414 | }; | 445 | }; |
| ... | @@ -430,7 +461,22 @@ pub const NativeTargetInfo = struct { | ... | @@ -430,7 +461,22 @@ pub const NativeTargetInfo = struct { |
| 430 | const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type); | 461 | const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type); |
| 431 | switch (p_type) { | 462 | switch (p_type) { |
| 432 | elf.PT_INTERP => { | 463 | elf.PT_INTERP => { |
| 433 | std.debug.warn("found PT_INTERP\n", .{}); | 464 | const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset); |
| | 465 | const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz); |
| | 466 | var interp_buf: [255]u8 = undefined; |
| | 467 | if (p_filesz > interp_buf.len) return error.NameTooLong; |
| | 468 | var read_offset: usize = 0; |
| | 469 | while (true) { |
| | 470 | const len = try wrapRead(env_file.pread( |
| | 471 | interp_buf[read_offset .. p_filesz - read_offset], |
| | 472 | p_offset + read_offset, |
| | 473 | )); |
| | 474 | if (len == 0) return error.UnexpectedEndOfFile; |
| | 475 | read_offset += len; |
| | 476 | if (read_offset == p_filesz) break; |
| | 477 | } |
| | 478 | // PT_INTERP includes a null byte in p_filesz. |
| | 479 | result.setDynamicLinker(interp_buf[0 .. p_filesz - 1]); |
| 434 | }, | 480 | }, |
| 435 | elf.PT_DYNAMIC => { | 481 | elf.PT_DYNAMIC => { |
| 436 | std.debug.warn("found PT_DYNAMIC\n", .{}); | 482 | std.debug.warn("found PT_DYNAMIC\n", .{}); |
| ... | @@ -440,7 +486,7 @@ pub const NativeTargetInfo = struct { | ... | @@ -440,7 +486,7 @@ pub const NativeTargetInfo = struct { |
| 440 | } | 486 | } |
| 441 | } | 487 | } |
| 442 | | 488 | |
| 443 | return error.OutOfMemory; // TODO | 489 | return result; |
| 444 | } | 490 | } |
| 445 | | 491 | |
| 446 | fn wrapRead(res: std.os.ReadError!usize) !usize { | 492 | fn wrapRead(res: std.os.ReadError!usize) !usize { |
| ... | @@ -457,18 +503,17 @@ pub const NativeTargetInfo = struct { | ... | @@ -457,18 +503,17 @@ pub const NativeTargetInfo = struct { |
| 457 | } | 503 | } |
| 458 | | 504 | |
| 459 | fn defaultAbiAndDynamicLinker(allocator: *Allocator, cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { | 505 | fn defaultAbiAndDynamicLinker(allocator: *Allocator, cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo { |
| 460 | const target: Target = .{ | 506 | var result: NativeTargetInfo = .{ |
| 461 | .cpu = cpu, | 507 | .target = .{ |
| 462 | .os = os, | 508 | .cpu = cpu, |
| 463 | .abi = Target.Abi.default(cpu.arch, os), | 509 | .os = os, |
| 464 | }; | 510 | .abi = Target.Abi.default(cpu.arch, os), |
| 465 | return @as(NativeTargetInfo, .{ | | |
| 466 | .target = target, | | |
| 467 | .dynamic_linker = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) { | | |
| 468 | error.OutOfMemory => return error.OutOfMemory, | | |
| 469 | error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => null, | | |
| 470 | }, | 511 | }, |
| 471 | }); | 512 | }; |
| | 513 | if (result.target.standardDynamicLinkerPath(&result.dynamic_linker_buffer)) |s| { |
| | 514 | result.dynamic_linker_max = @intCast(u8, s.len - 1); |
| | 515 | } |
| | 516 | return result; |
| 472 | } | 517 | } |
| 473 | }; | 518 | }; |
| 474 | | 519 | |