diff --git a/lib/std/build.zig b/lib/std/build.zig index 53f8f19df5e5..42726039fbc4 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -214,6 +214,17 @@ pub const Builder = struct { name, if (root_src) |p| FileSource{ .path = p } else null, false, + null, + ); + } + + pub fn addExecutableCustomExtension(self: *Builder, name: []const u8, root_src: ?[]const u8, custom_extension: ?[] const u8) *LibExeObjStep { + return LibExeObjStep.createExecutable( + self, + name, + if (root_src) |p| FileSource{ .path = p } else null, + false, + custom_extension, ); } @@ -228,7 +239,7 @@ pub const Builder = struct { .step = wfs, .basename = basename, }, - }), false); + }), false, null); } pub fn addExecutableSource( @@ -236,7 +247,7 @@ pub const Builder = struct { name: []const u8, root_src: ?FileSource, ) *LibExeObjStep { - return LibExeObjStep.createExecutable(self, name, root_src, false); + return LibExeObjStep.createExecutable(self, name, root_src, false, null); } pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { @@ -1129,6 +1140,7 @@ pub const LibExeObjStep = struct { version: Version, build_mode: builtin.Mode, kind: Kind, + emit: ?Emit, major_only_filename: []const u8, name_only_filename: []const u8, strip: bool, @@ -1199,6 +1211,8 @@ pub const LibExeObjStep = struct { subsystem: ?builtin.SubSystem = null, + custom_extension: ?[]const u8 = null, + const LinkObject = union(enum) { StaticPath: []const u8, OtherStep: *LibExeObjStep, @@ -1220,33 +1234,40 @@ pub const LibExeObjStep = struct { Test, }; + const Emit = enum { + Asm, + Bin, + LLVMIr, + Raw, + }; + pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource, ver: Version) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, ver); + self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, ver, null); return self; } pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, builder.version(0, 0, 0)); + self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, builder.version(0, 0, 0), null); return self; } pub fn createObject(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0)); + self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0), null); return self; } - pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?FileSource, is_dynamic: bool) *LibExeObjStep { + pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?FileSource, is_dynamic: bool, custom_extension: ?[]const u8,) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initExtraArgs(builder, name, root_src, Kind.Exe, is_dynamic, builder.version(0, 0, 0)); + self.* = initExtraArgs(builder, name, root_src, Kind.Exe, is_dynamic, builder.version(0, 0, 0), custom_extension); return self; } pub fn createTest(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initExtraArgs(builder, name, root_src, Kind.Test, false, builder.version(0, 0, 0)); + self.* = initExtraArgs(builder, name, root_src, Kind.Test, false, builder.version(0, 0, 0), null); return self; } @@ -1257,6 +1278,7 @@ pub const LibExeObjStep = struct { kind: Kind, is_dynamic: bool, ver: Version, + custom_extension: ?[]const u8, ) LibExeObjStep { if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { panic("invalid name: '{}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); @@ -1269,6 +1291,7 @@ pub const LibExeObjStep = struct { .build_mode = builtin.Mode.Debug, .is_dynamic = is_dynamic, .kind = kind, + .emit = null, .root_src = root_src, .name = name, .target = Target.Native, @@ -1305,6 +1328,7 @@ pub const LibExeObjStep = struct { .single_threaded = false, .installed_path = null, .install_step = null, + .custom_extension = custom_extension, }; self.computeOutFileNames(); if (root_src) |rs| rs.addStepDependencies(&self.step); @@ -1317,7 +1341,13 @@ pub const LibExeObjStep = struct { self.out_filename = self.builder.fmt("{}{}", .{ self.name, self.target.oFileExt() }); }, .Exe => { - self.out_filename = self.builder.fmt("{}{}", .{ self.name, self.target.exeFileExt() }); + const extension = blk: { + if (self.custom_extension) |ext| { + break :blk ext; + } + break :blk self.target.exeFileExt(); + }; + self.out_filename = self.builder.fmt("{}{}", .{ self.name, extension }); }, .Test => { self.out_filename = self.builder.fmt("test{}", .{self.target.exeFileExt()}); @@ -1384,6 +1414,10 @@ pub const LibExeObjStep = struct { self.computeOutFileNames(); } + pub fn setEmit(self: *LibExeObjStep, emit: Emit) void { + self.emit = emit; + } + pub fn setTargetGLibC(self: *LibExeObjStep, major: u32, minor: u32, patch: u32) void { self.target_glibc = Version{ .major = major, @@ -1835,6 +1869,18 @@ pub const LibExeObjStep = struct { if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder)); + if (self.emit) |emit| { + try zig_args.append("--emit"); + + const emitArg = switch (emit) { + .Asm => "asm", + .Bin => "bin", + .LLVMIr => "llvm-ir", + .Raw => "raw", + }; + try zig_args.append(emitArg); + } + for (self.link_objects.toSlice()) |link_object| { switch (link_object) { .StaticPath => |static_path| { @@ -1942,6 +1988,11 @@ pub const LibExeObjStep = struct { zig_args.append("--name") catch unreachable; zig_args.append(self.name) catch unreachable; + if (self.custom_extension) |ext| { + try zig_args.append("--custom-ext"); + try zig_args.append(ext); + } + if (self.kind == Kind.Lib and self.is_dynamic) { zig_args.append("--ver-major") catch unreachable; zig_args.append(builder.fmt("{}", .{self.version.major})) catch unreachable; diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 33b062db2385..1b70c4aba77b 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -928,8 +928,8 @@ fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Section { const elf_header = (try elf_file.findSection(name)) orelse return null; return DwarfInfo.Section{ - .offset = elf_header.offset, - .size = elf_header.size, + .offset = elf_header.sh_offset, + .size = elf_header.sh_size, }; } diff --git a/lib/std/elf.zig b/lib/std/elf.zig index dc1638c5cb30..9e4c1ac5f6ed 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -330,19 +330,8 @@ pub const ET = extern enum(u16) { pub const HIPROC = 0xffff; }; -/// TODO delete this in favor of Elf64_Shdr -pub const SectionHeader = struct { - name: u32, - sh_type: u32, - flags: u64, - addr: u64, - offset: u64, - size: u64, - link: u32, - info: u32, - addr_align: u64, - ent_size: u64, -}; +pub const SectionHeader = Elf64_Shdr; +pub const ProgramHeader = Elf64_Phdr; pub const Elf = struct { seekable_stream: *io.SeekableStream(anyerror, anyerror), @@ -357,6 +346,7 @@ pub const Elf = struct { string_section_index: usize, string_section: *SectionHeader, section_headers: []SectionHeader, + program_headers: []ProgramHeader, allocator: *mem.Allocator, /// Call close when done. @@ -421,14 +411,24 @@ pub const Elf = struct { try seekable_stream.seekBy(4); const header_size = try in.readInt(u16, elf.endian); - if ((elf.is_64 and header_size != 64) or (!elf.is_64 and header_size != 52)) { + if ((elf.is_64 and header_size != @sizeOf(Elf64_Ehdr)) or (!elf.is_64 and header_size != @sizeOf(Elf32_Ehdr))) { return error.InvalidFormat; } const ph_entry_size = try in.readInt(u16, elf.endian); const ph_entry_count = try in.readInt(u16, elf.endian); + + if ((elf.is_64 and ph_entry_size != @sizeOf(Elf64_Phdr)) or (!elf.is_64 and ph_entry_size != @sizeOf(Elf32_Phdr))) { + return error.InvalidFormat; + } + const sh_entry_size = try in.readInt(u16, elf.endian); const sh_entry_count = try in.readInt(u16, elf.endian); + + if ((elf.is_64 and sh_entry_size != @sizeOf(Elf64_Shdr)) or (!elf.is_64 and sh_entry_size != @sizeOf(Elf32_Shdr))) { + return error.InvalidFormat; + } + elf.string_section_index = @as(usize, try in.readInt(u16, elf.endian)); if (elf.string_section_index >= sh_entry_count) return error.InvalidFormat; @@ -443,47 +443,72 @@ pub const Elf = struct { return error.InvalidFormat; } + try seekable_stream.seekTo(elf.program_header_offset); + + elf.program_headers = try elf.allocator.alloc(ProgramHeader, ph_entry_count); + errdefer elf.allocator.free(elf.program_headers); + + if (elf.is_64) { + for (elf.program_headers) |*elf_program| { + elf_program.p_type = try in.readInt(Elf64_Word, elf.endian); + elf_program.p_flags = try in.readInt(Elf64_Word, elf.endian); + elf_program.p_offset = try in.readInt(Elf64_Off, elf.endian); + elf_program.p_vaddr = try in.readInt(Elf64_Addr, elf.endian); + elf_program.p_paddr = try in.readInt(Elf64_Addr, elf.endian); + elf_program.p_filesz = try in.readInt(Elf64_Xword, elf.endian); + elf_program.p_memsz = try in.readInt(Elf64_Xword, elf.endian); + elf_program.p_align = try in.readInt(Elf64_Xword, elf.endian); + } + } else { + for (elf.program_headers) |*elf_program| { + elf_program.p_type = @as(Elf64_Word, try in.readInt(Elf32_Word, elf.endian)); + elf_program.p_offset = @as(Elf64_Off, try in.readInt(Elf32_Off, elf.endian)); + elf_program.p_vaddr = @as(Elf64_Addr, try in.readInt(Elf32_Addr, elf.endian)); + elf_program.p_paddr = @as(Elf64_Addr, try in.readInt(Elf32_Addr, elf.endian)); + elf_program.p_filesz = @as(Elf64_Word, try in.readInt(Elf32_Word, elf.endian)); + elf_program.p_memsz = @as(Elf64_Word, try in.readInt(Elf32_Word, elf.endian)); + elf_program.p_flags = @as(Elf64_Word, try in.readInt(Elf32_Word, elf.endian)); + elf_program.p_align = @as(Elf64_Word, try in.readInt(Elf32_Word, elf.endian)); + } + } + try seekable_stream.seekTo(elf.section_header_offset); elf.section_headers = try elf.allocator.alloc(SectionHeader, sh_entry_count); errdefer elf.allocator.free(elf.section_headers); if (elf.is_64) { - if (sh_entry_size != 64) return error.InvalidFormat; - for (elf.section_headers) |*elf_section| { - elf_section.name = try in.readInt(u32, elf.endian); + elf_section.sh_name = try in.readInt(u32, elf.endian); elf_section.sh_type = try in.readInt(u32, elf.endian); - elf_section.flags = try in.readInt(u64, elf.endian); - elf_section.addr = try in.readInt(u64, elf.endian); - elf_section.offset = try in.readInt(u64, elf.endian); - elf_section.size = try in.readInt(u64, elf.endian); - elf_section.link = try in.readInt(u32, elf.endian); - elf_section.info = try in.readInt(u32, elf.endian); - elf_section.addr_align = try in.readInt(u64, elf.endian); - elf_section.ent_size = try in.readInt(u64, elf.endian); + elf_section.sh_flags = try in.readInt(u64, elf.endian); + elf_section.sh_addr = try in.readInt(u64, elf.endian); + elf_section.sh_offset = try in.readInt(u64, elf.endian); + elf_section.sh_size = try in.readInt(u64, elf.endian); + elf_section.sh_link = try in.readInt(u32, elf.endian); + elf_section.sh_info = try in.readInt(u32, elf.endian); + elf_section.sh_addralign = try in.readInt(u64, elf.endian); + elf_section.sh_entsize = try in.readInt(u64, elf.endian); } } else { - if (sh_entry_size != 40) return error.InvalidFormat; - for (elf.section_headers) |*elf_section| { // TODO (multiple occurrences) allow implicit cast from %u32 -> %u64 ? - elf_section.name = try in.readInt(u32, elf.endian); + elf_section.sh_name = try in.readInt(u32, elf.endian); elf_section.sh_type = try in.readInt(u32, elf.endian); - elf_section.flags = @as(u64, try in.readInt(u32, elf.endian)); - elf_section.addr = @as(u64, try in.readInt(u32, elf.endian)); - elf_section.offset = @as(u64, try in.readInt(u32, elf.endian)); - elf_section.size = @as(u64, try in.readInt(u32, elf.endian)); - elf_section.link = try in.readInt(u32, elf.endian); - elf_section.info = try in.readInt(u32, elf.endian); - elf_section.addr_align = @as(u64, try in.readInt(u32, elf.endian)); - elf_section.ent_size = @as(u64, try in.readInt(u32, elf.endian)); + elf_section.sh_flags = @as(u64, try in.readInt(u32, elf.endian)); + elf_section.sh_addr = @as(u64, try in.readInt(u32, elf.endian)); + elf_section.sh_offset = @as(u64, try in.readInt(u32, elf.endian)); + elf_section.sh_size = @as(u64, try in.readInt(u32, elf.endian)); + elf_section.sh_link = try in.readInt(u32, elf.endian); + elf_section.sh_info = try in.readInt(u32, elf.endian); + elf_section.sh_addralign = @as(u64, try in.readInt(u32, elf.endian)); + elf_section.sh_entsize = @as(u64, try in.readInt(u32, elf.endian)); } } for (elf.section_headers) |*elf_section| { if (elf_section.sh_type != SHT_NOBITS) { - const file_end_offset = try math.add(u64, elf_section.offset, elf_section.size); + const file_end_offset = try math.add(u64, elf_section.sh_offset, elf_section.sh_size); if (stream_end < file_end_offset) return error.InvalidFormat; } } @@ -499,13 +524,14 @@ pub const Elf = struct { pub fn close(elf: *Elf) void { elf.allocator.free(elf.section_headers); + elf.allocator.free(elf.program_headers); } pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader { section_loop: for (elf.section_headers) |*elf_section| { if (elf_section.sh_type == SHT_NULL) continue; - const name_offset = elf.string_section.offset + elf_section.name; + const name_offset = elf.string_section.sh_offset + elf_section.sh_name; try elf.seekable_stream.seekTo(name_offset); for (name) |expected_c| { @@ -523,7 +549,7 @@ pub const Elf = struct { } pub fn seekToSection(elf: *Elf, elf_section: *SectionHeader) !void { - try elf.seekable_stream.seekTo(elf_section.offset); + try elf.seekable_stream.seekTo(elf_section.sh_offset); } }; @@ -578,6 +604,26 @@ pub const Elf64_Ehdr = extern struct { e_shnum: Elf64_Half, e_shstrndx: Elf64_Half, }; +pub const Elf32_Phdr = extern struct { + p_type: Elf32_Word, + p_offset: Elf32_Off, + p_vaddr: Elf32_Addr, + p_paddr: Elf32_Addr, + p_filesz: Elf32_Word, + p_memsz: Elf32_Word, + p_flags: Elf32_Word, + p_align: Elf32_Word, +}; +pub const Elf64_Phdr = extern struct { + p_type: Elf64_Word, + p_flags: Elf64_Word, + p_offset: Elf64_Off, + p_vaddr: Elf64_Addr, + p_paddr: Elf64_Addr, + p_filesz: Elf64_Xword, + p_memsz: Elf64_Xword, + p_align: Elf64_Xword, +}; pub const Elf32_Shdr = extern struct { sh_name: Elf32_Word, sh_type: Elf32_Word, @@ -655,26 +701,6 @@ pub const Elf64_Rela = extern struct { r_info: Elf64_Xword, r_addend: Elf64_Sxword, }; -pub const Elf32_Phdr = extern struct { - p_type: Elf32_Word, - p_offset: Elf32_Off, - p_vaddr: Elf32_Addr, - p_paddr: Elf32_Addr, - p_filesz: Elf32_Word, - p_memsz: Elf32_Word, - p_flags: Elf32_Word, - p_align: Elf32_Word, -}; -pub const Elf64_Phdr = extern struct { - p_type: Elf64_Word, - p_flags: Elf64_Word, - p_offset: Elf64_Off, - p_vaddr: Elf64_Addr, - p_paddr: Elf64_Addr, - p_filesz: Elf64_Xword, - p_memsz: Elf64_Xword, - p_align: Elf64_Xword, -}; pub const Elf32_Dyn = extern struct { d_tag: Elf32_Sword, d_un: extern union { @@ -833,6 +859,17 @@ pub const Elf_MIPS_ABIFlags_v0 = extern struct { flags2: Elf32_Word, }; +comptime { + debug.assert(@sizeOf(Elf32_Ehdr) == 52); + debug.assert(@sizeOf(Elf64_Ehdr) == 64); + + debug.assert(@sizeOf(Elf32_Phdr) == 32); + debug.assert(@sizeOf(Elf64_Phdr) == 56); + + debug.assert(@sizeOf(Elf32_Shdr) == 40); + debug.assert(@sizeOf(Elf64_Shdr) == 64); +} + pub const Auxv = switch (@sizeOf(usize)) { 4 => Elf32_auxv_t, 8 => Elf64_auxv_t, diff --git a/src-self-hosted/emit_raw.zig b/src-self-hosted/emit_raw.zig new file mode 100644 index 000000000000..e572d3b5e0a2 --- /dev/null +++ b/src-self-hosted/emit_raw.zig @@ -0,0 +1,203 @@ +const Allocator = @import("std").mem.Allocator; +const ArenaAllocator = @import("std").heap.ArenaAllocator; +const ArrayList = @import("std").ArrayList; +const c_allocator = @import("std").heap.c_allocator; +const File = @import("std").fs.File; +const elf = @import("std").elf; +const fs = @import("std").fs; +const io = @import("std").io; +const sort = @import("std").sort; + +const BinOutStream = io.OutStream(anyerror); +const BinSeekStream = io.SeekableStream(anyerror, anyerror); +const ElfSeekStream = io.SeekableStream(anyerror, anyerror); +const ElfInStream = io.InStream(anyerror); + +const BinaryElfSection = struct { + elfOffset: u64, + binaryOffset: u64, + fileSize: usize, + segment: ?*BinaryElfSegment, +}; + +const BinaryElfSegment = struct { + physicalAddress: u64, + virtualAddress: u64, + elfOffset: u64, + binaryOffset: u64, + fileSize: usize, + firstSection: ?*BinaryElfSection, +}; + +const BinaryElfOutput = struct { + segments: ArrayList(*BinaryElfSegment), + sections: ArrayList(*BinaryElfSection), + + const Self = @This(); + + pub fn init(allocator: *Allocator) Self { + return Self{ + .segments = ArrayList(*BinaryElfSegment).init(allocator), + .sections = ArrayList(*BinaryElfSection).init(allocator), + }; + } + + pub fn deinit(self: *Self) void { + self.sections.deinit(); + self.segments.deinit(); + } + + pub fn parseElf(self: *Self, elfFile: elf.Elf) !void { + const allocator = self.segments.allocator; + + for (elfFile.section_headers) |section, i| { + if (sectionValidForOutput(section)) { + const newSection = try allocator.create(BinaryElfSection); + + newSection.binaryOffset = 0; + newSection.elfOffset = section.sh_offset; + newSection.fileSize = @intCast(usize, section.sh_size); + newSection.segment = null; + + try self.sections.append(newSection); + } + } + + for (elfFile.program_headers) |programHeader, i| { + if (programHeader.p_type == elf.PT_LOAD) { + const newSegment = try allocator.create(BinaryElfSegment); + + newSegment.physicalAddress = if (programHeader.p_paddr != 0) programHeader.p_paddr else programHeader.p_vaddr; + newSegment.virtualAddress = programHeader.p_vaddr; + newSegment.fileSize = @intCast(usize, programHeader.p_filesz); + newSegment.elfOffset = programHeader.p_offset; + newSegment.binaryOffset = 0; + newSegment.firstSection = null; + + for (self.sections.toSlice()) |section| { + if (sectionWithinSegment(section, programHeader)) { + if (section.segment) |sectionSegment| { + if (sectionSegment.elfOffset > newSegment.elfOffset) { + section.segment = newSegment; + } + } else { + section.segment = newSegment; + } + + if (newSegment.firstSection == null) { + newSegment.firstSection = section; + } + } + } + + try self.segments.append(newSegment); + } + } + + sort.sort(*BinaryElfSegment, self.segments.toSlice(), segmentSortCompare); + + if (self.segments.len > 0) { + const firstSegment = self.segments.at(0); + if (firstSegment.firstSection) |firstSection| { + const diff = firstSection.elfOffset - firstSegment.elfOffset; + + firstSegment.elfOffset += diff; + firstSegment.fileSize += diff; + firstSegment.physicalAddress += diff; + + const basePhysicalAddress = firstSegment.physicalAddress; + + for (self.segments.toSlice()) |segment| { + segment.binaryOffset = segment.physicalAddress - basePhysicalAddress; + } + } + } + + for (self.sections.toSlice()) |section| { + if (section.segment) |segment| { + section.binaryOffset = segment.binaryOffset + (section.elfOffset - segment.elfOffset); + } + } + + sort.sort(*BinaryElfSection, self.sections.toSlice(), sectionSortCompare); + } + + fn sectionWithinSegment(section: *BinaryElfSection, segment: elf.ProgramHeader) bool { + return segment.p_offset <= section.elfOffset and (segment.p_offset + segment.p_filesz) >= (section.elfOffset + section.fileSize); + } + + fn sectionValidForOutput(section: elf.SectionHeader) bool { + return section.sh_size > 0 and section.sh_type != elf.SHT_NOBITS and ((section.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC); + } + + fn segmentSortCompare(left: *BinaryElfSegment, right: *BinaryElfSegment) bool { + if (left.physicalAddress < right.physicalAddress) { + return true; + } + if (left.physicalAddress > right.physicalAddress) { + return false; + } + return false; + } + + fn sectionSortCompare(left: *BinaryElfSection, right: *BinaryElfSection) bool { + return left.binaryOffset < right.binaryOffset; + } +}; + +const WriteContext = struct { + inStream: *ElfInStream, + inSeekStream: *ElfSeekStream, + outStream: *BinOutStream, + outSeekStream: *BinSeekStream, +}; + +fn writeBinaryElfSection(allocator: *Allocator, context: WriteContext, section: *BinaryElfSection) !void { + var readBuffer = try allocator.alloc(u8, section.fileSize); + defer allocator.free(readBuffer); + + try context.inSeekStream.seekTo(section.elfOffset); + _ = try context.inStream.read(readBuffer); + + try context.outSeekStream.seekTo(section.binaryOffset); + try context.outStream.write(readBuffer); +} + +pub fn emit_raw(elf_path: []const u8, raw_path: []const u8) !void { + var arenaAlloc = ArenaAllocator.init(c_allocator); + errdefer arenaAlloc.deinit(); + var arena_allocator = &arenaAlloc.allocator; + + const currentDir = fs.cwd(); + + var file = try currentDir.openFile(elf_path, File.OpenFlags{}); + defer file.close(); + + var fileInStream = file.inStream(); + var fileSeekStream = file.seekableStream(); + + var elfFile = try elf.Elf.openStream(c_allocator, @ptrCast(*ElfSeekStream, &fileSeekStream.stream), @ptrCast(*ElfInStream, &fileInStream.stream)); + defer elfFile.close(); + + var outFile = try currentDir.createFile(raw_path, File.CreateFlags{}); + defer outFile.close(); + + var outFileOutStream = outFile.outStream(); + var outFileSeekStream = outFile.seekableStream(); + + const writeContext = WriteContext{ + .inStream = @ptrCast(*ElfInStream, &fileInStream.stream), + .inSeekStream = @ptrCast(*ElfSeekStream, &fileSeekStream.stream), + .outStream = @ptrCast(*BinOutStream, &outFileOutStream.stream), + .outSeekStream = @ptrCast(*BinSeekStream, &outFileSeekStream.stream), + }; + + var binaryElfOutput = BinaryElfOutput.init(arena_allocator); + defer binaryElfOutput.deinit(); + + try binaryElfOutput.parseElf(elfFile); + + for (binaryElfOutput.sections.toSlice()) |section| { + try writeBinaryElfSection(c_allocator, writeContext, section); + } +} diff --git a/src-self-hosted/stage1.zig b/src-self-hosted/stage1.zig index ec683e4ba882..5095e68aabb4 100644 --- a/src-self-hosted/stage1.zig +++ b/src-self-hosted/stage1.zig @@ -85,6 +85,7 @@ const Error = extern enum { const FILE = std.c.FILE; const ast = std.zig.ast; const translate_c = @import("translate_c.zig"); +const emit_raw = @import("emit_raw.zig"); /// Args should have a null terminating last arg. export fn stage2_translate_c( @@ -527,3 +528,8 @@ export fn stage2_progress_update_node(node: *std.Progress.Node, done_count: usiz node.activate(); node.context.maybeRefresh(); } + +// ABI warning +export fn stage2_emit_raw(elf_path_ptr: [*]const u8, elf_path_len: usize, raw_path_ptr: [*]const u8, raw_path_len: usize) void { + emit_raw.emit_raw(elf_path_ptr[0..elf_path_len], raw_path_ptr[0..raw_path_len]) catch @panic("failed to emit raw"); +} diff --git a/src/all_types.hpp b/src/all_types.hpp index df52c29a4e11..4566e3767d9c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1917,6 +1917,7 @@ enum EmitFileType { EmitFileTypeBinary, EmitFileTypeAssembly, EmitFileTypeLLVMIr, + EmitFileTypeRaw, }; struct LinkLib { @@ -2094,6 +2095,7 @@ struct CodeGen { Buf *output_dir; Buf **libc_include_dir_list; size_t libc_include_dir_len; + Buf *custom_extension; Buf *zig_c_headers_dir; // Cannot be overridden; derived from zig_lib_dir. Buf *zig_std_special_dir; // Cannot be overridden; derived from zig_lib_dir. diff --git a/src/codegen.cpp b/src/codegen.cpp index 3181b45aac9f..603a4893e23b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -147,6 +147,10 @@ void codegen_set_out_name(CodeGen *g, Buf *out_name) { g->root_out_name = out_name; } +void codegen_set_custom_extension(CodeGen* g, Buf* custom_extension) { + g->custom_extension = custom_extension; +} + void codegen_add_lib_dir(CodeGen *g, const char *dir) { g->lib_dirs.append(dir); } @@ -7938,6 +7942,22 @@ static void zig_llvm_emit_output(CodeGen *g) { validate_inline_fns(g); break; + case EmitFileTypeRaw: + if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path), + ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug, is_small, + g->enable_time_report)) + { + zig_panic("unable to write raw file %s: %s", buf_ptr(output_path), err_msg); + } + validate_inline_fns(g); + g->link_objects.append(output_path); + if (g->bundle_compiler_rt && (g->out_type == OutTypeObj || + (g->out_type == OutTypeLib && !g->is_dynamic))) + { + zig_link_add_compiler_rt(g, g->sub_progress_node); + } + break; + default: zig_unreachable(); } @@ -9434,6 +9454,25 @@ static void update_test_functions_builtin_decl(CodeGen *g) { assert(g->test_runner_package != nullptr); } +void codegen_emit_raw(CodeGen* g) +{ + codegen_add_time_event(g, "Emiting raw binary"); + + Buf* raw_file_path = buf_alloc(); + Buf* raw_file_name = buf_create_from_buf(g->root_out_name); + if (g->custom_extension) { + buf_append_buf(raw_file_name, g->custom_extension); + } + os_path_join(g->output_dir, raw_file_name, raw_file_path); + + stage2_emit_raw(buf_ptr(&g->output_file_path), buf_len(&g->output_file_path), buf_ptr(raw_file_path), buf_len(raw_file_path)); + + buf_destroy(raw_file_path); + buf_destroy(raw_file_name); + + os_delete_file(&g->output_file_path); +} + static Buf *get_resolved_root_src_path(CodeGen *g) { // TODO memoize if (buf_len(&g->main_pkg->root_src_path) == 0) @@ -10383,6 +10422,8 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { } cache_buf_opt(ch, g->dynamic_linker_path); cache_buf_opt(ch, g->version_script_path); + cache_buf_opt(ch, g->custom_extension); + cache_int(ch, (g->emit_file_type == EmitFileTypeRaw) ? 1 : 0); // gen_c_objects appends objects to g->link_objects which we want to include in the hash gen_c_objects(g); @@ -10432,31 +10473,36 @@ static void resolve_out_paths(CodeGen *g) { break; case OutTypeExe: buf_append_str(o_basename, target_o_file_ext(g->zig_target)); - buf_append_str(out_basename, target_exe_file_ext(g->zig_target)); + buf_append_str(out_basename, codegen_exe_file_ext(g)); break; case OutTypeLib: buf_append_str(o_basename, target_o_file_ext(g->zig_target)); buf_resize(out_basename, 0); buf_append_str(out_basename, target_lib_file_prefix(g->zig_target)); buf_append_buf(out_basename, g->root_out_name); - buf_append_str(out_basename, target_lib_file_ext(g->zig_target, !g->is_dynamic, + buf_append_str(out_basename, codegen_lib_file_ext(g, !g->is_dynamic, g->version_major, g->version_minor, g->version_patch)); break; } break; } case EmitFileTypeAssembly: { - const char *asm_ext = target_asm_file_ext(g->zig_target); + const char *asm_ext = codegen_asm_file_ext(g); buf_append_str(o_basename, asm_ext); buf_append_str(out_basename, asm_ext); break; } case EmitFileTypeLLVMIr: { - const char *llvm_ir_ext = target_llvm_ir_file_ext(g->zig_target); + const char *llvm_ir_ext = codegen_llvm_ir_file_ext(g); buf_append_str(o_basename, llvm_ir_ext); buf_append_str(out_basename, llvm_ir_ext); break; } + case EmitFileTypeRaw: { + buf_append_str(o_basename, target_o_file_ext(g->zig_target)); + buf_append_str(out_basename, "-rawtmp"); + break; + } } os_path_join(g->output_dir, o_basename, &g->o_file_output_path); @@ -10622,12 +10668,16 @@ void codegen_build_and_link(CodeGen *g) { // If there is more than one object, we have to link them (with -r). // Finally, if we didn't make an object from zig source, and we don't have caching enabled, // then we have an object from C source that we must copy to the output dir which we do with a -r link. - if (!g->disable_bin_generation && g->emit_file_type == EmitFileTypeBinary && + if (!g->disable_bin_generation && (g->emit_file_type == EmitFileTypeBinary || g->emit_file_type == EmitFileTypeRaw) && (g->out_type != OutTypeObj || g->link_objects.length > 1 || (!need_llvm_module(g) && !g->enable_cache))) { codegen_link(g); } + + if (g->emit_file_type == EmitFileTypeRaw) { + codegen_emit_raw(g); + } } codegen_release_caches(g); @@ -10635,6 +10685,28 @@ void codegen_build_and_link(CodeGen *g) { codegen_switch_sub_prog_node(g, nullptr); } +const char* codegen_asm_file_ext(CodeGen* g) { + return target_asm_file_ext(g->zig_target); +} + +const char* codegen_llvm_ir_file_ext(CodeGen* g) { + return target_llvm_ir_file_ext(g->zig_target); +} + +const char* codegen_exe_file_ext(CodeGen* g) { + if (g->custom_extension) { + return buf_ptr(g->custom_extension); + } + + return target_exe_file_ext(g->zig_target); +} + +const char* codegen_lib_file_ext(CodeGen* g, bool is_static, + size_t version_major, size_t version_minor, size_t version_patch) { + return target_lib_file_ext(g->zig_target, is_static, version_major, + version_minor, version_patch); +} + void codegen_release_caches(CodeGen *g) { while (g->caches_to_release.length != 0) { cache_release(g->caches_to_release.pop()); diff --git a/src/codegen.hpp b/src/codegen.hpp index b9bcf05d282d..cab7c1831d1c 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -31,6 +31,7 @@ void codegen_set_emit_file_type(CodeGen *g, EmitFileType emit_file_type); void codegen_set_strip(CodeGen *codegen, bool strip); void codegen_set_errmsg_color(CodeGen *codegen, ErrColor err_color); void codegen_set_out_name(CodeGen *codegen, Buf *out_name); +void codegen_set_custom_extension(CodeGen *codegen, Buf *custom_extension); void codegen_add_lib_dir(CodeGen *codegen, const char *dir); void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib); LinkLib *codegen_add_link_lib(CodeGen *codegen, Buf *lib); @@ -49,6 +50,12 @@ void codegen_link(CodeGen *g); void zig_link_add_compiler_rt(CodeGen *g, Stage2ProgressNode *progress_node); void codegen_build_and_link(CodeGen *g); +const char* codegen_asm_file_ext(CodeGen *g); +const char* codegen_llvm_ir_file_ext(CodeGen *g); +const char* codegen_exe_file_ext(CodeGen *g); +const char* codegen_lib_file_ext(CodeGen *g, bool is_static, + size_t version_major, size_t version_minor, size_t version_patch); + ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path, const char *pkg_path); void codegen_add_assembly(CodeGen *g, Buf *path); @@ -56,6 +63,8 @@ void codegen_add_object(CodeGen *g, Buf *object_path); void codegen_translate_c(CodeGen *g, Buf *full_path); +void codegen_emit_raw(CodeGen* g); + Buf *codegen_generate_builtin_source(CodeGen *g); TargetSubsystem detect_subsystem(CodeGen *g); diff --git a/src/main.cpp b/src/main.cpp index fd50c1b145fe..178f00d733a4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -53,6 +53,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " --cache-dir [path] override the local cache directory\n" " --cache [auto|off|on] build in cache, print output path to stdout\n" " --color [auto|off|on] enable or disable colored error messages\n" + " --custom-ext [extension] override extension of the output\n" " --disable-gen-h do not generate a C header file (.h)\n" " --disable-valgrind omit valgrind client requests in debug builds\n" " --eh-frame-hdr enable C++ exception handling by passing --eh-frame-hdr to linker\n" @@ -61,7 +62,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " -fno-stack-check disable stack probing in safe builds\n" " -fsanitize-c enable C undefined behavior detection in unsafe builds\n" " -fno-sanitize-c disable C undefined behavior detection in safe builds\n" - " --emit [asm|bin|llvm-ir] emit a specific file format as compilation output\n" + " --emit [asm|bin|raw|llvm-ir] emit a specific file format as compilation output\n" " -fPIC enable Position Independent Code\n" " -fno-PIC disable Position Independent Code\n" " -ftime-report print timing diagnostics\n" @@ -471,6 +472,7 @@ int main(int argc, char **argv) { bool is_dynamic = false; OutType out_type = OutTypeUnknown; const char *out_name = nullptr; + Buf *custom_extension = nullptr; bool verbose_tokenize = false; bool verbose_ast = false; bool verbose_link = false; @@ -815,12 +817,16 @@ int main(int argc, char **argv) { emit_file_type = EmitFileTypeBinary; } else if (strcmp(argv[i], "llvm-ir") == 0) { emit_file_type = EmitFileTypeLLVMIr; + } else if (strcmp(argv[i], "raw") == 0) { + emit_file_type = EmitFileTypeRaw; } else { - fprintf(stderr, "--emit options are 'asm', 'bin', or 'llvm-ir'\n"); + fprintf(stderr, "--emit options are 'asm', 'bin', 'raw', or 'llvm-ir'\n"); return print_error_usage(arg0); } } else if (strcmp(arg, "--name") == 0) { out_name = argv[i]; + } else if(strcmp(arg, "--custom-ext") == 0) { + custom_extension = buf_create_from_str(argv[i]); } else if (strcmp(arg, "--dynamic-linker") == 0) { dynamic_linker = buf_create_from_str(argv[i]); } else if (strcmp(arg, "--libc") == 0) { @@ -1068,6 +1074,11 @@ int main(int argc, char **argv) { return print_error_usage(arg0); } + if (emit_file_type == EmitFileTypeRaw && target_object_format(&target) != ZigLLVM_ELF) { + fprintf(stderr, "Emit raw is only supported on target that use the ELF file format\n"); + return print_error_usage(arg0); + } + if (llvm_argv.length > 1) { llvm_argv.append(nullptr); ZigLLVMParseCommandLineOptions(llvm_argv.length - 1, llvm_argv.items); @@ -1207,6 +1218,7 @@ int main(int argc, char **argv) { g->enable_doc_generation = enable_doc_generation; g->disable_bin_generation = disable_bin_generation; codegen_set_out_name(g, buf_out_name); + codegen_set_custom_extension(g, custom_extension); codegen_set_lib_version(g, ver_major, ver_minor, ver_patch); g->want_single_threaded = want_single_threaded; codegen_set_linker_script(g, linker_script); diff --git a/src/userland.cpp b/src/userland.cpp index 263ef0cbc38c..66f8d92c18a2 100644 --- a/src/userland.cpp +++ b/src/userland.cpp @@ -59,6 +59,10 @@ stage2_DepNextResult stage2_DepTokenizer_next(stage2_DepTokenizer *self) { stage2_panic(msg, strlen(msg)); } +void stage2_emit_raw(const char* elf_path_ptr, size_t elf_path_len, const char* raw_path_ptr, size_t raw_path_len) { + const char* msg = "stage0 called stage2_emit_raw"; + stage2_panic(msg, strlen(msg)); +} struct Stage2Progress { int trash; diff --git a/src/userland.h b/src/userland.h index fe3f072ae569..5fa8a90a3abb 100644 --- a/src/userland.h +++ b/src/userland.h @@ -118,6 +118,9 @@ ZIG_EXTERN_C ZIG_ATTRIBUTE_NORETURN void stage2_panic(const char *ptr, size_t le // ABI warning ZIG_EXTERN_C int stage2_fmt(int argc, char **argv); +// ABI warning +ZIG_EXTERN_C void stage2_emit_raw(const char* elf_path_ptr, size_t elf_path_len, const char* raw_path_ptr, size_t raw_path_len); + // ABI warning struct stage2_DepTokenizer { void *handle;