| author | |
| committer | |
| log | 8087ec8e8c9e3abf8cf2f3952127aa97749610a5 |
| tree | 2d9302cc45c2dc8dde46fd5241902c3f72e4ab57 |
| parent | 2f3add4f301fb0fd6f72fb6d00a39342a2255c29 |
6 files changed, 78 insertions(+), 167 deletions(-)
src/link/Elf.zig+33-56| ... | ... | @@ -1353,10 +1353,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1353 | 1353 | } |
| 1354 | 1354 | |
| 1355 | 1355 | for (positionals.items) |obj| { |
| 1356 | const in_file = try std.fs.cwd().openFile(obj.path, .{}); | |
| 1357 | defer in_file.close(); | |
| 1358 | 1356 | var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined }; |
| 1359 | self.parsePositional(in_file, obj.path, obj.must_link, &parse_ctx) catch |err| | |
| 1357 | self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err| | |
| 1360 | 1358 | try self.handleAndReportParseError(obj.path, err, &parse_ctx); |
| 1361 | 1359 | } |
| 1362 | 1360 | |
| ... | ... | @@ -1437,9 +1435,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1437 | 1435 | |
| 1438 | 1436 | for (system_libs.items) |lib| { |
| 1439 | 1437 | var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined }; |
| 1440 | const in_file = try std.fs.cwd().openFile(lib.path, .{}); | |
| 1441 | defer in_file.close(); | |
| 1442 | self.parseLibrary(in_file, lib, false, &parse_ctx) catch |err| | |
| 1438 | self.parseLibrary(lib, false, &parse_ctx) catch |err| | |
| 1443 | 1439 | try self.handleAndReportParseError(lib.path, err, &parse_ctx); |
| 1444 | 1440 | } |
| 1445 | 1441 | |
| ... | ... | @@ -1456,10 +1452,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1456 | 1452 | if (csu.crtn) |v| try positionals.append(.{ .path = v }); |
| 1457 | 1453 | |
| 1458 | 1454 | for (positionals.items) |obj| { |
| 1459 | const in_file = try std.fs.cwd().openFile(obj.path, .{}); | |
| 1460 | defer in_file.close(); | |
| 1461 | 1455 | var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined }; |
| 1462 | self.parsePositional(in_file, obj.path, obj.must_link, &parse_ctx) catch |err| | |
| 1456 | self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err| | |
| 1463 | 1457 | try self.handleAndReportParseError(obj.path, err, &parse_ctx); |
| 1464 | 1458 | } |
| 1465 | 1459 | |
| ... | ... | @@ -1679,51 +1673,40 @@ const ParseError = error{ |
| 1679 | 1673 | InvalidCharacter, |
| 1680 | 1674 | } || LdScript.Error || std.os.AccessError || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError; |
| 1681 | 1675 | |
| 1682 | fn parsePositional( | |
| 1683 | self: *Elf, | |
| 1684 | in_file: std.fs.File, | |
| 1685 | path: []const u8, | |
| 1686 | must_link: bool, | |
| 1687 | ctx: *ParseErrorCtx, | |
| 1688 | ) ParseError!void { | |
| 1676 | fn parsePositional(self: *Elf, path: []const u8, must_link: bool, ctx: *ParseErrorCtx) ParseError!void { | |
| 1689 | 1677 | const tracy = trace(@src()); |
| 1690 | 1678 | defer tracy.end(); |
| 1691 | ||
| 1692 | if (Object.isObject(in_file)) { | |
| 1693 | try self.parseObject(in_file, path, ctx); | |
| 1679 | if (try Object.isObject(path)) { | |
| 1680 | try self.parseObject(path, ctx); | |
| 1694 | 1681 | } else { |
| 1695 | try self.parseLibrary(in_file, .{ .path = path }, must_link, ctx); | |
| 1682 | try self.parseLibrary(.{ .path = path }, must_link, ctx); | |
| 1696 | 1683 | } |
| 1697 | 1684 | } |
| 1698 | 1685 | |
| 1699 | fn parseLibrary( | |
| 1700 | self: *Elf, | |
| 1701 | in_file: std.fs.File, | |
| 1702 | lib: SystemLib, | |
| 1703 | must_link: bool, | |
| 1704 | ctx: *ParseErrorCtx, | |
| 1705 | ) ParseError!void { | |
| 1686 | fn parseLibrary(self: *Elf, lib: SystemLib, must_link: bool, ctx: *ParseErrorCtx) ParseError!void { | |
| 1706 | 1687 | const tracy = trace(@src()); |
| 1707 | 1688 | defer tracy.end(); |
| 1708 | 1689 | |
| 1709 | if (Archive.isArchive(in_file)) { | |
| 1710 | try self.parseArchive(in_file, lib.path, must_link, ctx); | |
| 1711 | } else if (SharedObject.isSharedObject(in_file)) { | |
| 1712 | try self.parseSharedObject(in_file, lib, ctx); | |
| 1690 | if (try Archive.isArchive(lib.path)) { | |
| 1691 | try self.parseArchive(lib.path, must_link, ctx); | |
| 1692 | } else if (try SharedObject.isSharedObject(lib.path)) { | |
| 1693 | try self.parseSharedObject(lib, ctx); | |
| 1713 | 1694 | } else { |
| 1714 | 1695 | // TODO if the script has a top-level comment identifying it as GNU ld script, |
| 1715 | 1696 | // then report parse errors. Otherwise return UnknownFileType. |
| 1716 | self.parseLdScript(in_file, lib, ctx) catch |err| switch (err) { | |
| 1697 | self.parseLdScript(lib, ctx) catch |err| switch (err) { | |
| 1717 | 1698 | else => return error.UnknownFileType, |
| 1718 | 1699 | }; |
| 1719 | 1700 | } |
| 1720 | 1701 | } |
| 1721 | 1702 | |
| 1722 | fn parseObject(self: *Elf, in_file: std.fs.File, path: []const u8, ctx: *ParseErrorCtx) ParseError!void { | |
| 1703 | fn parseObject(self: *Elf, path: []const u8, ctx: *ParseErrorCtx) ParseError!void { | |
| 1723 | 1704 | const tracy = trace(@src()); |
| 1724 | 1705 | defer tracy.end(); |
| 1725 | 1706 | |
| 1726 | 1707 | const gpa = self.base.allocator; |
| 1708 | const in_file = try std.fs.cwd().openFile(path, .{}); | |
| 1709 | defer in_file.close(); | |
| 1727 | 1710 | const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); |
| 1728 | 1711 | const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); |
| 1729 | 1712 | self.files.set(index, .{ .object = .{ |
| ... | ... | @@ -1740,17 +1723,13 @@ fn parseObject(self: *Elf, in_file: std.fs.File, path: []const u8, ctx: *ParseEr |
| 1740 | 1723 | if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch; |
| 1741 | 1724 | } |
| 1742 | 1725 | |
| 1743 | fn parseArchive( | |
| 1744 | self: *Elf, | |
| 1745 | in_file: std.fs.File, | |
| 1746 | path: []const u8, | |
| 1747 | must_link: bool, | |
| 1748 | ctx: *ParseErrorCtx, | |
| 1749 | ) ParseError!void { | |
| 1726 | fn parseArchive(self: *Elf, path: []const u8, must_link: bool, ctx: *ParseErrorCtx) ParseError!void { | |
| 1750 | 1727 | const tracy = trace(@src()); |
| 1751 | 1728 | defer tracy.end(); |
| 1752 | 1729 | |
| 1753 | 1730 | const gpa = self.base.allocator; |
| 1731 | const in_file = try std.fs.cwd().openFile(path, .{}); | |
| 1732 | defer in_file.close(); | |
| 1754 | 1733 | const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); |
| 1755 | 1734 | var archive = Archive{ .path = try gpa.dupe(u8, path), .data = data }; |
| 1756 | 1735 | defer archive.deinit(gpa); |
| ... | ... | @@ -1773,16 +1752,13 @@ fn parseArchive( |
| 1773 | 1752 | } |
| 1774 | 1753 | } |
| 1775 | 1754 | |
| 1776 | fn parseSharedObject( | |
| 1777 | self: *Elf, | |
| 1778 | in_file: std.fs.File, | |
| 1779 | lib: SystemLib, | |
| 1780 | ctx: *ParseErrorCtx, | |
| 1781 | ) ParseError!void { | |
| 1755 | fn parseSharedObject(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError!void { | |
| 1782 | 1756 | const tracy = trace(@src()); |
| 1783 | 1757 | defer tracy.end(); |
| 1784 | 1758 | |
| 1785 | 1759 | const gpa = self.base.allocator; |
| 1760 | const in_file = try std.fs.cwd().openFile(lib.path, .{}); | |
| 1761 | defer in_file.close(); | |
| 1786 | 1762 | const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); |
| 1787 | 1763 | const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); |
| 1788 | 1764 | self.files.set(index, .{ .shared_object = .{ |
| ... | ... | @@ -1801,11 +1777,13 @@ fn parseSharedObject( |
| 1801 | 1777 | if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch; |
| 1802 | 1778 | } |
| 1803 | 1779 | |
| 1804 | fn parseLdScript(self: *Elf, in_file: std.fs.File, lib: SystemLib, ctx: *ParseErrorCtx) ParseError!void { | |
| 1780 | fn parseLdScript(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError!void { | |
| 1805 | 1781 | const tracy = trace(@src()); |
| 1806 | 1782 | defer tracy.end(); |
| 1807 | 1783 | |
| 1808 | 1784 | const gpa = self.base.allocator; |
| 1785 | const in_file = try std.fs.cwd().openFile(lib.path, .{}); | |
| 1786 | defer in_file.close(); | |
| 1809 | 1787 | const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); |
| 1810 | 1788 | defer gpa.free(data); |
| 1811 | 1789 | |
| ... | ... | @@ -1871,11 +1849,8 @@ fn parseLdScript(self: *Elf, in_file: std.fs.File, lib: SystemLib, ctx: *ParseEr |
| 1871 | 1849 | } |
| 1872 | 1850 | |
| 1873 | 1851 | const full_path = test_path.items; |
| 1874 | const scr_file = try std.fs.cwd().openFile(full_path, .{}); | |
| 1875 | defer scr_file.close(); | |
| 1876 | ||
| 1877 | 1852 | var scr_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined }; |
| 1878 | self.parseLibrary(scr_file, .{ | |
| 1853 | self.parseLibrary(.{ | |
| 1879 | 1854 | .needed = scr_obj.needed, |
| 1880 | 1855 | .path = full_path, |
| 1881 | 1856 | }, false, &scr_ctx) catch |err| try self.handleAndReportParseError(full_path, err, &scr_ctx); |
| ... | ... | @@ -1893,14 +1868,16 @@ fn accessLibPath( |
| 1893 | 1868 | const sep = fs.path.sep_str; |
| 1894 | 1869 | const target = self.base.options.target; |
| 1895 | 1870 | test_path.clearRetainingCapacity(); |
| 1871 | const prefix = if (link_mode != null) "lib" else ""; | |
| 1872 | const suffix = if (link_mode) |mode| switch (mode) { | |
| 1873 | .Static => target.staticLibSuffix(), | |
| 1874 | .Dynamic => target.dynamicLibSuffix(), | |
| 1875 | } else ""; | |
| 1896 | 1876 | try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{ |
| 1897 | 1877 | lib_dir_path, |
| 1898 | target.libPrefix(), | |
| 1878 | prefix, | |
| 1899 | 1879 | lib_name, |
| 1900 | if (link_mode) |mode| switch (mode) { | |
| 1901 | .Static => target.staticLibSuffix(), | |
| 1902 | .Dynamic => target.dynamicLibSuffix(), | |
| 1903 | } else "", | |
| 1880 | suffix, | |
| 1904 | 1881 | }); |
| 1905 | 1882 | if (checked_paths) |cpaths| { |
| 1906 | 1883 | try cpaths.append(try self.base.allocator.dupe(u8, test_path.items)); |
src/link/Elf/Archive.zig+3-2| ... | ... | @@ -62,10 +62,11 @@ const ar_hdr = extern struct { |
| 62 | 62 | } |
| 63 | 63 | }; |
| 64 | 64 | |
| 65 | pub fn isArchive(file: std.fs.File) bool { | |
| 65 | pub fn isArchive(path: []const u8) !bool { | |
| 66 | const file = try std.fs.cwd().openFile(path, .{}); | |
| 67 | defer file.close(); | |
| 66 | 68 | const reader = file.reader(); |
| 67 | 69 | const magic = reader.readBytesNoEof(Archive.SARMAG) catch return false; |
| 68 | defer file.seekTo(0) catch {}; | |
| 69 | 70 | if (!mem.eql(u8, &magic, ARMAG)) return false; |
| 70 | 71 | return true; |
| 71 | 72 | } |
src/link/Elf/LdScript.zig+3-105| ... | ... | @@ -83,7 +83,8 @@ fn doParse(scr: *LdScript, ctx: struct { |
| 83 | 83 | const cmd = ctx.parser.getCommand(cmd_id); |
| 84 | 84 | switch (cmd) { |
| 85 | 85 | .output_format => scr.cpu_arch = try ctx.parser.outputFormat(), |
| 86 | .group => try ctx.parser.group(ctx.args), | |
| 86 | // TODO we should verify that group only contains libraries | |
| 87 | .input, .group => try ctx.parser.group(ctx.args), | |
| 87 | 88 | else => return error.UnexpectedToken, |
| 88 | 89 | } |
| 89 | 90 | } else break; |
| ... | ... | @@ -102,6 +103,7 @@ const LineColumn = struct { |
| 102 | 103 | |
| 103 | 104 | const Command = enum { |
| 104 | 105 | output_format, |
| 106 | input, | |
| 105 | 107 | group, |
| 106 | 108 | as_needed, |
| 107 | 109 | |
| ... | ... | @@ -420,110 +422,6 @@ const TokenIterator = struct { |
| 420 | 422 | } |
| 421 | 423 | }; |
| 422 | 424 | |
| 423 | const testing = std.testing; | |
| 424 | ||
| 425 | fn testExpectedTokens(input: []const u8, expected: []const Token.Id) !void { | |
| 426 | var given = std.ArrayList(Token.Id).init(testing.allocator); | |
| 427 | defer given.deinit(); | |
| 428 | ||
| 429 | var tokenizer = Tokenizer{ .source = input }; | |
| 430 | while (true) { | |
| 431 | const tok = tokenizer.next(); | |
| 432 | if (tok.id == .invalid) { | |
| 433 | std.debug.print(" {s} => '{s}'\n", .{ @tagName(tok.id), tok.get(input) }); | |
| 434 | } | |
| 435 | try given.append(tok.id); | |
| 436 | if (tok.id == .eof) break; | |
| 437 | } | |
| 438 | ||
| 439 | try testing.expectEqualSlices(Token.Id, expected, given.items); | |
| 440 | } | |
| 441 | ||
| 442 | test "Tokenizer - just comments" { | |
| 443 | try testExpectedTokens( | |
| 444 | \\/* GNU ld script | |
| 445 | \\ Use the shared library, but some functions are only in | |
| 446 | \\ the static library, so try that secondarily. */ | |
| 447 | , &.{ .comment, .eof }); | |
| 448 | } | |
| 449 | ||
| 450 | test "Tokenizer - comments with a simple command" { | |
| 451 | try testExpectedTokens( | |
| 452 | \\/* GNU ld script | |
| 453 | \\ Use the shared library, but some functions are only in | |
| 454 | \\ the static library, so try that secondarily. */ | |
| 455 | \\OUTPUT_FORMAT(elf64-x86-64) | |
| 456 | , &.{ .comment, .new_line, .command, .lparen, .literal, .rparen, .eof }); | |
| 457 | } | |
| 458 | ||
| 459 | test "Tokenizer - libc.so" { | |
| 460 | try testExpectedTokens( | |
| 461 | \\/* GNU ld script | |
| 462 | \\ Use the shared library, but some functions are only in | |
| 463 | \\ the static library, so try that secondarily. */ | |
| 464 | \\OUTPUT_FORMAT(elf64-x86-64) | |
| 465 | \\GROUP ( /a/b/c.so.6 /a/d/e.a AS_NEEDED ( /f/g/h.so.2 ) ) | |
| 466 | , &.{ | |
| 467 | .comment, .new_line, // GNU comment | |
| 468 | .command, .lparen, .literal, .rparen, .new_line, // output format | |
| 469 | .command, .lparen, .literal, .literal, // group start | |
| 470 | .command, .lparen, .literal, .rparen, // as needed | |
| 471 | .rparen, // group end | |
| 472 | .eof, | |
| 473 | }); | |
| 474 | } | |
| 475 | ||
| 476 | test "Parser - output format" { | |
| 477 | const source = | |
| 478 | \\OUTPUT_FORMAT(elf64-x86-64) | |
| 479 | ; | |
| 480 | var tokenizer = Tokenizer{ .source = source }; | |
| 481 | var tokens = std.ArrayList(Token).init(testing.allocator); | |
| 482 | defer tokens.deinit(); | |
| 483 | while (true) { | |
| 484 | const tok = tokenizer.next(); | |
| 485 | try testing.expect(tok.id != .invalid); | |
| 486 | try tokens.append(tok); | |
| 487 | if (tok.id == .eof) break; | |
| 488 | } | |
| 489 | var it = TokenIterator{ .tokens = tokens.items }; | |
| 490 | var parser = Parser{ .source = source, .it = &it }; | |
| 491 | const tok_id = try parser.require(.command); | |
| 492 | try testing.expectEqual(parser.getCommand(tok_id), .output_format); | |
| 493 | const cpu_arch = try parser.outputFormat(); | |
| 494 | try testing.expectEqual(cpu_arch, .x86_64); | |
| 495 | } | |
| 496 | ||
| 497 | test "Parser - group with as-needed" { | |
| 498 | const source = | |
| 499 | \\GROUP ( /a/b/c.so.6 /a/d/e.a AS_NEEDED ( /f/g/h.so.2 ) ) | |
| 500 | ; | |
| 501 | var tokenizer = Tokenizer{ .source = source }; | |
| 502 | var tokens = std.ArrayList(Token).init(testing.allocator); | |
| 503 | defer tokens.deinit(); | |
| 504 | while (true) { | |
| 505 | const tok = tokenizer.next(); | |
| 506 | try testing.expect(tok.id != .invalid); | |
| 507 | try tokens.append(tok); | |
| 508 | if (tok.id == .eof) break; | |
| 509 | } | |
| 510 | var it = TokenIterator{ .tokens = tokens.items }; | |
| 511 | var parser = Parser{ .source = source, .it = &it }; | |
| 512 | ||
| 513 | var args = std.ArrayList(Elf.LinkObject).init(testing.allocator); | |
| 514 | defer args.deinit(); | |
| 515 | const tok_id = try parser.require(.command); | |
| 516 | try testing.expectEqual(parser.getCommand(tok_id), .group); | |
| 517 | try parser.group(&args); | |
| 518 | ||
| 519 | try testing.expectEqualStrings("/a/b/c.so.6", args.items[0].path); | |
| 520 | try testing.expect(args.items[0].needed); | |
| 521 | try testing.expectEqualStrings("/a/d/e.a", args.items[1].path); | |
| 522 | try testing.expect(args.items[1].needed); | |
| 523 | try testing.expectEqualStrings("/f/g/h.so.2", args.items[2].path); | |
| 524 | try testing.expect(!args.items[2].needed); | |
| 525 | } | |
| 526 | ||
| 527 | 425 | const LdScript = @This(); |
| 528 | 426 | |
| 529 | 427 | const std = @import("std"); |
src/link/Elf/Object.zig+3-2| ... | ... | @@ -22,10 +22,11 @@ num_dynrelocs: u32 = 0, |
| 22 | 22 | |
| 23 | 23 | output_symtab_size: Elf.SymtabSize = .{}, |
| 24 | 24 | |
| 25 | pub fn isObject(file: std.fs.File) bool { | |
| 25 | pub fn isObject(path: []const u8) !bool { | |
| 26 | const file = try std.fs.cwd().openFile(path, .{}); | |
| 27 | defer file.close(); | |
| 26 | 28 | const reader = file.reader(); |
| 27 | 29 | const header = reader.readStruct(elf.Elf64_Ehdr) catch return false; |
| 28 | defer file.seekTo(0) catch {}; | |
| 29 | 30 | if (!mem.eql(u8, header.e_ident[0..4], "\x7fELF")) return false; |
| 30 | 31 | if (header.e_ident[elf.EI_VERSION] != 1) return false; |
| 31 | 32 | if (header.e_type != elf.ET.REL) return false; |
src/link/Elf/SharedObject.zig+3-2| ... | ... | @@ -22,10 +22,11 @@ alive: bool, |
| 22 | 22 | |
| 23 | 23 | output_symtab_size: Elf.SymtabSize = .{}, |
| 24 | 24 | |
| 25 | pub fn isSharedObject(file: std.fs.File) bool { | |
| 25 | pub fn isSharedObject(path: []const u8) !bool { | |
| 26 | const file = try std.fs.cwd().openFile(path, .{}); | |
| 27 | defer file.close(); | |
| 26 | 28 | const reader = file.reader(); |
| 27 | 29 | const header = reader.readStruct(elf.Elf64_Ehdr) catch return false; |
| 28 | defer file.seekTo(0) catch {}; | |
| 29 | 30 | if (!mem.eql(u8, header.e_ident[0..4], "\x7fELF")) return false; |
| 30 | 31 | if (header.e_ident[elf.EI_VERSION] != 1) return false; |
| 31 | 32 | if (header.e_type != elf.ET.DYN) return false; |
test/link/elf.zig+33| ... | ... | @@ -75,6 +75,7 @@ pub fn build(b: *Build) void { |
| 75 | 75 | elf_step.dependOn(testLargeAlignmentExe(b, .{ .target = glibc_target })); |
| 76 | 76 | elf_step.dependOn(testLargeBss(b, .{ .target = glibc_target })); |
| 77 | 77 | elf_step.dependOn(testLinkOrder(b, .{ .target = glibc_target })); |
| 78 | elf_step.dependOn(testLdScript(b, .{ .target = glibc_target })); | |
| 78 | 79 | // https://github.com/ziglang/zig/issues/17451 |
| 79 | 80 | // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = glibc_target })); |
| 80 | 81 | elf_step.dependOn(testPie(b, .{ .target = glibc_target })); |
| ... | ... | @@ -1568,6 +1569,38 @@ fn testLinkOrder(b: *Build, opts: Options) *Step { |
| 1568 | 1569 | return test_step; |
| 1569 | 1570 | } |
| 1570 | 1571 | |
| 1572 | fn testLdScript(b: *Build, opts: Options) *Step { | |
| 1573 | const test_step = addTestStep(b, "ld-script", opts); | |
| 1574 | ||
| 1575 | const dso = addSharedLibrary(b, "bar", opts); | |
| 1576 | addCSourceBytes(dso, "int foo() { return 42; }", &.{}); | |
| 1577 | ||
| 1578 | const scripts = WriteFile.create(b); | |
| 1579 | _ = scripts.add("liba.so", "INPUT(libfoo.so)"); | |
| 1580 | _ = scripts.add("libfoo.so", "GROUP(AS_NEEDED(-lbar))"); | |
| 1581 | ||
| 1582 | const exe = addExecutable(b, "main", opts); | |
| 1583 | addCSourceBytes(exe, | |
| 1584 | \\int foo(); | |
| 1585 | \\int main() { | |
| 1586 | \\ return foo() - 42; | |
| 1587 | \\} | |
| 1588 | , &.{}); | |
| 1589 | exe.linkSystemLibrary2("a", .{}); | |
| 1590 | exe.addLibraryPath(scripts.getDirectory()); | |
| 1591 | exe.addLibraryPath(dso.getEmittedBinDirectory()); | |
| 1592 | exe.addRPath(dso.getEmittedBinDirectory()); | |
| 1593 | exe.linkLibC(); | |
| 1594 | // https://github.com/ziglang/zig/issues/17619 | |
| 1595 | exe.pie = true; | |
| 1596 | ||
| 1597 | const run = addRunArtifact(exe); | |
| 1598 | run.expectExitCode(0); | |
| 1599 | test_step.dependOn(&run.step); | |
| 1600 | ||
| 1601 | return test_step; | |
| 1602 | } | |
| 1603 | ||
| 1571 | 1604 | fn testLinkingC(b: *Build, opts: Options) *Step { |
| 1572 | 1605 | const test_step = addTestStep(b, "linking-c", opts); |
| 1573 | 1606 |