authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-30 20:30:31-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-31 06:48:17+00:00
logf1c1b8c02d489b9ebd9c55bfce2345b8c9bbf01a
tree1bbd6aa452ab656f9f09ed3d785bf078f0c85b09
parentf962315363b6306f819b7c88b830ea00ab0e734e

stage2: add DWARF info for the main compilation unit


7 files changed, 310 insertions(+), 19 deletions(-)

lib/std/dwarf.zig+1-1
...@@ -9,7 +9,7 @@ const leb = @import("debug/leb128.zig");...@@ -9,7 +9,7 @@ const leb = @import("debug/leb128.zig");
99
10const ArrayList = std.ArrayList;10const ArrayList = std.ArrayList;
1111
12usingnamespace @import("dwarf_bits.zig");12pub usingnamespace @import("dwarf_bits.zig");
1313
14const PcRange = struct {14const PcRange = struct {
15 start: u64,15 start: u64,
lib/std/dwarf_bits.zig+9
...@@ -680,3 +680,12 @@ pub const LANG_HP_Basic91 = 0x8004;...@@ -680,3 +680,12 @@ pub const LANG_HP_Basic91 = 0x8004;
680pub const LANG_HP_Pascal91 = 0x8005;680pub const LANG_HP_Pascal91 = 0x8005;
681pub const LANG_HP_IMacro = 0x8006;681pub const LANG_HP_IMacro = 0x8006;
682pub const LANG_HP_Assembler = 0x8007;682pub const LANG_HP_Assembler = 0x8007;
683
684pub const UT_compile = 0x01;
685pub const UT_type = 0x02;
686pub const UT_partial = 0x03;
687pub const UT_skeleton = 0x04;
688pub const UT_split_compile = 0x05;
689pub const UT_split_type = 0x06;
690pub const UT_lo_user = 0x80;
691pub const UT_hi_user = 0xff;
src-self-hosted/Module.zig+10
...@@ -75,6 +75,8 @@ next_anon_name_index: usize = 0,...@@ -75,6 +75,8 @@ next_anon_name_index: usize = 0,
75/// contains Decls that need to be deleted if they end up having no references to them.75/// contains Decls that need to be deleted if they end up having no references to them.
76deletion_set: std.ArrayListUnmanaged(*Decl) = .{},76deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
7777
78/// Owned by Module.
79root_name: []u8,
78keep_source_files_loaded: bool,80keep_source_files_loaded: bool,
7981
80pub const InnerError = error{ OutOfMemory, AnalysisFail };82pub const InnerError = error{ OutOfMemory, AnalysisFail };
...@@ -772,6 +774,7 @@ pub const AllErrors = struct {...@@ -772,6 +774,7 @@ pub const AllErrors = struct {
772774
773pub const InitOptions = struct {775pub const InitOptions = struct {
774 target: std.Target,776 target: std.Target,
777 root_name: []const u8,
775 root_pkg: *Package,778 root_pkg: *Package,
776 output_mode: std.builtin.OutputMode,779 output_mode: std.builtin.OutputMode,
777 bin_file_dir: ?std.fs.Dir = null,780 bin_file_dir: ?std.fs.Dir = null,
...@@ -783,8 +786,13 @@ pub const InitOptions = struct {...@@ -783,8 +786,13 @@ pub const InitOptions = struct {
783};786};
784787
785pub fn init(gpa: *Allocator, options: InitOptions) !Module {788pub fn init(gpa: *Allocator, options: InitOptions) !Module {
789 const root_name = try gpa.dupe(u8, options.root_name);
790 errdefer gpa.free(root_name);
791
786 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();792 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
787 const bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{793 const bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{
794 .root_name = root_name,
795 .root_src_dir_path = options.root_pkg.root_src_dir_path,
788 .target = options.target,796 .target = options.target,
789 .output_mode = options.output_mode,797 .output_mode = options.output_mode,
790 .link_mode = options.link_mode orelse .Static,798 .link_mode = options.link_mode orelse .Static,
...@@ -821,6 +829,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {...@@ -821,6 +829,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
821829
822 return Module{830 return Module{
823 .gpa = gpa,831 .gpa = gpa,
832 .root_name = root_name,
824 .root_pkg = options.root_pkg,833 .root_pkg = options.root_pkg,
825 .root_scope = root_scope,834 .root_scope = root_scope,
826 .bin_file_dir = bin_file_dir,835 .bin_file_dir = bin_file_dir,
...@@ -834,6 +843,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {...@@ -834,6 +843,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
834pub fn deinit(self: *Module) void {843pub fn deinit(self: *Module) void {
835 self.bin_file.destroy();844 self.bin_file.destroy();
836 const gpa = self.gpa;845 const gpa = self.gpa;
846 self.gpa.free(self.root_name);
837 self.deletion_set.deinit(gpa);847 self.deletion_set.deinit(gpa);
838 self.work_queue.deinit();848 self.work_queue.deinit();
839849
src-self-hosted/Package.zig+11-5
...@@ -1,8 +1,11 @@...@@ -1,8 +1,11 @@
1pub const Table = std.StringHashMap(*Package);1pub const Table = std.StringHashMap(*Package);
22
3/// This should be used for file operations.
3root_src_dir: std.fs.Dir,4root_src_dir: std.fs.Dir,
4/// Relative to `root_src_dir`.5/// This is for metadata purposes, for example putting into debug information.
5root_src_path: []const u8,6root_src_dir_path: []u8,
7/// Relative to `root_src_dir` and `root_src_dir_path`.
8root_src_path: []u8,
6table: Table,9table: Table,
710
8/// No references to `root_src_dir` and `root_src_path` are kept.11/// No references to `root_src_dir` and `root_src_path` are kept.
...@@ -18,8 +21,11 @@ pub fn create(...@@ -18,8 +21,11 @@ pub fn create(
18 errdefer allocator.destroy(ptr);21 errdefer allocator.destroy(ptr);
19 const root_src_path_dupe = try mem.dupe(allocator, u8, root_src_path);22 const root_src_path_dupe = try mem.dupe(allocator, u8, root_src_path);
20 errdefer allocator.free(root_src_path_dupe);23 errdefer allocator.free(root_src_path_dupe);
24 const root_src_dir_path = try mem.dupe(allocator, u8, root_src_dir);
25 errdefer allocator.free(root_src_dir_path);
21 ptr.* = .{26 ptr.* = .{
22 .root_src_dir = try base_dir.openDir(root_src_dir, .{}),27 .root_src_dir = try base_dir.openDir(root_src_dir, .{}),
28 .root_src_dir_path = root_src_dir_path,
23 .root_src_path = root_src_path_dupe,29 .root_src_path = root_src_path_dupe,
24 .table = Table.init(allocator),30 .table = Table.init(allocator),
25 };31 };
...@@ -30,6 +36,7 @@ pub fn destroy(self: *Package) void {...@@ -30,6 +36,7 @@ pub fn destroy(self: *Package) void {
30 const allocator = self.table.allocator;36 const allocator = self.table.allocator;
31 self.root_src_dir.close();37 self.root_src_dir.close();
32 allocator.free(self.root_src_path);38 allocator.free(self.root_src_path);
39 allocator.free(self.root_src_dir_path);
33 {40 {
34 var it = self.table.iterator();41 var it = self.table.iterator();
35 while (it.next()) |kv| {42 while (it.next()) |kv| {
...@@ -41,10 +48,9 @@ pub fn destroy(self: *Package) void {...@@ -41,10 +48,9 @@ pub fn destroy(self: *Package) void {
41}48}
4249
43pub fn add(self: *Package, name: []const u8, package: *Package) !void {50pub fn add(self: *Package, name: []const u8, package: *Package) !void {
51 try self.table.ensureCapacity(self.table.items().len + 1);
44 const name_dupe = try mem.dupe(self.table.allocator, u8, name);52 const name_dupe = try mem.dupe(self.table.allocator, u8, name);
45 errdefer self.table.allocator.deinit(name_dupe);53 self.table.putAssumeCapacityNoClobber(name_dupe, package);
46 const entry = try self.table.put(name_dupe, package);
47 assert(entry == null);
48}54}
4955
50const std = @import("std");56const std = @import("std");
src-self-hosted/link.zig+275-13
...@@ -8,6 +8,11 @@ const fs = std.fs;...@@ -8,6 +8,11 @@ const fs = std.fs;
8const elf = std.elf;8const elf = std.elf;
9const codegen = @import("codegen.zig");9const codegen = @import("codegen.zig");
10const c_codegen = @import("codegen/c.zig");10const c_codegen = @import("codegen/c.zig");
11const log = std.log;
12const DW = std.dwarf;
13
14// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
15// zig fmt: off
1116
12const default_entry_addr = 0x8000000;17const default_entry_addr = 0x8000000;
1318
...@@ -17,6 +22,8 @@ pub const Options = struct {...@@ -17,6 +22,8 @@ pub const Options = struct {
17 link_mode: std.builtin.LinkMode,22 link_mode: std.builtin.LinkMode,
18 object_format: std.builtin.ObjectFormat,23 object_format: std.builtin.ObjectFormat,
19 optimize_mode: std.builtin.Mode,24 optimize_mode: std.builtin.Mode,
25 root_name: []const u8,
26 root_src_dir_path: []const u8,
20 /// Used for calculating how much space to reserve for symbols in case the binary file27 /// Used for calculating how much space to reserve for symbols in case the binary file
21 /// does not already have a symbol table.28 /// does not already have a symbol table.
22 symbol_count_hint: u64 = 32,29 symbol_count_hint: u64 = 32,
...@@ -319,12 +326,18 @@ pub const File = struct {...@@ -319,12 +326,18 @@ pub const File = struct {
319 phdr_got_index: ?u16 = null,326 phdr_got_index: ?u16 = null,
320 entry_addr: ?u64 = null,327 entry_addr: ?u64 = null,
321328
329 debug_strtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
322 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},330 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
323 shstrtab_index: ?u16 = null,331 shstrtab_index: ?u16 = null,
324332
325 text_section_index: ?u16 = null,333 text_section_index: ?u16 = null,
326 symtab_section_index: ?u16 = null,334 symtab_section_index: ?u16 = null,
327 got_section_index: ?u16 = null,335 got_section_index: ?u16 = null,
336 debug_info_section_index: ?u16 = null,
337 debug_abbrev_section_index: ?u16 = null,
338 debug_str_section_index: ?u16 = null,
339
340 debug_abbrev_table_offset: ?u64 = null,
328341
329 /// The same order as in the file. ELF requires global symbols to all be after the342 /// The same order as in the file. ELF requires global symbols to all be after the
330 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and343 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
...@@ -345,7 +358,10 @@ pub const File = struct {...@@ -345,7 +358,10 @@ pub const File = struct {
345 phdr_table_dirty: bool = false,358 phdr_table_dirty: bool = false,
346 shdr_table_dirty: bool = false,359 shdr_table_dirty: bool = false,
347 shstrtab_dirty: bool = false,360 shstrtab_dirty: bool = false,
361 debug_strtab_dirty: bool = false,
348 offset_table_count_dirty: bool = false,362 offset_table_count_dirty: bool = false,
363 debug_info_section_dirty: bool = false,
364 debug_abbrev_section_dirty: bool = false,
349365
350 error_flags: ErrorFlags = ErrorFlags{},366 error_flags: ErrorFlags = ErrorFlags{},
351367
...@@ -434,6 +450,7 @@ pub const File = struct {...@@ -434,6 +450,7 @@ pub const File = struct {
434 self.sections.deinit(self.allocator);450 self.sections.deinit(self.allocator);
435 self.program_headers.deinit(self.allocator);451 self.program_headers.deinit(self.allocator);
436 self.shstrtab.deinit(self.allocator);452 self.shstrtab.deinit(self.allocator);
453 self.debug_strtab.deinit(self.allocator);
437 self.local_symbols.deinit(self.allocator);454 self.local_symbols.deinit(self.allocator);
438 self.global_symbols.deinit(self.allocator);455 self.global_symbols.deinit(self.allocator);
439 self.global_symbol_free_list.deinit(self.allocator);456 self.global_symbol_free_list.deinit(self.allocator);
...@@ -545,6 +562,14 @@ pub const File = struct {...@@ -545,6 +562,14 @@ pub const File = struct {
545 return @intCast(u32, result);562 return @intCast(u32, result);
546 }563 }
547564
565 fn makeDebugString(self: *Elf, bytes: []const u8) !u32 {
566 try self.debug_strtab.ensureCapacity(self.allocator, self.debug_strtab.items.len + bytes.len + 1);
567 const result = self.debug_strtab.items.len;
568 self.debug_strtab.appendSliceAssumeCapacity(bytes);
569 self.debug_strtab.appendAssumeCapacity(0);
570 return @intCast(u32, result);
571 }
572
548 fn getString(self: *Elf, str_off: u32) []const u8 {573 fn getString(self: *Elf, str_off: u32) []const u8 {
549 assert(str_off < self.shstrtab.items.len);574 assert(str_off < self.shstrtab.items.len);
550 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));575 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));
...@@ -572,7 +597,7 @@ pub const File = struct {...@@ -572,7 +597,7 @@ pub const File = struct {
572 const file_size = self.base.options.program_code_size_hint;597 const file_size = self.base.options.program_code_size_hint;
573 const p_align = 0x1000;598 const p_align = 0x1000;
574 const off = self.findFreeSpace(file_size, p_align);599 const off = self.findFreeSpace(file_size, p_align);
575 std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });600 log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
576 try self.program_headers.append(self.allocator, .{601 try self.program_headers.append(self.allocator, .{
577 .p_type = elf.PT_LOAD,602 .p_type = elf.PT_LOAD,
578 .p_offset = off,603 .p_offset = off,
...@@ -593,7 +618,7 @@ pub const File = struct {...@@ -593,7 +618,7 @@ pub const File = struct {
593 // page align.618 // page align.
594 const p_align = 0x1000;619 const p_align = 0x1000;
595 const off = self.findFreeSpace(file_size, p_align);620 const off = self.findFreeSpace(file_size, p_align);
596 std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });621 log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
597 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.622 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
598 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something623 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
599 // else in virtual memory.624 // else in virtual memory.
...@@ -615,7 +640,7 @@ pub const File = struct {...@@ -615,7 +640,7 @@ pub const File = struct {
615 assert(self.shstrtab.items.len == 0);640 assert(self.shstrtab.items.len == 0);
616 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0641 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0
617 const off = self.findFreeSpace(self.shstrtab.items.len, 1);642 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
618 std.log.debug(.link, "found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });643 log.debug(.link, "found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
619 try self.sections.append(self.allocator, .{644 try self.sections.append(self.allocator, .{
620 .sh_name = try self.makeString(".shstrtab"),645 .sh_name = try self.makeString(".shstrtab"),
621 .sh_type = elf.SHT_STRTAB,646 .sh_type = elf.SHT_STRTAB,
...@@ -631,6 +656,27 @@ pub const File = struct {...@@ -631,6 +656,27 @@ pub const File = struct {
631 self.shstrtab_dirty = true;656 self.shstrtab_dirty = true;
632 self.shdr_table_dirty = true;657 self.shdr_table_dirty = true;
633 }658 }
659 if (self.debug_str_section_index == null) {
660 self.debug_str_section_index = @intCast(u16, self.sections.items.len);
661 assert(self.debug_strtab.items.len == 0);
662 try self.debug_strtab.append(self.allocator, 0); // need a 0 at position 0
663 const off = self.findFreeSpace(self.debug_strtab.items.len, 1);
664 log.debug(.link, "found debug_strtab free space 0x{x} to 0x{x}\n", .{ off, off + self.debug_strtab.items.len });
665 try self.sections.append(self.allocator, .{
666 .sh_name = try self.makeString(".debug_str"),
667 .sh_type = elf.SHT_PROGBITS,
668 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
669 .sh_addr = 0,
670 .sh_offset = off,
671 .sh_size = self.debug_strtab.items.len,
672 .sh_link = 0,
673 .sh_info = 0,
674 .sh_addralign = 1,
675 .sh_entsize = 1,
676 });
677 self.debug_strtab_dirty = true;
678 self.shdr_table_dirty = true;
679 }
634 if (self.text_section_index == null) {680 if (self.text_section_index == null) {
635 self.text_section_index = @intCast(u16, self.sections.items.len);681 self.text_section_index = @intCast(u16, self.sections.items.len);
636 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];682 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
...@@ -673,7 +719,7 @@ pub const File = struct {...@@ -673,7 +719,7 @@ pub const File = struct {
673 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);719 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
674 const file_size = self.base.options.symbol_count_hint * each_size;720 const file_size = self.base.options.symbol_count_hint * each_size;
675 const off = self.findFreeSpace(file_size, min_align);721 const off = self.findFreeSpace(file_size, min_align);
676 std.log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });722 log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
677723
678 try self.sections.append(self.allocator, .{724 try self.sections.append(self.allocator, .{
679 .sh_name = try self.makeString(".symtab"),725 .sh_name = try self.makeString(".symtab"),
...@@ -691,6 +737,56 @@ pub const File = struct {...@@ -691,6 +737,56 @@ pub const File = struct {
691 self.shdr_table_dirty = true;737 self.shdr_table_dirty = true;
692 try self.writeSymbol(0);738 try self.writeSymbol(0);
693 }739 }
740 if (self.debug_info_section_index == null) {
741 self.debug_info_section_index = @intCast(u16, self.sections.items.len);
742
743 const file_size_hint = 200;
744 const p_align = 1;
745 const off = self.findFreeSpace(file_size_hint, p_align);
746 log.debug(.link, "found .debug_info free space 0x{x} to 0x{x}\n", .{
747 off,
748 off + file_size_hint,
749 });
750 try self.sections.append(self.allocator, .{
751 .sh_name = try self.makeString(".debug_info"),
752 .sh_type = elf.SHT_PROGBITS,
753 .sh_flags = 0,
754 .sh_addr = 0,
755 .sh_offset = off,
756 .sh_size = file_size_hint,
757 .sh_link = 0,
758 .sh_info = 0,
759 .sh_addralign = p_align,
760 .sh_entsize = 0,
761 });
762 self.shdr_table_dirty = true;
763 self.debug_info_section_dirty = true;
764 }
765 if (self.debug_abbrev_section_index == null) {
766 self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len);
767
768 const file_size_hint = 128;
769 const p_align = 1;
770 const off = self.findFreeSpace(file_size_hint, p_align);
771 log.debug(.link, "found .debug_abbrev free space 0x{x} to 0x{x}\n", .{
772 off,
773 off + file_size_hint,
774 });
775 try self.sections.append(self.allocator, .{
776 .sh_name = try self.makeString(".debug_abbrev"),
777 .sh_type = elf.SHT_PROGBITS,
778 .sh_flags = 0,
779 .sh_addr = 0,
780 .sh_offset = off,
781 .sh_size = file_size_hint,
782 .sh_link = 0,
783 .sh_info = 0,
784 .sh_addralign = p_align,
785 .sh_entsize = 0,
786 });
787 self.shdr_table_dirty = true;
788 self.debug_abbrev_section_dirty = true;
789 }
694 const shsize: u64 = switch (self.ptr_width) {790 const shsize: u64 = switch (self.ptr_width) {
695 .p32 => @sizeOf(elf.Elf32_Shdr),791 .p32 => @sizeOf(elf.Elf32_Shdr),
696 .p64 => @sizeOf(elf.Elf64_Shdr),792 .p64 => @sizeOf(elf.Elf64_Shdr),
...@@ -726,12 +822,141 @@ pub const File = struct {...@@ -726,12 +822,141 @@ pub const File = struct {
726822
727 /// Commit pending changes and write headers.823 /// Commit pending changes and write headers.
728 pub fn flush(self: *Elf) !void {824 pub fn flush(self: *Elf) !void {
729 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();825 const target_endian = self.base.options.target.cpu.arch.endian();
826 const foreign_endian = target_endian != std.Target.current.cpu.arch.endian();
730827
731 // Unfortunately these have to be buffered and done at the end because ELF does not allow828 // Unfortunately these have to be buffered and done at the end because ELF does not allow
732 // mixing local and global symbols within a symbol table.829 // mixing local and global symbols within a symbol table.
733 try self.writeAllGlobalSymbols();830 try self.writeAllGlobalSymbols();
734831
832 if (self.debug_abbrev_section_dirty) {
833 const debug_abbrev_sect = &self.sections.items[self.debug_abbrev_section_index.?];
834
835 // These are LEB encoded but since the values are all less than 127
836 // we can simply append these bytes.
837 const abbrev_buf = [_]u8{
838 1, DW.TAG_compile_unit, DW.CHILDREN_no, // header
839 //DW.AT_stmt_list, DW.FORM_data4, TODO
840 DW.AT_low_pc , DW.FORM_addr,
841 DW.AT_high_pc , DW.FORM_addr,
842 DW.AT_name , DW.FORM_strp,
843 DW.AT_comp_dir , DW.FORM_strp,
844 DW.AT_producer , DW.FORM_strp,
845 DW.AT_language , DW.FORM_data2,
846 0, 0, // table sentinel
847
848 0, 0, 0, // section sentinel
849 };
850
851 const needed_size = abbrev_buf.len;
852 const allocated_size = self.allocatedSize(debug_abbrev_sect.sh_offset);
853 if (needed_size > allocated_size) {
854 debug_abbrev_sect.sh_size = 0; // free the space
855 debug_abbrev_sect.sh_offset = self.findFreeSpace(needed_size, 1);
856 }
857 debug_abbrev_sect.sh_size = needed_size;
858 log.debug(.link, ".debug_abbrev start=0x{x} end=0x{x}\n", .{
859 debug_abbrev_sect.sh_offset,
860 debug_abbrev_sect.sh_offset + needed_size,
861 });
862
863 const abbrev_offset = 0;
864 self.debug_abbrev_table_offset = abbrev_offset;
865 try self.file.?.pwriteAll(&abbrev_buf, debug_abbrev_sect.sh_offset + abbrev_offset);
866 if (!self.shdr_table_dirty) {
867 // Then it won't get written with the others and we need to do it.
868 try self.writeSectHeader(self.debug_abbrev_section_index.?);
869 }
870
871 self.debug_abbrev_section_dirty = false;
872 }
873 if (self.debug_info_section_dirty) {
874 const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
875
876 var di_buf = std.ArrayList(u8).init(self.allocator);
877 defer di_buf.deinit();
878
879 // Enough for a 64-bit header and main compilation unit without resizing.
880 try di_buf.ensureCapacity(100);
881
882 // initial length - length of the .debug_info contribution for this compilation unit,
883 // not including the initial length itself.
884 // We have to come back and write it later after we know the size.
885 const init_len_index = di_buf.items.len;
886 switch (self.ptr_width) {
887 .p32 => di_buf.items.len += 4,
888 .p64 => di_buf.items.len += 12,
889 }
890 const after_init_len = di_buf.items.len;
891 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 5, target_endian); // DWARF version
892 di_buf.appendAssumeCapacity(DW.UT_compile);
893 const abbrev_offset = self.debug_abbrev_table_offset.?;
894 switch (self.ptr_width) {
895 .p32 => {
896 di_buf.appendAssumeCapacity(4); // address size
897 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset), target_endian);
898 },
899 .p64 => {
900 di_buf.appendAssumeCapacity(8); // address size
901 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), abbrev_offset, target_endian);
902 },
903 }
904 // Write the form for the compile unit, which must match the abbrev table above.
905 const name_strp = try self.makeDebugString(self.base.options.root_name);
906 const comp_dir_strp = try self.makeDebugString(self.base.options.root_src_dir_path);
907 const producer_strp = try self.makeDebugString("zig (TODO version here)");
908 // Currently only one compilation unit is supported, so the address range is simply
909 // identical to the main program header virtual address and memory size.
910 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
911 const low_pc = text_phdr.p_vaddr;
912 const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
913
914 di_buf.appendAssumeCapacity(1); // abbrev tag, matching the value from the abbrev table header
915 //DW.AT_stmt_list, DW.FORM_data4, TODO line information
916 self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc);
917 self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc);
918 self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp);
919 self.writeDwarfAddrAssumeCapacity(&di_buf, comp_dir_strp);
920 self.writeDwarfAddrAssumeCapacity(&di_buf, producer_strp);
921 // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number:
922 // http://dwarfstd.org/ShowIssue.php?issue=171115.1
923 // Until then we say it is C99.
924 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG_C99, target_endian);
925
926 const init_len = di_buf.items.len - after_init_len;
927 switch (self.ptr_width) {
928 .p32 => {
929 mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len), target_endian);
930 },
931 .p64 => {
932 // initial length - length of the .debug_info contribution for this compilation unit,
933 // not including the initial length itself.
934 di_buf.items[init_len_index..][0..4].* = [_]u8{ 0xff, 0xff, 0xff, 0xff };
935 mem.writeInt(u64, di_buf.items[init_len_index + 4..][0..8], init_len, target_endian);
936 },
937 }
938
939 const needed_size = di_buf.items.len;
940 const allocated_size = self.allocatedSize(debug_info_sect.sh_offset);
941 if (needed_size > allocated_size) {
942 debug_info_sect.sh_size = 0; // free the space
943 debug_info_sect.sh_offset = self.findFreeSpace(needed_size, 1);
944 }
945 debug_info_sect.sh_size = needed_size;
946 log.debug(.link, ".debug_info start=0x{x} end=0x{x}\n", .{
947 debug_info_sect.sh_offset,
948 debug_info_sect.sh_offset + needed_size,
949 });
950
951 try self.file.?.pwriteAll(di_buf.items, debug_info_sect.sh_offset);
952 if (!self.shdr_table_dirty) {
953 // Then it won't get written with the others and we need to do it.
954 try self.writeSectHeader(self.debug_info_section_index.?);
955 }
956
957 self.debug_info_section_dirty = false;
958 }
959
735 if (self.phdr_table_dirty) {960 if (self.phdr_table_dirty) {
736 const phsize: u64 = switch (self.ptr_width) {961 const phsize: u64 = switch (self.ptr_width) {
737 .p32 => @sizeOf(elf.Elf32_Phdr),962 .p32 => @sizeOf(elf.Elf32_Phdr),
...@@ -789,7 +1014,7 @@ pub const File = struct {...@@ -789,7 +1014,7 @@ pub const File = struct {
789 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);1014 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
790 }1015 }
791 shstrtab_sect.sh_size = needed_size;1016 shstrtab_sect.sh_size = needed_size;
792 std.log.debug(.link, "shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });1017 log.debug(.link, "shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
7931018
794 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);1019 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
795 if (!self.shdr_table_dirty) {1020 if (!self.shdr_table_dirty) {
...@@ -799,6 +1024,27 @@ pub const File = struct {...@@ -799,6 +1024,27 @@ pub const File = struct {
799 self.shstrtab_dirty = false;1024 self.shstrtab_dirty = false;
800 }1025 }
801 }1026 }
1027 {
1028 const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];
1029 if (self.debug_strtab_dirty or self.debug_strtab.items.len != debug_strtab_sect.sh_size) {
1030 const allocated_size = self.allocatedSize(debug_strtab_sect.sh_offset);
1031 const needed_size = self.debug_strtab.items.len;
1032
1033 if (needed_size > allocated_size) {
1034 debug_strtab_sect.sh_size = 0; // free the space
1035 debug_strtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
1036 }
1037 debug_strtab_sect.sh_size = needed_size;
1038 log.debug(.link, "debug_strtab start=0x{x} end=0x{x}\n", .{ debug_strtab_sect.sh_offset, debug_strtab_sect.sh_offset + needed_size });
1039
1040 try self.file.?.pwriteAll(self.debug_strtab.items, debug_strtab_sect.sh_offset);
1041 if (!self.shdr_table_dirty) {
1042 // Then it won't get written with the others and we need to do it.
1043 try self.writeSectHeader(self.debug_str_section_index.?);
1044 }
1045 self.debug_strtab_dirty = false;
1046 }
1047 }
802 if (self.shdr_table_dirty) {1048 if (self.shdr_table_dirty) {
803 const shsize: u64 = switch (self.ptr_width) {1049 const shsize: u64 = switch (self.ptr_width) {
804 .p32 => @sizeOf(elf.Elf32_Shdr),1050 .p32 => @sizeOf(elf.Elf32_Shdr),
...@@ -835,7 +1081,7 @@ pub const File = struct {...@@ -835,7 +1081,7 @@ pub const File = struct {
8351081
836 for (buf) |*shdr, i| {1082 for (buf) |*shdr, i| {
837 shdr.* = self.sections.items[i];1083 shdr.* = self.sections.items[i];
838 std.log.debug(.link, "writing section {}\n", .{shdr.*});1084 log.debug(.link, "writing section {}\n", .{shdr.*});
839 if (foreign_endian) {1085 if (foreign_endian) {
840 bswapAllFields(elf.Elf64_Shdr, shdr);1086 bswapAllFields(elf.Elf64_Shdr, shdr);
841 }1087 }
...@@ -846,7 +1092,7 @@ pub const File = struct {...@@ -846,7 +1092,7 @@ pub const File = struct {
846 self.shdr_table_dirty = false;1092 self.shdr_table_dirty = false;
847 }1093 }
848 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {1094 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
849 std.log.debug(.link, "no_entry_point_found = true\n", .{});1095 log.debug(.link, "no_entry_point_found = true\n", .{});
850 self.error_flags.no_entry_point_found = true;1096 self.error_flags.no_entry_point_found = true;
851 } else {1097 } else {
852 self.error_flags.no_entry_point_found = false;1098 self.error_flags.no_entry_point_found = false;
...@@ -854,14 +1100,25 @@ pub const File = struct {...@@ -854,14 +1100,25 @@ pub const File = struct {
854 }1100 }
8551101
856 // The point of flush() is to commit changes, so nothing should be dirty after this.1102 // The point of flush() is to commit changes, so nothing should be dirty after this.
1103 assert(!self.debug_info_section_dirty);
1104 assert(!self.debug_abbrev_section_dirty);
857 assert(!self.phdr_table_dirty);1105 assert(!self.phdr_table_dirty);
858 assert(!self.shdr_table_dirty);1106 assert(!self.shdr_table_dirty);
859 assert(!self.shstrtab_dirty);1107 assert(!self.shstrtab_dirty);
1108 assert(!self.debug_strtab_dirty);
860 assert(!self.offset_table_count_dirty);1109 assert(!self.offset_table_count_dirty);
861 const syms_sect = &self.sections.items[self.symtab_section_index.?];1110 const syms_sect = &self.sections.items[self.symtab_section_index.?];
862 assert(syms_sect.sh_info == self.local_symbols.items.len);1111 assert(syms_sect.sh_info == self.local_symbols.items.len);
863 }1112 }
8641113
1114 fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {
1115 const target_endian = self.base.options.target.cpu.arch.endian();
1116 switch (self.ptr_width) {
1117 .p32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, addr), target_endian),
1118 .p64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), addr, target_endian),
1119 }
1120 }
1121
865 fn writeElfHeader(self: *Elf) !void {1122 fn writeElfHeader(self: *Elf) !void {
866 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;1123 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
8671124
...@@ -1122,6 +1379,11 @@ pub const File = struct {...@@ -1122,6 +1379,11 @@ pub const File = struct {
1122 phdr.p_memsz = needed_size;1379 phdr.p_memsz = needed_size;
1123 phdr.p_filesz = needed_size;1380 phdr.p_filesz = needed_size;
11241381
1382 // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
1383 // range of the compilation unit. When we expand the text section, this range changes,
1384 // so the .debug_info section becomes dirty.
1385 self.debug_info_section_dirty = true;
1386
1125 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty1387 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
1126 self.shdr_table_dirty = true; // TODO look into making only the one section dirty1388 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1127 }1389 }
...@@ -1160,10 +1422,10 @@ pub const File = struct {...@@ -1160,10 +1422,10 @@ pub const File = struct {
1160 try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);1422 try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
11611423
1162 if (self.local_symbol_free_list.popOrNull()) |i| {1424 if (self.local_symbol_free_list.popOrNull()) |i| {
1163 std.log.debug(.link, "reusing symbol index {} for {}\n", .{ i, decl.name });1425 log.debug(.link, "reusing symbol index {} for {}\n", .{ i, decl.name });
1164 decl.link.local_sym_index = i;1426 decl.link.local_sym_index = i;
1165 } else {1427 } else {
1166 std.log.debug(.link, "allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });1428 log.debug(.link, "allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
1167 decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);1429 decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
1168 _ = self.local_symbols.addOneAssumeCapacity();1430 _ = self.local_symbols.addOneAssumeCapacity();
1169 }1431 }
...@@ -1231,11 +1493,11 @@ pub const File = struct {...@@ -1231,11 +1493,11 @@ pub const File = struct {
1231 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);1493 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
1232 if (need_realloc) {1494 if (need_realloc) {
1233 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);1495 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
1234 std.log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });1496 log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
1235 if (vaddr != local_sym.st_value) {1497 if (vaddr != local_sym.st_value) {
1236 local_sym.st_value = vaddr;1498 local_sym.st_value = vaddr;
12371499
1238 std.log.debug(.link, " (writing new offset table entry)\n", .{});1500 log.debug(.link, " (writing new offset table entry)\n", .{});
1239 self.offset_table.items[decl.link.offset_table_index] = vaddr;1501 self.offset_table.items[decl.link.offset_table_index] = vaddr;
1240 try self.writeOffsetTableEntry(decl.link.offset_table_index);1502 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1241 }1503 }
...@@ -1253,7 +1515,7 @@ pub const File = struct {...@@ -1253,7 +1515,7 @@ pub const File = struct {
1253 const decl_name = mem.spanZ(decl.name);1515 const decl_name = mem.spanZ(decl.name);
1254 const name_str_index = try self.makeString(decl_name);1516 const name_str_index = try self.makeString(decl_name);
1255 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);1517 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
1256 std.log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });1518 log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
1257 errdefer self.freeTextBlock(&decl.link);1519 errdefer self.freeTextBlock(&decl.link);
12581520
1259 local_sym.* = .{1521 local_sym.* = .{
src-self-hosted/main.zig+3
...@@ -94,6 +94,8 @@ pub fn main() !void {...@@ -94,6 +94,8 @@ pub fn main() !void {
94 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);94 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
95 } else if (mem.eql(u8, cmd, "version")) {95 } else if (mem.eql(u8, cmd, "version")) {
96 // Need to set up the build script to give the version as a comptime value.96 // Need to set up the build script to give the version as a comptime value.
97 // TODO when you solve this, also take a look at link.zig, there is a placeholder
98 // that says "TODO version here".
97 std.debug.print("TODO version command not implemented yet\n", .{});99 std.debug.print("TODO version command not implemented yet\n", .{});
98 return error.Unimplemented;100 return error.Unimplemented;
99 } else if (mem.eql(u8, cmd, "zen")) {101 } else if (mem.eql(u8, cmd, "zen")) {
...@@ -492,6 +494,7 @@ fn buildOutputType(...@@ -492,6 +494,7 @@ fn buildOutputType(
492 defer root_pkg.destroy();494 defer root_pkg.destroy();
493495
494 var module = try Module.init(gpa, .{496 var module = try Module.init(gpa, .{
497 .root_name = root_name,
495 .target = target_info.target,498 .target = target_info.target,
496 .output_mode = output_mode,499 .output_mode = output_mode,
497 .root_pkg = root_pkg,500 .root_pkg = root_pkg,
src-self-hosted/test.zig+1
...@@ -433,6 +433,7 @@ pub const TestContext = struct {...@@ -433,6 +433,7 @@ pub const TestContext = struct {
433 defer allocator.free(bin_name);433 defer allocator.free(bin_name);
434434
435 var module = try Module.init(allocator, .{435 var module = try Module.init(allocator, .{
436 .root_name = "test_case",
436 .target = target,437 .target = target,
437 // TODO: support tests for object file building, and library builds438 // TODO: support tests for object file building, and library builds
438 // and linking. This will require a rework to support multi-file439 // and linking. This will require a rework to support multi-file