authorgravatar for xavierb@gmail.comXavier Bouchoux <xavierb@gmail.com> 2023-03-12 11:08:48+01:00
committergravatar for xavierb@gmail.comXavier Bouchoux <xavierb@gmail.com> 2023-03-20 08:39:23+01:00
log9ea404f03d53c3589043c9a2d6f9b9e06ec86e20
tree8bf8dbab5109b1cf8b570b62ab53906116f8856e
parentb014ffc74f5c2c497ef62e1f77132287288f8bba

objcopy: add support for stripping 32bits ELF files


1 files changed, 504 insertions(+), 480 deletions(-)

src/objcopy.zig+504-480
......@@ -143,8 +143,6 @@ pub fn cmdObjCopy(
143143 .elf => {
144144 if (elf_hdr.endian != @import("builtin").target.cpu.arch.endian())
145145 fatal("zig objcopy: ELF to ELF copying only supports native endian", .{});
146 if (!elf_hdr.is_64)
147 fatal("zig objcopy: ELF to ELF copying only supports 64-bit files", .{});
148146 if (elf_hdr.phoff == 0) // no program header
149147 fatal("zig objcopy: ELF to ELF copying only supports programs", .{});
150148 if (only_section) |_|
......@@ -675,568 +673,594 @@ fn stripElf(
675673 elf_hdr: elf.Header,
676674 options: StripElfOptions,
677675) !void {
678 const filter: ElfContents.Filter = filter: {
679 if (options.only_keep_debug) break :filter .debug;
680 if (options.strip_all) break :filter .program;
681 if (options.strip_debug) break :filter .program_and_symbols;
682 break :filter .all;
683 };
684
685 var elf_contents = try ElfContents.parse(allocator, in_file, elf_hdr);
686 defer elf_contents.deinit();
676 switch (elf_hdr.is_64) {
677 inline else => |is_64| {
678 const Elf = ElfContents(is_64);
679 const Filter = Elf.Filter;
680 const DebugLink = Elf.DebugLink;
681
682 var elf_contents = try Elf.parse(allocator, in_file, elf_hdr);
683 defer elf_contents.deinit();
684
685 const filter: Filter = filter: {
686 if (options.only_keep_debug) break :filter .debug;
687 if (options.strip_all) break :filter .program;
688 if (options.strip_debug) break :filter .program_and_symbols;
689 break :filter .all;
690 };
687691
688 if (options.extract_to) |filename| {
689 const dbg_file = std.fs.cwd().createFile(filename, .{}) catch |err| {
690 fatal("zig objcopy: unable to create '{s}': {s}", .{ filename, @errorName(err) });
691 };
692 defer dbg_file.close();
693
694 const filter_complement: ElfContents.Filter = switch (filter) {
695 .program => .debug_and_symbols,
696 .debug => .program_and_symbols,
697 .program_and_symbols => .debug,
698 .debug_and_symbols => .program,
699 .all => fatal("zig objcopy: nothing to extract", .{}),
700 };
692 if (options.extract_to) |filename| {
693 const dbg_file = std.fs.cwd().createFile(filename, .{}) catch |err| {
694 fatal("zig objcopy: unable to create '{s}': {s}", .{ filename, @errorName(err) });
695 };
696 defer dbg_file.close();
697
698 const filter_complement: Filter = switch (filter) {
699 .program => .debug_and_symbols,
700 .debug => .program_and_symbols,
701 .program_and_symbols => .debug,
702 .debug_and_symbols => .program,
703 .all => fatal("zig objcopy: nothing to extract", .{}),
704 };
701705
702 try elf_contents.emit(allocator, dbg_file, in_file, filter_complement, null);
703 }
706 try elf_contents.emit(allocator, dbg_file, in_file, filter_complement, null);
707 }
704708
705 const debuglink: ?ElfContents.DebugLink = blk: {
706 const debuglink_filename = name: {
707 if (options.add_debuglink) |filename| break :name filename;
708 if (options.extract_to) |filename| break :name filename;
709 break :name null;
710 };
711 if (debuglink_filename) |filename| {
712 const dbg_file = std.fs.cwd().openFile(filename, .{}) catch |err| {
713 fatal("zig objcopy: could not read `{s}`: {s}\n", .{ filename, @errorName(err) });
714 };
715 defer dbg_file.close();
709 const debuglink: ?DebugLink = blk: {
710 const debuglink_filename = name: {
711 if (options.add_debuglink) |filename| break :name filename;
712 if (options.extract_to) |filename| break :name filename;
713 break :name null;
714 };
715 if (debuglink_filename) |filename| {
716 const dbg_file = std.fs.cwd().openFile(filename, .{}) catch |err| {
717 fatal("zig objcopy: could not read `{s}`: {s}\n", .{ filename, @errorName(err) });
718 };
719 defer dbg_file.close();
716720
717 break :blk .{
718 .name = std.fs.path.basename(filename),
719 .crc32 = try computeFileCrc(dbg_file),
721 break :blk .{
722 .name = std.fs.path.basename(filename),
723 .crc32 = try computeFileCrc(dbg_file),
724 };
725 } else {
726 break :blk null;
727 }
720728 };
721 } else {
722 break :blk null;
723 }
724 };
725729
726 try elf_contents.emit(allocator, out_file, in_file, filter, debuglink);
730 try elf_contents.emit(allocator, out_file, in_file, filter, debuglink);
731 },
732 }
727733}
728734
729735// note: this is "a minimal effort implementation"
730736// It doesn't support all possibile elf files: some sections type may need fixups, the program header may need fix up, ...
731737// it was written for a specific use case (strip debug info to a sperate file, for linux 64-bits executables built with `zig` or `zig c++` )
732738// It manupulates and reoders the sections as little as possible to avoid having to do fixups.
733// TODO: support 32-bit files
734739// TODO: support non-native endianess
735740
736const ElfContents = struct {
737 raw_elf_header: elf.Elf64_Ehdr,
738 program_segments: []const elf.Elf64_Phdr,
739 sections: []const Section,
740 arena: std.heap.ArenaAllocator,
741
742 const section_memory_align = @alignOf(elf.Elf64_Sym); // most restrictive of what we may load in memory
743 const Section = struct {
744 section: elf.Elf64_Shdr,
745 name: []const u8 = "",
746 segment: ?*const elf.Elf64_Phdr = null, // if the section is used by a program segment (there can be more than one)
747 payload: ?[]align(section_memory_align) const u8 = null, // if we need the data in memory
748 usage: Usage = .none, // should the section be kept in the exe or stripped to the debug database, or both.
749
750 const Usage = enum { common, exe, debug, symbols, none };
751 };
741fn ElfContents(comptime is_64: bool) type {
742 const Elf_Ehdr = if (is_64) elf.Elf64_Ehdr else elf.Elf32_Ehdr;
743 const Elf_Phdr = if (is_64) elf.Elf64_Phdr else elf.Elf32_Phdr;
744 const Elf_Shdr = if (is_64) elf.Elf64_Shdr else elf.Elf32_Shdr;
745 const Elf_Sym = if (is_64) elf.Elf64_Sym else elf.Elf32_Sym;
746 const Elf_Verdef = if (is_64) elf.Elf64_Verdef else elf.Elf32_Verdef;
747 const Elf_OffSize = if (is_64) elf.Elf64_Off else elf.Elf32_Off;
748
749 return struct {
750 raw_elf_header: Elf_Ehdr,
751 program_segments: []const Elf_Phdr,
752 sections: []const Section,
753 arena: std.heap.ArenaAllocator,
754
755 const section_memory_align = @alignOf(Elf_Sym); // most restrictive of what we may load in memory
756 const Section = struct {
757 section: Elf_Shdr,
758 name: []const u8 = "",
759 segment: ?*const Elf_Phdr = null, // if the section is used by a program segment (there can be more than one)
760 payload: ?[]align(section_memory_align) const u8 = null, // if we need the data in memory
761 usage: Usage = .none, // should the section be kept in the exe or stripped to the debug database, or both.
762
763 const Usage = enum { common, exe, debug, symbols, none };
764 };
752765
753 const Self = @This();
766 const Self = @This();
754767
755 pub fn parse(gpa: Allocator, source: File, header: elf.Header) !Self {
756 var arena = std.heap.ArenaAllocator.init(gpa);
757 errdefer arena.deinit();
758 const allocator = arena.allocator();
759
760 var raw_header: elf.Elf64_Ehdr = undefined;
761 try source.seekableStream().seekTo(0);
762 try source.reader().readNoEof(std.mem.asBytes(&raw_header));
763
764 // program header: list of segments
765 const program_segments = blk: {
766 const program_header = try allocator.alloc(elf.Elf64_Phdr, header.phnum);
767 var i: u32 = 0;
768 var it = header.program_header_iterator(source);
769 while (try it.next()) |hdr| {
770 program_header[i] = hdr;
771 i += 1;
772 }
773 break :blk @ptrCast([]const elf.Elf64_Phdr, program_header[0..i]);
774 };
768 pub fn parse(gpa: Allocator, source: File, header: elf.Header) !Self {
769 var arena = std.heap.ArenaAllocator.init(gpa);
770 errdefer arena.deinit();
771 const allocator = arena.allocator();
775772
776 // section header
777 const sections = blk: {
778 const section_header = try allocator.alloc(Section, header.shnum);
779 var it = header.section_header_iterator(source);
780 var i: u32 = 0;
781 while (try it.next()) |hdr| {
782 section_header[i] = .{ .section = hdr };
783 i += 1;
773 var raw_header: Elf_Ehdr = undefined;
774 {
775 const bytes_read = try source.preadAll(std.mem.asBytes(&raw_header), 0);
776 if (bytes_read < @sizeOf(Elf_Ehdr))
777 return error.TRUNCATED_ELF;
784778 }
785 break :blk section_header[0..i];
786 };
787779
788 // load data to memory for some sections:
789 // string tables for access
790 // sections than need modifications when other sections move.
791 for (sections, 0..) |*section, idx| {
792 const need_data = switch (section.section.sh_type) {
793 elf.DT_VERSYM => true,
794 elf.SHT_SYMTAB, elf.SHT_DYNSYM => true,
795 else => false,
780 // program header: list of segments
781 const program_segments = blk: {
782 if (@sizeOf(Elf_Phdr) != header.phentsize)
783 fatal("zig objcopy: unsuported ELF file, unexpected phentsize ({d})", .{header.phentsize});
784
785 const program_header = try allocator.alloc(Elf_Phdr, header.phnum);
786 const bytes_read = try source.preadAll(std.mem.sliceAsBytes(program_header), header.phoff);
787 if (bytes_read < @sizeOf(Elf_Phdr) * header.phnum)
788 return error.TRUNCATED_ELF;
789 break :blk program_header;
796790 };
797 const need_strings = (idx == header.shstrndx);
798791
799 if (need_data or need_strings) {
800 const buffer = try allocator.alignedAlloc(u8, section_memory_align, @intCast(usize, section.section.sh_size));
801 const bytes_read = try source.preadAll(buffer, section.section.sh_offset);
802 if (bytes_read != section.section.sh_size) return error.TRUNCATED_ELF;
803 section.payload = buffer;
804 }
805 }
792 // section header
793 const sections = blk: {
794 if (@sizeOf(Elf_Shdr) != header.shentsize)
795 fatal("zig objcopy: unsuported ELF file, unexpected shentsize ({d})", .{header.shentsize});
806796
807 // fill-in sections info:
808 // resolve the name
809 // find if a program segment uses the section
810 // classify sections usage (used by program segments, debug datadase, common metadata, symbol table)
811 for (sections) |*section| {
812 section.segment = for (program_segments) |*seg| {
813 if (sectionWithinSegment(section.section, seg.*)) break seg;
814 } else null;
815
816 if (section.section.sh_name != 0 and header.shstrndx != elf.SHN_UNDEF)
817 section.name = std.mem.span(@ptrCast([*:0]const u8, &sections[header.shstrndx].payload.?[section.section.sh_name]));
818
819 const usage_from_program: Section.Usage = if (section.segment != null) .exe else .debug;
820 section.usage = switch (section.section.sh_type) {
821 elf.SHT_NOTE => .common,
822 elf.SHT_SYMTAB => .symbols, // "strip all" vs "strip only debug"
823 elf.SHT_DYNSYM => .exe,
824 elf.SHT_PROGBITS => usage: {
825 if (std.mem.eql(u8, section.name, ".comment")) break :usage .exe;
826 if (std.mem.eql(u8, section.name, ".gnu_debuglink")) break :usage .none;
827 break :usage usage_from_program;
828 },
829 elf.SHT_LOPROC...elf.SHT_HIPROC => .common, // don't strip unkonwn sections
830 elf.SHT_LOUSER...elf.SHT_HIUSER => .common, // don't strip unkonwn sections
831 else => usage_from_program,
832 };
833 }
797 const section_header = try allocator.alloc(Section, header.shnum);
834798
835 sections[0].usage = .common; // mandatory null section
836 if (header.shstrndx != elf.SHN_UNDEF)
837 sections[header.shstrndx].usage = .common; // string table for the headers
838
839 // recursive dependencies
840 var dirty: u1 = 1;
841 while (dirty != 0) {
842 dirty = 0;
843
844 const Local = struct {
845 fn propagateUsage(cur: *Section.Usage, new: Section.Usage) u1 {
846 const use: Section.Usage = switch (cur.*) {
847 .none => new,
848 .common => .common,
849 .debug => switch (new) {
850 .none, .debug => .debug,
851 else => new,
852 },
853 .exe => switch (new) {
854 .common => .common,
855 .none, .debug, .exe => .exe,
856 .symbols => .exe,
857 },
858 .symbols => switch (new) {
859 .none, .common, .debug, .exe => unreachable,
860 .symbols => .symbols,
861 },
862 };
799 const raw_section_header = try allocator.alloc(Elf_Shdr, header.shnum);
800 defer allocator.free(raw_section_header);
801 const bytes_read = try source.preadAll(std.mem.sliceAsBytes(raw_section_header), header.shoff);
802 if (bytes_read < @sizeOf(Elf_Phdr) * header.shnum)
803 return error.TRUNCATED_ELF;
863804
864 if (cur.* != use) {
865 cur.* = use;
866 return 1;
867 } else {
868 return 0;
869 }
805 for (section_header, raw_section_header) |*section, hdr| {
806 section.* = .{ .section = hdr };
870807 }
808 break :blk section_header;
871809 };
872810
873 for (sections) |*section| {
874 if (section.section.sh_link != elf.SHN_UNDEF)
875 dirty |= Local.propagateUsage(&sections[section.section.sh_link].usage, section.usage);
876 if ((section.section.sh_flags & elf.SHF_INFO_LINK) != 0 and section.section.sh_info != elf.SHN_UNDEF)
877 dirty |= Local.propagateUsage(&sections[section.section.sh_info].usage, section.usage);
878
879 if (section.payload) |data| {
880 switch (section.section.sh_type) {
881 elf.DT_VERSYM => {
882 std.debug.assert(section.section.sh_entsize == @sizeOf(elf.Elf64_Verdef));
883 const defs = @ptrCast([*]const elf.Elf64_Verdef, data)[0 .. @intCast(usize, section.section.sh_size) / @sizeOf(elf.Elf64_Verdef)];
884 for (defs) |def| {
885 if (def.vd_ndx != elf.SHN_UNDEF)
886 dirty |= Local.propagateUsage(&sections[def.vd_ndx].usage, section.usage);
887 }
888 },
889 elf.SHT_SYMTAB, elf.SHT_DYNSYM => {
890 std.debug.assert(section.section.sh_entsize == @sizeOf(elf.Elf64_Sym));
891 const syms = @ptrCast([*]const elf.Elf64_Sym, data)[0 .. @intCast(usize, section.section.sh_size) / @sizeOf(elf.Elf64_Sym)];
811 // load data to memory for some sections:
812 // string tables for access
813 // sections than need modifications when other sections move.
814 for (sections, 0..) |*section, idx| {
815 const need_data = switch (section.section.sh_type) {
816 elf.DT_VERSYM => true,
817 elf.SHT_SYMTAB, elf.SHT_DYNSYM => true,
818 else => false,
819 };
820 const need_strings = (idx == header.shstrndx);
892821
893 for (syms) |sym| {
894 if (sym.st_shndx != elf.SHN_UNDEF and sym.st_shndx < elf.SHN_LORESERVE)
895 dirty |= Local.propagateUsage(&sections[sym.st_shndx].usage, section.usage);
896 }
897 },
898 else => {},
899 }
822 if (need_data or need_strings) {
823 const buffer = try allocator.alignedAlloc(u8, section_memory_align, @intCast(usize, section.section.sh_size));
824 const bytes_read = try source.preadAll(buffer, section.section.sh_offset);
825 if (bytes_read != section.section.sh_size) return error.TRUNCATED_ELF;
826 section.payload = buffer;
900827 }
901828 }
902 }
903829
904 return Self{
905 .arena = arena,
906 .raw_elf_header = raw_header,
907 .program_segments = program_segments,
908 .sections = sections,
909 };
910 }
830 // fill-in sections info:
831 // resolve the name
832 // find if a program segment uses the section
833 // classify sections usage (used by program segments, debug datadase, common metadata, symbol table)
834 for (sections) |*section| {
835 section.segment = for (program_segments) |*seg| {
836 if (sectionWithinSegment(section.section, seg.*)) break seg;
837 } else null;
838
839 if (section.section.sh_name != 0 and header.shstrndx != elf.SHN_UNDEF)
840 section.name = std.mem.span(@ptrCast([*:0]const u8, &sections[header.shstrndx].payload.?[section.section.sh_name]));
841
842 const usage_from_program: Section.Usage = if (section.segment != null) .exe else .debug;
843 section.usage = switch (section.section.sh_type) {
844 elf.SHT_NOTE => .common,
845 elf.SHT_SYMTAB => .symbols, // "strip all" vs "strip only debug"
846 elf.SHT_DYNSYM => .exe,
847 elf.SHT_PROGBITS => usage: {
848 if (std.mem.eql(u8, section.name, ".comment")) break :usage .exe;
849 if (std.mem.eql(u8, section.name, ".gnu_debuglink")) break :usage .none;
850 break :usage usage_from_program;
851 },
852 elf.SHT_LOPROC...elf.SHT_HIPROC => .common, // don't strip unkonwn sections
853 elf.SHT_LOUSER...elf.SHT_HIUSER => .common, // don't strip unkonwn sections
854 else => usage_from_program,
855 };
856 }
911857
912 pub fn deinit(self: *Self) void {
913 self.arena.deinit();
914 }
858 sections[0].usage = .common; // mandatory null section
859 if (header.shstrndx != elf.SHN_UNDEF)
860 sections[header.shstrndx].usage = .common; // string table for the headers
915861
916 const DebugLink = struct { name: []const u8, crc32: u32 };
917 const Filter = enum { all, program, debug, program_and_symbols, debug_and_symbols };
918 fn emit(self: *const Self, gpa: Allocator, output: File, source: File, filter: Filter, debuglink: ?DebugLink) !void {
919 var arena = std.heap.ArenaAllocator.init(gpa);
920 defer arena.deinit();
921 const allocator = arena.allocator();
862 // recursive dependencies
863 var dirty: u1 = 1;
864 while (dirty != 0) {
865 dirty = 0;
922866
923 // when emitting the stripped exe:
924 // - unused sections are removed
925 // when emitting the debug file:
926 // - all sections are kept, but some are emptied and their types is changed to SHT_NOBITS
927 // the program header is kept unchanged. (`strip` does update it, but `eu-strip` does not, and it still works)
867 const Local = struct {
868 fn propagateUsage(cur: *Section.Usage, new: Section.Usage) u1 {
869 const use: Section.Usage = switch (cur.*) {
870 .none => new,
871 .common => .common,
872 .debug => switch (new) {
873 .none, .debug => .debug,
874 else => new,
875 },
876 .exe => switch (new) {
877 .common => .common,
878 .none, .debug, .exe => .exe,
879 .symbols => .exe,
880 },
881 .symbols => switch (new) {
882 .none, .common, .debug, .exe => unreachable,
883 .symbols => .symbols,
884 },
885 };
928886
929 const Update = struct {
930 action: enum { keep, strip, empty },
887 if (cur.* != use) {
888 cur.* = use;
889 return 1;
890 } else {
891 return 0;
892 }
893 }
894 };
931895
932 // remap the indexs after omitting the filtered sections
933 remap_idx: u16,
896 for (sections) |*section| {
897 if (section.section.sh_link != elf.SHN_UNDEF)
898 dirty |= Local.propagateUsage(&sections[section.section.sh_link].usage, section.usage);
899 if ((section.section.sh_flags & elf.SHF_INFO_LINK) != 0 and section.section.sh_info != elf.SHN_UNDEF)
900 dirty |= Local.propagateUsage(&sections[section.section.sh_info].usage, section.usage);
934901
935 // optionally overrides the payload from the source file
936 payload: ?[]align(section_memory_align) const u8,
937 };
938 const sections_update = try allocator.alloc(Update, self.sections.len);
939 const new_shnum = blk: {
940 var next_idx: u16 = 0;
941 for (self.sections, sections_update) |section, *update| {
942 update.action = action: {
943 if (section.usage == .none) break :action .strip;
944 break :action switch (filter) {
945 .all => switch (section.usage) {
946 .none => .strip,
947 else => .keep,
948 },
949 .program => switch (section.usage) {
950 .common, .exe => .keep,
951 else => .strip,
952 },
953 .program_and_symbols => switch (section.usage) {
954 .common, .exe, .symbols => .keep,
955 else => .strip,
956 },
957 .debug => switch (section.usage) {
958 .exe, .symbols => .empty,
959 .none => .strip,
960 else => .keep,
961 },
962 .debug_and_symbols => switch (section.usage) {
963 .exe => .empty,
964 .none => .strip,
965 else => .keep,
966 },
967 };
968 };
902 if (section.payload) |data| {
903 switch (section.section.sh_type) {
904 elf.DT_VERSYM => {
905 std.debug.assert(section.section.sh_entsize == @sizeOf(Elf_Verdef));
906 const defs = @ptrCast([*]const Elf_Verdef, data)[0 .. @intCast(usize, section.section.sh_size) / @sizeOf(Elf_Verdef)];
907 for (defs) |def| {
908 if (def.vd_ndx != elf.SHN_UNDEF)
909 dirty |= Local.propagateUsage(&sections[def.vd_ndx].usage, section.usage);
910 }
911 },
912 elf.SHT_SYMTAB, elf.SHT_DYNSYM => {
913 std.debug.assert(section.section.sh_entsize == @sizeOf(Elf_Sym));
914 const syms = @ptrCast([*]const Elf_Sym, data)[0 .. @intCast(usize, section.section.sh_size) / @sizeOf(Elf_Sym)];
969915
970 if (update.action == .strip) {
971 update.remap_idx = elf.SHN_UNDEF;
972 } else {
973 update.remap_idx = next_idx;
974 next_idx += 1;
916 for (syms) |sym| {
917 if (sym.st_shndx != elf.SHN_UNDEF and sym.st_shndx < elf.SHN_LORESERVE)
918 dirty |= Local.propagateUsage(&sections[sym.st_shndx].usage, section.usage);
919 }
920 },
921 else => {},
922 }
923 }
975924 }
976
977 update.payload = null;
978925 }
979926
980 if (debuglink != null)
981 next_idx += 1;
982 break :blk next_idx;
983 };
984
985 const debuglink_name: elf.Elf64_Word = blk: {
986 if (debuglink == null) break :blk elf.SHN_UNDEF;
987 if (self.raw_elf_header.e_shstrndx == elf.SHN_UNDEF)
988 fatal("zig objcopy: no strtab, cannot add the debuglink section", .{}); // TODO add the section if needed?
927 return Self{
928 .arena = arena,
929 .raw_elf_header = raw_header,
930 .program_segments = program_segments,
931 .sections = sections,
932 };
933 }
989934
990 const strtab = &self.sections[self.raw_elf_header.e_shstrndx];
991 const update = &sections_update[self.raw_elf_header.e_shstrndx];
935 pub fn deinit(self: *Self) void {
936 self.arena.deinit();
937 }
992938
993 const name: []const u8 = ".gnu_debuglink";
994 const new_offset = @intCast(u32, strtab.payload.?.len);
995 const buf = try allocator.alignedAlloc(u8, section_memory_align, new_offset + name.len + 1);
996 std.mem.copy(u8, buf[0..new_offset], strtab.payload.?);
997 std.mem.copy(u8, buf[new_offset .. new_offset + name.len], name);
998 buf[new_offset + name.len] = 0;
939 const DebugLink = struct { name: []const u8, crc32: u32 };
940 const Filter = enum { all, program, debug, program_and_symbols, debug_and_symbols };
941 fn emit(self: *const Self, gpa: Allocator, output: File, source: File, filter: Filter, debuglink: ?DebugLink) !void {
942 var arena = std.heap.ArenaAllocator.init(gpa);
943 defer arena.deinit();
944 const allocator = arena.allocator();
999945
1000 std.debug.assert(update.action == .keep);
1001 update.payload = buf;
946 // when emitting the stripped exe:
947 // - unused sections are removed
948 // when emitting the debug file:
949 // - all sections are kept, but some are emptied and their types is changed to SHT_NOBITS
950 // the program header is kept unchanged. (`strip` does update it, but `eu-strip` does not, and it still works)
1002951
1003 break :blk new_offset;
1004 };
952 const Update = struct {
953 action: enum { keep, strip, empty },
1005954
1006 const WriteCmd = union(enum) {
1007 copy_range: struct { in_offset: u64, len: u64, out_offset: u64 },
1008 write_data: struct { data: []const u8, out_offset: u64 },
1009 };
1010 var cmdbuf = std.ArrayList(WriteCmd).init(allocator);
1011 defer cmdbuf.deinit();
1012 try cmdbuf.ensureUnusedCapacity(3 + new_shnum);
1013 var eof_offset: u64 = 0; // track the end of the data written so far.
1014
1015 // build the updated headers
1016 // nb: updated_elf_header will be updated before the actual write
1017 var updated_elf_header = self.raw_elf_header;
1018 if (updated_elf_header.e_shstrndx != elf.SHN_UNDEF)
1019 updated_elf_header.e_shstrndx = sections_update[updated_elf_header.e_shstrndx].remap_idx;
1020 cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = std.mem.asBytes(&updated_elf_header), .out_offset = 0 } });
1021 eof_offset = @sizeOf(elf.Elf64_Ehdr);
1022
1023 // program header as-is.
1024 // nb: for only-debug files, removing it appears to work, but is invalid by ELF specifcation.
1025 {
1026 std.debug.assert(updated_elf_header.e_phoff == @sizeOf(elf.Elf64_Ehdr));
1027 const data = std.mem.sliceAsBytes(self.program_segments);
1028 std.debug.assert(data.len == @as(usize, updated_elf_header.e_phentsize) * updated_elf_header.e_phnum);
1029 cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = data, .out_offset = updated_elf_header.e_phoff } });
1030 eof_offset = updated_elf_header.e_phoff + data.len;
1031 }
955 // remap the indexs after omitting the filtered sections
956 remap_idx: u16,
1032957
1033 // update sections and queue payload writes
1034 const updated_section_header = blk: {
1035 const dest_sections = try allocator.alloc(elf.Elf64_Shdr, new_shnum);
958 // optionally overrides the payload from the source file
959 payload: ?[]align(section_memory_align) const u8,
960 };
961 const sections_update = try allocator.alloc(Update, self.sections.len);
962 const new_shnum = blk: {
963 var next_idx: u16 = 0;
964 for (self.sections, sections_update) |section, *update| {
965 update.action = action: {
966 if (section.usage == .none) break :action .strip;
967 break :action switch (filter) {
968 .all => switch (section.usage) {
969 .none => .strip,
970 else => .keep,
971 },
972 .program => switch (section.usage) {
973 .common, .exe => .keep,
974 else => .strip,
975 },
976 .program_and_symbols => switch (section.usage) {
977 .common, .exe, .symbols => .keep,
978 else => .strip,
979 },
980 .debug => switch (section.usage) {
981 .exe, .symbols => .empty,
982 .none => .strip,
983 else => .keep,
984 },
985 .debug_and_symbols => switch (section.usage) {
986 .exe => .empty,
987 .none => .strip,
988 else => .keep,
989 },
990 };
991 };
1036992
1037 {
1038 // the ELF format doesn't specify the order for all sections.
1039 // this code only supports when they are in increasing file order.
1040 var offset: u64 = eof_offset;
1041 for (self.sections[1..]) |section| {
1042 if (section.section.sh_offset < offset) {
1043 fatal("zig objcopy: unsuported ELF file", .{});
993 if (update.action == .strip) {
994 update.remap_idx = elf.SHN_UNDEF;
995 } else {
996 update.remap_idx = next_idx;
997 next_idx += 1;
1044998 }
1045 offset = section.section.sh_offset;
999
1000 update.payload = null;
10461001 }
1047 }
10481002
1049 dest_sections[0] = self.sections[0].section;
1003 if (debuglink != null)
1004 next_idx += 1;
1005 break :blk next_idx;
1006 };
10501007
1051 var dest_section_idx: u32 = 1;
1052 for (self.sections[1..], sections_update[1..]) |section, update| {
1053 if (update.action == .strip) continue;
1054 std.debug.assert(update.remap_idx == dest_section_idx);
1008 const debuglink_name: u32 = blk: {
1009 if (debuglink == null) break :blk elf.SHN_UNDEF;
1010 if (self.raw_elf_header.e_shstrndx == elf.SHN_UNDEF)
1011 fatal("zig objcopy: no strtab, cannot add the debuglink section", .{}); // TODO add the section if needed?
10551012
1056 const src = &section.section;
1057 const dest = &dest_sections[dest_section_idx];
1058 dest_section_idx += 1;
1013 const strtab = &self.sections[self.raw_elf_header.e_shstrndx];
1014 const update = &sections_update[self.raw_elf_header.e_shstrndx];
10591015
1060 dest.* = src.*;
1016 const name: []const u8 = ".gnu_debuglink";
1017 const new_offset = @intCast(u32, strtab.payload.?.len);
1018 const buf = try allocator.alignedAlloc(u8, section_memory_align, new_offset + name.len + 1);
1019 std.mem.copy(u8, buf[0..new_offset], strtab.payload.?);
1020 std.mem.copy(u8, buf[new_offset .. new_offset + name.len], name);
1021 buf[new_offset + name.len] = 0;
10611022
1062 if (src.sh_link != elf.SHN_UNDEF)
1063 dest.sh_link = sections_update[src.sh_link].remap_idx;
1064 if ((src.sh_flags & elf.SHF_INFO_LINK) != 0 and src.sh_info != elf.SHN_UNDEF)
1065 dest.sh_info = sections_update[src.sh_info].remap_idx;
1023 std.debug.assert(update.action == .keep);
1024 update.payload = buf;
10661025
1067 const payload = if (update.payload) |data| data else section.payload;
1068 if (payload) |data|
1069 dest.sh_size = data.len;
1026 break :blk new_offset;
1027 };
10701028
1071 const addralign = if (src.sh_addralign == 0 or dest.sh_type == elf.SHT_NOBITS) 1 else src.sh_addralign;
1072 dest.sh_offset = std.mem.alignForwardGeneric(u64, eof_offset, addralign);
1073 if (src.sh_offset != dest.sh_offset and section.segment != null and update.action != .empty and dest.sh_type != elf.SHT_NOTE) {
1074 if (src.sh_offset > dest.sh_offset) {
1075 dest.sh_offset = src.sh_offset; // add padding to avoid modifing the program segments
1076 } else {
1077 fatal("zig objcopy: cannot adjust program segments", .{});
1029 const WriteCmd = union(enum) {
1030 copy_range: struct { in_offset: u64, len: u64, out_offset: u64 },
1031 write_data: struct { data: []const u8, out_offset: u64 },
1032 };
1033 var cmdbuf = std.ArrayList(WriteCmd).init(allocator);
1034 defer cmdbuf.deinit();
1035 try cmdbuf.ensureUnusedCapacity(3 + new_shnum);
1036 var eof_offset: Elf_OffSize = 0; // track the end of the data written so far.
1037
1038 // build the updated headers
1039 // nb: updated_elf_header will be updated before the actual write
1040 var updated_elf_header = self.raw_elf_header;
1041 if (updated_elf_header.e_shstrndx != elf.SHN_UNDEF)
1042 updated_elf_header.e_shstrndx = sections_update[updated_elf_header.e_shstrndx].remap_idx;
1043 cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = std.mem.asBytes(&updated_elf_header), .out_offset = 0 } });
1044 eof_offset = @sizeOf(Elf_Ehdr);
1045
1046 // program header as-is.
1047 // nb: for only-debug files, removing it appears to work, but is invalid by ELF specifcation.
1048 {
1049 std.debug.assert(updated_elf_header.e_phoff == @sizeOf(Elf_Ehdr));
1050 const data = std.mem.sliceAsBytes(self.program_segments);
1051 std.debug.assert(data.len == @as(usize, updated_elf_header.e_phentsize) * updated_elf_header.e_phnum);
1052 cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = data, .out_offset = updated_elf_header.e_phoff } });
1053 eof_offset = updated_elf_header.e_phoff + @intCast(Elf_OffSize, data.len);
1054 }
1055
1056 // update sections and queue payload writes
1057 const updated_section_header = blk: {
1058 const dest_sections = try allocator.alloc(Elf_Shdr, new_shnum);
1059
1060 {
1061 // the ELF format doesn't specify the order for all sections.
1062 // this code only supports when they are in increasing file order.
1063 var offset: u64 = eof_offset;
1064 for (self.sections[1..]) |section| {
1065 if (section.section.sh_offset < offset) {
1066 fatal("zig objcopy: unsuported ELF file", .{});
1067 }
1068 offset = section.section.sh_offset;
10781069 }
10791070 }
1080 std.debug.assert(dest.sh_addr % addralign == dest.sh_offset % addralign);
10811071
1082 if (update.action == .empty)
1083 dest.sh_type = elf.SHT_NOBITS;
1072 dest_sections[0] = self.sections[0].section;
10841073
1085 if (dest.sh_type != elf.SHT_NOBITS) {
1086 if (payload) |src_data| {
1087 // update sections payload and write
1088 const data = try allocator.alignedAlloc(u8, section_memory_align, src_data.len);
1089 std.mem.copy(u8, data, src_data);
1074 var dest_section_idx: u32 = 1;
1075 for (self.sections[1..], sections_update[1..]) |section, update| {
1076 if (update.action == .strip) continue;
1077 std.debug.assert(update.remap_idx == dest_section_idx);
10901078
1091 switch (src.sh_type) {
1092 elf.DT_VERSYM => {
1093 const defs = @ptrCast([*]elf.Elf64_Verdef, data)[0 .. @intCast(usize, src.sh_size) / @sizeOf(elf.Elf64_Verdef)];
1094 for (defs) |*def| {
1095 if (def.vd_ndx != elf.SHN_UNDEF)
1096 def.vd_ndx = sections_update[src.sh_info].remap_idx;
1097 }
1098 },
1099 elf.SHT_SYMTAB, elf.SHT_DYNSYM => {
1100 const syms = @ptrCast([*]elf.Elf64_Sym, data)[0 .. @intCast(usize, src.sh_size) / @sizeOf(elf.Elf64_Sym)];
1101 for (syms) |*sym| {
1102 if (sym.st_shndx != elf.SHN_UNDEF and sym.st_shndx < elf.SHN_LORESERVE)
1103 sym.st_shndx = sections_update[sym.st_shndx].remap_idx;
1104 }
1105 },
1106 else => {},
1079 const src = &section.section;
1080 const dest = &dest_sections[dest_section_idx];
1081 dest_section_idx += 1;
1082
1083 dest.* = src.*;
1084
1085 if (src.sh_link != elf.SHN_UNDEF)
1086 dest.sh_link = sections_update[src.sh_link].remap_idx;
1087 if ((src.sh_flags & elf.SHF_INFO_LINK) != 0 and src.sh_info != elf.SHN_UNDEF)
1088 dest.sh_info = sections_update[src.sh_info].remap_idx;
1089
1090 const payload = if (update.payload) |data| data else section.payload;
1091 if (payload) |data|
1092 dest.sh_size = @intCast(Elf_OffSize, data.len);
1093
1094 const addralign = if (src.sh_addralign == 0 or dest.sh_type == elf.SHT_NOBITS) 1 else src.sh_addralign;
1095 dest.sh_offset = std.mem.alignForwardGeneric(Elf_OffSize, eof_offset, addralign);
1096 if (src.sh_offset != dest.sh_offset and section.segment != null and update.action != .empty and dest.sh_type != elf.SHT_NOTE) {
1097 if (src.sh_offset > dest.sh_offset) {
1098 dest.sh_offset = src.sh_offset; // add padding to avoid modifing the program segments
1099 } else {
1100 fatal("zig objcopy: cannot adjust program segments", .{});
11071101 }
1102 }
1103 std.debug.assert(dest.sh_addr % addralign == dest.sh_offset % addralign);
1104
1105 if (update.action == .empty)
1106 dest.sh_type = elf.SHT_NOBITS;
1107
1108 if (dest.sh_type != elf.SHT_NOBITS) {
1109 if (payload) |src_data| {
1110 // update sections payload and write
1111 const data = try allocator.alignedAlloc(u8, section_memory_align, src_data.len);
1112 std.mem.copy(u8, data, src_data);
1113
1114 switch (src.sh_type) {
1115 elf.DT_VERSYM => {
1116 const defs = @ptrCast([*]Elf_Verdef, data)[0 .. @intCast(usize, src.sh_size) / @sizeOf(Elf_Verdef)];
1117 for (defs) |*def| {
1118 if (def.vd_ndx != elf.SHN_UNDEF)
1119 def.vd_ndx = sections_update[src.sh_info].remap_idx;
1120 }
1121 },
1122 elf.SHT_SYMTAB, elf.SHT_DYNSYM => {
1123 const syms = @ptrCast([*]Elf_Sym, data)[0 .. @intCast(usize, src.sh_size) / @sizeOf(Elf_Sym)];
1124 for (syms) |*sym| {
1125 if (sym.st_shndx != elf.SHN_UNDEF and sym.st_shndx < elf.SHN_LORESERVE)
1126 sym.st_shndx = sections_update[sym.st_shndx].remap_idx;
1127 }
1128 },
1129 else => {},
1130 }
11081131
1109 std.debug.assert(data.len == dest.sh_size);
1110 cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = data, .out_offset = dest.sh_offset } });
1111 eof_offset = dest.sh_offset + dest.sh_size;
1132 std.debug.assert(data.len == dest.sh_size);
1133 cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = data, .out_offset = dest.sh_offset } });
1134 eof_offset = dest.sh_offset + dest.sh_size;
1135 } else {
1136 // direct contents copy
1137 cmdbuf.appendAssumeCapacity(.{ .copy_range = .{ .in_offset = src.sh_offset, .len = dest.sh_size, .out_offset = dest.sh_offset } });
1138 eof_offset = dest.sh_offset + dest.sh_size;
1139 }
11121140 } else {
1113 // direct contents copy
1114 cmdbuf.appendAssumeCapacity(.{ .copy_range = .{ .in_offset = src.sh_offset, .len = dest.sh_size, .out_offset = dest.sh_offset } });
1115 eof_offset = dest.sh_offset + dest.sh_size;
1141 // account for alignment padding even in empty sections to keep logical section order
1142 eof_offset = dest.sh_offset;
11161143 }
1117 } else {
1118 // account for alignment padding even in empty sections to keep logical section order
1119 eof_offset = dest.sh_offset;
11201144 }
1121 }
11221145
1123 // add a ".gnu_debuglink" section
1124 if (debuglink) |link| {
1125 const payload = payload: {
1126 const crc_offset = std.mem.alignForward(link.name.len + 1, 4);
1127 const buf = try allocator.alignedAlloc(u8, 4, crc_offset + 4);
1128 std.mem.copy(u8, buf[0..link.name.len], link.name);
1129 std.mem.set(u8, buf[link.name.len..crc_offset], 0);
1130 std.mem.copy(u8, buf[crc_offset..], std.mem.asBytes(&link.crc32));
1131 break :payload buf;
1132 };
1146 // add a ".gnu_debuglink" section
1147 if (debuglink) |link| {
1148 const payload = payload: {
1149 const crc_offset = std.mem.alignForward(link.name.len + 1, 4);
1150 const buf = try allocator.alignedAlloc(u8, 4, crc_offset + 4);
1151 std.mem.copy(u8, buf[0..link.name.len], link.name);
1152 std.mem.set(u8, buf[link.name.len..crc_offset], 0);
1153 std.mem.copy(u8, buf[crc_offset..], std.mem.asBytes(&link.crc32));
1154 break :payload buf;
1155 };
11331156
1134 dest_sections[dest_section_idx] = elf.Elf64_Shdr{
1135 .sh_name = debuglink_name,
1136 .sh_type = elf.SHT_PROGBITS,
1137 .sh_flags = 0,
1138 .sh_addr = 0,
1139 .sh_offset = eof_offset,
1140 .sh_size = payload.len,
1141 .sh_link = elf.SHN_UNDEF,
1142 .sh_info = elf.SHN_UNDEF,
1143 .sh_addralign = 4,
1144 .sh_entsize = 0,
1145 };
1146 dest_section_idx += 1;
1157 dest_sections[dest_section_idx] = Elf_Shdr{
1158 .sh_name = debuglink_name,
1159 .sh_type = elf.SHT_PROGBITS,
1160 .sh_flags = 0,
1161 .sh_addr = 0,
1162 .sh_offset = eof_offset,
1163 .sh_size = @intCast(Elf_OffSize, payload.len),
1164 .sh_link = elf.SHN_UNDEF,
1165 .sh_info = elf.SHN_UNDEF,
1166 .sh_addralign = 4,
1167 .sh_entsize = 0,
1168 };
1169 dest_section_idx += 1;
11471170
1148 cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = payload, .out_offset = eof_offset } });
1149 eof_offset += payload.len;
1150 }
1171 cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = payload, .out_offset = eof_offset } });
1172 eof_offset += @intCast(Elf_OffSize, payload.len);
1173 }
11511174
1152 std.debug.assert(dest_section_idx == new_shnum);
1153 break :blk dest_sections;
1154 };
1175 std.debug.assert(dest_section_idx == new_shnum);
1176 break :blk dest_sections;
1177 };
11551178
1156 // write the section header at the tail
1157 {
1158 const offset = std.mem.alignForwardGeneric(u64, eof_offset, @alignOf(elf.Elf64_Shdr));
1179 // write the section header at the tail
1180 {
1181 const offset = std.mem.alignForwardGeneric(Elf_OffSize, eof_offset, @alignOf(Elf_Shdr));
11591182
1160 const data = std.mem.sliceAsBytes(updated_section_header);
1161 std.debug.assert(data.len == @as(usize, updated_elf_header.e_shentsize) * new_shnum);
1162 updated_elf_header.e_shoff = offset;
1163 updated_elf_header.e_shnum = new_shnum;
1183 const data = std.mem.sliceAsBytes(updated_section_header);
1184 std.debug.assert(data.len == @as(usize, updated_elf_header.e_shentsize) * new_shnum);
1185 updated_elf_header.e_shoff = offset;
1186 updated_elf_header.e_shnum = new_shnum;
11641187
1165 cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = data, .out_offset = updated_elf_header.e_shoff } });
1166 }
1188 cmdbuf.appendAssumeCapacity(.{ .write_data = .{ .data = data, .out_offset = updated_elf_header.e_shoff } });
1189 }
11671190
1168 // consolidate holes between writes:
1169 // by coping original padding data from in_file (by fusing contiguous ranges)
1170 // by writing zeroes otherwise
1171 const zeroes = [1]u8{0} ** 4096;
1172 const consolidated_cmdbuf = blk: {
1173 var newbuf = std.ArrayList(WriteCmd).init(allocator);
1174 try newbuf.ensureUnusedCapacity(cmdbuf.items.len * 2);
1175 var offset: u64 = 0;
1176 var fused_cmd: ?WriteCmd = null;
1177 for (cmdbuf.items) |cmd| {
1178 switch (cmd) {
1179 .write_data => |data| {
1180 std.debug.assert(data.out_offset >= offset);
1181 if (fused_cmd) |prev| {
1182 newbuf.appendAssumeCapacity(prev);
1183 fused_cmd = null;
1184 }
1185 if (data.out_offset > offset) {
1186 newbuf.appendAssumeCapacity(.{ .write_data = .{ .data = zeroes[0 .. @intCast(usize, data.out_offset - offset)], .out_offset = offset } });
1187 }
1188 newbuf.appendAssumeCapacity(cmd);
1189 offset = data.out_offset + data.data.len;
1190 },
1191 .copy_range => |range| {
1192 std.debug.assert(range.out_offset >= offset);
1193 if (fused_cmd) |prev| {
1194 if (range.in_offset >= prev.copy_range.in_offset + prev.copy_range.len and (range.out_offset - prev.copy_range.out_offset == range.in_offset - prev.copy_range.in_offset)) {
1195 fused_cmd = .{ .copy_range = .{
1196 .in_offset = prev.copy_range.in_offset,
1197 .out_offset = prev.copy_range.out_offset,
1198 .len = (range.out_offset + range.len) - prev.copy_range.out_offset,
1199 } };
1200 } else {
1191 // consolidate holes between writes:
1192 // by coping original padding data from in_file (by fusing contiguous ranges)
1193 // by writing zeroes otherwise
1194 const zeroes = [1]u8{0} ** 4096;
1195 const consolidated_cmdbuf = blk: {
1196 var newbuf = std.ArrayList(WriteCmd).init(allocator);
1197 try newbuf.ensureUnusedCapacity(cmdbuf.items.len * 2);
1198 var offset: u64 = 0;
1199 var fused_cmd: ?WriteCmd = null;
1200 for (cmdbuf.items) |cmd| {
1201 switch (cmd) {
1202 .write_data => |data| {
1203 std.debug.assert(data.out_offset >= offset);
1204 if (fused_cmd) |prev| {
12011205 newbuf.appendAssumeCapacity(prev);
1202 if (range.out_offset > offset) {
1203 newbuf.appendAssumeCapacity(.{ .write_data = .{ .data = zeroes[0 .. @intCast(usize, range.out_offset - offset)], .out_offset = offset } });
1206 fused_cmd = null;
1207 }
1208 if (data.out_offset > offset) {
1209 newbuf.appendAssumeCapacity(.{ .write_data = .{ .data = zeroes[0..@intCast(usize, data.out_offset - offset)], .out_offset = offset } });
1210 }
1211 newbuf.appendAssumeCapacity(cmd);
1212 offset = data.out_offset + data.data.len;
1213 },
1214 .copy_range => |range| {
1215 std.debug.assert(range.out_offset >= offset);
1216 if (fused_cmd) |prev| {
1217 if (range.in_offset >= prev.copy_range.in_offset + prev.copy_range.len and (range.out_offset - prev.copy_range.out_offset == range.in_offset - prev.copy_range.in_offset)) {
1218 fused_cmd = .{ .copy_range = .{
1219 .in_offset = prev.copy_range.in_offset,
1220 .out_offset = prev.copy_range.out_offset,
1221 .len = (range.out_offset + range.len) - prev.copy_range.out_offset,
1222 } };
1223 } else {
1224 newbuf.appendAssumeCapacity(prev);
1225 if (range.out_offset > offset) {
1226 newbuf.appendAssumeCapacity(.{ .write_data = .{ .data = zeroes[0..@intCast(usize, range.out_offset - offset)], .out_offset = offset } });
1227 }
1228 fused_cmd = cmd;
12041229 }
1230 } else {
12051231 fused_cmd = cmd;
12061232 }
1207 } else {
1208 fused_cmd = cmd;
1209 }
1210 offset = range.out_offset + range.len;
1211 },
1233 offset = range.out_offset + range.len;
1234 },
1235 }
12121236 }
1213 }
1214 if (fused_cmd) |cmd| {
1215 newbuf.appendAssumeCapacity(cmd);
1216 }
1217 break :blk newbuf.items;
1218 };
1237 if (fused_cmd) |cmd| {
1238 newbuf.appendAssumeCapacity(cmd);
1239 }
1240 break :blk newbuf.items;
1241 };
12191242
1220 // write the output file
1221 for (consolidated_cmdbuf) |cmd| {
1222 switch (cmd) {
1223 .write_data => |data| {
1224 var iovec = [_]std.os.iovec_const{.{ .iov_base = data.data.ptr, .iov_len = data.data.len }};
1225 try output.pwritevAll(&iovec, data.out_offset);
1226 },
1227 .copy_range => |range| {
1228 const copied_bytes = try source.copyRangeAll(range.in_offset, output, range.out_offset, range.len);
1229 if (copied_bytes < range.len) return error.TRUNCATED_ELF;
1230 },
1243 // write the output file
1244 for (consolidated_cmdbuf) |cmd| {
1245 switch (cmd) {
1246 .write_data => |data| {
1247 var iovec = [_]std.os.iovec_const{.{ .iov_base = data.data.ptr, .iov_len = data.data.len }};
1248 try output.pwritevAll(&iovec, data.out_offset);
1249 },
1250 .copy_range => |range| {
1251 const copied_bytes = try source.copyRangeAll(range.in_offset, output, range.out_offset, range.len);
1252 if (copied_bytes < range.len) return error.TRUNCATED_ELF;
1253 },
1254 }
12311255 }
12321256 }
1233 }
12341257
1235 fn sectionWithinSegment(section: elf.Elf64_Shdr, segment: elf.Elf64_Phdr) bool {
1236 const file_size = if (section.sh_type == elf.SHT_NOBITS) 0 else section.sh_size;
1237 return segment.p_offset <= section.sh_offset and (segment.p_offset + segment.p_filesz) >= (section.sh_offset + file_size);
1238 }
1239};
1258 fn sectionWithinSegment(section: Elf_Shdr, segment: Elf_Phdr) bool {
1259 const file_size = if (section.sh_type == elf.SHT_NOBITS) 0 else section.sh_size;
1260 return segment.p_offset <= section.sh_offset and (segment.p_offset + segment.p_filesz) >= (section.sh_offset + file_size);
1261 }
1262 };
1263}
12401264
12411265fn computeFileCrc(file: File) !u32 {
12421266 var buf: [8000]u8 = undefined;