lib/tldr/src/formats/elf/metadata.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const root = @import("../../root.zig");
  3 const format = @import("format.zig");
  4 const build_id_note = @import("note.zig");
  5 
  6 const incremental = root.incremental;
  7 const model = root.model;
  8 const ehdr_size = format.ehdr_size;
  9 const phdr_size = format.phdr_size;
 10 const shdr_size = format.shdr_size;
 11 const SectionHeader = format.SectionHeader;
 12 const requireRange = format.requireRange;
 13 const sectionBytes = format.sectionBytes;
 14 const stringFromTable = format.stringFromTable;
 15 const readSectionHeader = format.readSectionHeader;
 16 const readU16 = format.readU16;
 17 const readU32 = format.readU32;
 18 const readU64 = format.readU64;
 19 
 20 pub const ProgramHeader = struct {
 21     flags: u32,
 22     file_offset: u64,
 23     virtual_address: u64,
 24     file_size: u64,
 25     memory_size: u64,
 26 };
 27 
 28 const Layout = struct {
 29     start: u64,
 30     section_header_offset: u64,
 31     section_count: u16,
 32     string_section_index: u16,
 33 };
 34 
 35 pub fn patchIncrementalMetadata(image: []u8, candidate_image: []const u8) model.Error!void {
 36     if (image.len != candidate_image.len) return error.InvalidRange;
 37     const image_metadata = try metadataLayout(image);
 38     const candidate_metadata = try metadataLayout(candidate_image);
 39     if (image_metadata.start != candidate_metadata.start) return error.InvalidElfHeader;
 40     if (image_metadata.section_header_offset != candidate_metadata.section_header_offset) return error.InvalidElfHeader;
 41     if (image_metadata.section_count != candidate_metadata.section_count) return error.InvalidElfHeader;
 42     if (image_metadata.string_section_index != candidate_metadata.string_section_index) return error.InvalidElfHeader;
 43     const start: usize = @intCast(image_metadata.start);
 44     @memcpy(image[start..], candidate_image[start..]);
 45     try patchGeneratedBuildIdNote(image, candidate_image);
 46 }
 47 
 48 pub fn finishIncrementalMetadata(image: []u8, options: model.LinkOptions) model.Error!void {
 49     try finishGeneratedBuildIdNote(image, options.build_id);
 50 }
 51 
 52 pub const MetadataRange = struct {
 53     offset: usize,
 54     len: usize,
 55 };
 56 
 57 pub fn buildIdNoteRange(image: []const u8) model.Error!?MetadataRange {
 58     const note = linkedSectionByName(image, build_id_note.section_name) catch |err| switch (err) {
 59         error.MissingSection => return null,
 60         else => return err,
 61     };
 62     return .{
 63         .offset = std.math.cast(usize, note.offset) orelse return error.InvalidRange,
 64         .len = std.math.cast(usize, note.size) orelse return error.InvalidRange,
 65     };
 66 }
 67 
 68 pub fn patchIncrementalMetadataRange(image: []const u8) model.Error!MetadataRange {
 69     const image_metadata = try metadataLayout(image);
 70     const start = std.math.cast(usize, image_metadata.start) orelse return error.InvalidRange;
 71     return .{ .offset = start, .len = image.len - start };
 72 }
 73 
 74 fn patchGeneratedBuildIdNote(image: []u8, candidate_image: []const u8) model.Error!void {
 75     const note = linkedSectionByName(image, build_id_note.section_name) catch |err| switch (err) {
 76         error.MissingSection => return,
 77         else => return err,
 78     };
 79     const candidate_note = try linkedSectionByName(candidate_image, build_id_note.section_name);
 80     try build_id_note.patchGenerated(image, candidate_image, note, candidate_note);
 81 }
 82 
 83 fn finishGeneratedBuildIdNote(image: []u8, mode: model.BuildIdMode) model.Error!void {
 84     if (mode == .none) return;
 85     const note = try linkedSectionByName(image, build_id_note.section_name);
 86     try build_id_note.finishBuildIdSection(image, note, mode);
 87 }
 88 
 89 pub fn manifestSection(manifest: incremental.Manifest, name: []const u8) ?incremental.SectionRecord {
 90     for (manifest.sections) |section| {
 91         if (std.mem.eql(u8, manifest.string(section.name_id), name)) return section;
 92     }
 93     return null;
 94 }
 95 
 96 pub fn manifestContribution(
 97     manifest: incremental.Manifest,
 98     input_name: []const u8,
 99     kind: incremental.ContributionKind,
100     name: []const u8,
101     ordinal: u32,
102 ) ?incremental.ContributionRecord {
103     for (manifest.contributions) |contribution| {
104         if (!std.mem.eql(u8, manifest.string(contribution.input_name_id), input_name)) continue;
105         if (contribution.kind != kind) continue;
106         if (contribution.ordinal != ordinal) continue;
107         if (std.mem.eql(u8, manifest.string(contribution.name_id), name)) return contribution;
108     }
109     return null;
110 }
111 
112 pub fn manifestDiscardedContribution(
113     manifest: incremental.Manifest,
114     input_name: []const u8,
115     name: []const u8,
116     ordinal: u32,
117 ) ?incremental.DiscardedContributionRecord {
118     for (manifest.discarded_contributions) |contribution| {
119         if (!std.mem.eql(u8, manifest.string(contribution.input_name_id), input_name)) continue;
120         if (contribution.ordinal != ordinal) continue;
121         if (std.mem.eql(u8, manifest.string(contribution.name_id), name)) return contribution;
122     }
123     return null;
124 }
125 
126 fn metadataLayout(image: []const u8) model.Error!Layout {
127     if (image.len < ehdr_size) return error.InvalidElfHeader;
128     if (!std.mem.eql(u8, image[0..4], std.elf.MAGIC)) return error.InvalidElfHeader;
129     const shoff = readU64(image, 40);
130     const shentsize = readU16(image, 58);
131     const shnum = readU16(image, 60);
132     const shstrndx = readU16(image, 62);
133     if (shentsize != shdr_size or shnum == 0 or shstrndx >= shnum) return error.InvalidElfHeader;
134     try requireRange(image, shoff, @as(u64, shnum) * shdr_size);
135 
136     const shstrtab_header = readSectionHeader(image, shoff + @as(u64, shstrndx) * shdr_size);
137     const shstrtab = try sectionBytes(image, shstrtab_header);
138     var start = shoff;
139     var found = false;
140     for (0..shnum) |index| {
141         const section = readSectionHeader(image, shoff + @as(u64, @intCast(index)) * shdr_size);
142         const section_name = try stringFromTable(shstrtab, section.name_offset);
143         if (!std.mem.eql(u8, section_name, ".symtab") and
144             !std.mem.eql(u8, section_name, ".strtab") and
145             !std.mem.eql(u8, section_name, ".shstrtab"))
146         {
147             continue;
148         }
149         start = @min(start, section.offset);
150         found = true;
151     }
152     if (!found) return error.MissingSection;
153     return .{
154         .start = start,
155         .section_header_offset = shoff,
156         .section_count = shnum,
157         .string_section_index = shstrndx,
158     };
159 }
160 
161 pub fn linkedSectionByName(image: []const u8, name: []const u8) model.Error!SectionHeader {
162     const shoff = readU64(image, 40);
163     const shnum = readU16(image, 60);
164     const shstrndx = readU16(image, 62);
165     if (shstrndx >= shnum) return error.InvalidElfHeader;
166     try requireRange(image, shoff, @as(u64, shnum) * shdr_size);
167 
168     const shstrtab_header = readSectionHeader(image, shoff + @as(u64, shstrndx) * shdr_size);
169     const shstrtab = try sectionBytes(image, shstrtab_header);
170     for (0..shnum) |index| {
171         const section = readSectionHeader(image, shoff + @as(u64, @intCast(index)) * shdr_size);
172         const section_name = try stringFromTable(shstrtab, section.name_offset);
173         if (std.mem.eql(u8, section_name, name)) return section;
174     }
175     return error.MissingSection;
176 }
177 
178 pub fn loadProgramHeaderByAddress(image: []const u8, virtual_address: u64) model.Error!ProgramHeader {
179     return loadProgramHeaderByAddressAndType(image, virtual_address, std.elf.PT_LOAD);
180 }
181 
182 pub fn programHeaderByType(image: []const u8, program_type: u32) model.Error!ProgramHeader {
183     return loadProgramHeaderByAddressAndType(image, null, program_type);
184 }
185 
186 fn loadProgramHeaderByAddressAndType(image: []const u8, virtual_address: ?u64, program_type: u32) model.Error!ProgramHeader {
187     const phoff = readU64(image, 32);
188     const phentsize = readU16(image, 54);
189     const phnum = readU16(image, 56);
190     if (phentsize != phdr_size) return error.InvalidElfHeader;
191     try requireRange(image, phoff, @as(u64, phnum) * phdr_size);
192 
193     for (0..phnum) |index| {
194         const offset = phoff + @as(u64, @intCast(index)) * phdr_size;
195         if (readU32(image, @intCast(offset)) != program_type) continue;
196         const header_virtual_address = readU64(image, @intCast(offset + 16));
197         const memory_size = readU64(image, @intCast(offset + 40));
198         if (virtual_address) |address| {
199             if (address < header_virtual_address) continue;
200             if (address - header_virtual_address >= memory_size) continue;
201         }
202         return .{
203             .flags = readU32(image, @intCast(offset + 4)),
204             .file_offset = readU64(image, @intCast(offset + 8)),
205             .virtual_address = header_virtual_address,
206             .file_size = readU64(image, @intCast(offset + 32)),
207             .memory_size = memory_size,
208         };
209     }
210     return error.MissingSection;
211 }