lib/tldr/src/formats/elf/ehframe/header/writer.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const root = @import("../../../../root.zig");
  3 const elf = @import("../../root.zig");
  4 const ehframe = @import("../root.zig");
  5 const reservation = @import("reservation.zig");
  6 
  7 const Allocator = std.mem.Allocator;
  8 const model = root.model;
  9 const addressing = elf.addressing;
 10 const format = elf.format;
 11 const layout = elf.layout;
 12 const output_section = elf.output_section;
 13 const parser = elf.parser;
 14 const ObjectFile = parser.ObjectFile;
 15 const ObjectLayout = layout.ObjectLayout;
 16 const OutputSection = layout.OutputSection;
 17 const GlobalSymbol = layout.GlobalSymbol;
 18 const SymbolAddressCache = addressing.Cache;
 19 const Rela = format.Rela;
 20 const OutputSectionKind = output_section.OutputSectionKind;
 21 const eh_frame_hdr_fixed_size = format.eh_frame_hdr_fixed_size;
 22 const eh_frame_hdr_entry_size = format.eh_frame_hdr_entry_size;
 23 const dwarf_eh_pe_udata4 = format.dwarf_eh_pe_udata4;
 24 const dwarf_eh_pe_sdata4 = format.dwarf_eh_pe_sdata4;
 25 const dwarf_eh_pe_pcrel = format.dwarf_eh_pe_pcrel;
 26 const dwarf_eh_pe_datarel = format.dwarf_eh_pe_datarel;
 27 const contributionAt = layout.contributionAt;
 28 const ehFrameSectionLayout = layout.ehFrameSection;
 29 const sectionBytes = format.sectionBytes;
 30 const readU32 = format.readU32;
 31 const writeU32 = format.writeU32;
 32 const requireRange = format.requireRange;
 33 const relocationTargetAddress = addressing.relocationTarget;
 34 const record = ehframe.record;
 35 const section = ehframe.section;
 36 
 37 const Entry = struct {
 38     start_ip: u64,
 39     fde_address: u64,
 40 };
 41 
 42 pub fn write(
 43     scratch: Allocator,
 44     image: []u8,
 45     objects: []const ObjectFile,
 46     layouts: []const ObjectLayout,
 47     output_sections: []const OutputSection,
 48     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
 49     symbol_addresses: *SymbolAddressCache,
 50     options: model.LinkOptions,
 51 ) model.Error!void {
 52     if (!options.eh_frame_header) return;
 53     const header = output_sections[@backingInt(OutputSectionKind.eh_frame_hdr)];
 54     if (header.logicalSize() == 0) return;
 55     const eh_frame_start = startAddress(objects, layouts, output_sections) orelse return error.MissingSection;
 56 
 57     var entries = std.ArrayListUnmanaged(Entry).empty;
 58     defer entries.deinit(scratch);
 59     try collectEntries(scratch, &entries, objects, layouts, output_sections, globals, symbol_addresses);
 60     if (try reservation.size(entries.items.len) != header.logicalSize()) return error.InvalidRange;
 61 
 62     std.mem.sort(Entry, entries.items, {}, compareEntries);
 63     try requireRange(image, header.file_offset, header.logicalSize());
 64     const start: usize = @intCast(header.file_offset);
 65     image[start + 0] = 1;
 66     image[start + 1] = dwarf_eh_pe_pcrel | dwarf_eh_pe_sdata4;
 67     image[start + 2] = dwarf_eh_pe_udata4;
 68     image[start + 3] = dwarf_eh_pe_datarel | dwarf_eh_pe_sdata4;
 69     writeU32(image, start + 4, @bitCast(try elf.relocation.checkedI32(@as(i128, @intCast(eh_frame_start)) - @as(i128, @intCast(header.address + 4)))));
 70     writeU32(image, start + 8, try elf.relocation.checkedU32(@intCast(entries.items.len)));
 71 
 72     var cursor = start + eh_frame_hdr_fixed_size;
 73     for (entries.items) |entry| {
 74         writeU32(image, cursor + 0, @bitCast(try elf.relocation.checkedI32(@as(i128, @intCast(entry.start_ip)) - @as(i128, @intCast(header.address)))));
 75         writeU32(image, cursor + 4, @bitCast(try elf.relocation.checkedI32(@as(i128, @intCast(entry.fde_address)) - @as(i128, @intCast(header.address)))));
 76         cursor += eh_frame_hdr_entry_size;
 77     }
 78 }
 79 
 80 fn collectEntries(
 81     allocator: Allocator,
 82     entries: *std.ArrayListUnmanaged(Entry),
 83     objects: []const ObjectFile,
 84     layouts: []const ObjectLayout,
 85     output_sections: []const OutputSection,
 86     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
 87     symbol_addresses: *SymbolAddressCache,
 88 ) model.Error!void {
 89     for (objects, 0..) |object, object_index| {
 90         const object_layout = layouts[object_index];
 91         for (object.sections, 0..) |input_section, section_index| {
 92             const contribution = contributionAt(object_layout.sections, section_index) orelse continue;
 93             if (!section.is(object, section_index, input_section)) continue;
 94             const output = output_sections[contribution.outputIndex()];
 95             const section_address = output.address + contribution.offset;
 96             const bytes = try sectionBytes(object.bytes, input_section);
 97             if (ehFrameSectionLayout(object_layout, section_index)) |eh_frame_section| {
 98                 var cursor = record.Cursor.init(eh_frame_section.relocations);
 99                 for (eh_frame_section.pieces) |piece| {
100                     try appendEntry(
101                         allocator,
102                         entries,
103                         objects,
104                         layouts,
105                         output_sections,
106                         globals,
107                         symbol_addresses,
108                         object_index,
109                         bytes,
110                         piece.input_offset,
111                         piece.size,
112                         piece.output_offset,
113                         &cursor,
114                         section_address,
115                     );
116                 }
117                 continue;
118             }
119             try collectRawEntries(
120                 allocator,
121                 entries,
122                 objects,
123                 layouts,
124                 output_sections,
125                 globals,
126                 symbol_addresses,
127                 object_index,
128                 bytes,
129                 object.relocationsForSection(section_index),
130                 section_address,
131             );
132         }
133     }
134 }
135 
136 fn collectRawEntries(
137     allocator: Allocator,
138     entries: *std.ArrayListUnmanaged(Entry),
139     objects: []const ObjectFile,
140     layouts: []const ObjectLayout,
141     output_sections: []const OutputSection,
142     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
143     symbol_addresses: *SymbolAddressCache,
144     object_index: usize,
145     bytes: []const u8,
146     relocations: []const Rela,
147     section_address: u64,
148 ) model.Error!void {
149     var cursor = record.Cursor.init(relocations);
150     var input_offset: u64 = 0;
151     while (input_offset < bytes.len) {
152         const record_start: usize = @intCast(input_offset);
153         if (bytes.len - record_start < 4) return error.InvalidObject;
154         const record_length = readU32(bytes, record_start);
155         if (record_length == 0xffffffff) return error.UnsupportedFormat;
156         const record_size = @as(u64, record_length) + 4;
157         if (record_size < 4) return error.InvalidObject;
158         if (input_offset > std.math.maxInt(u64) - record_size) return error.InvalidObject;
159         const record_end = input_offset + record_size;
160         if (record_end > bytes.len) return error.InvalidObject;
161         try appendEntry(
162             allocator,
163             entries,
164             objects,
165             layouts,
166             output_sections,
167             globals,
168             symbol_addresses,
169             object_index,
170             bytes,
171             input_offset,
172             record_size,
173             input_offset,
174             &cursor,
175             section_address,
176         );
177         if (record_length == 0) break;
178         input_offset = record_end;
179     }
180 }
181 
182 fn appendEntry(
183     allocator: Allocator,
184     entries: *std.ArrayListUnmanaged(Entry),
185     objects: []const ObjectFile,
186     layouts: []const ObjectLayout,
187     output_sections: []const OutputSection,
188     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
189     symbol_addresses: *SymbolAddressCache,
190     object_index: usize,
191     bytes: []const u8,
192     input_offset: u64,
193     entry_size: u64,
194     output_offset: u64,
195     cursor: *record.Cursor,
196     section_address: u64,
197 ) model.Error!void {
198     if (!try record.hasHeaderEntry(bytes, input_offset, entry_size, output_offset, cursor)) return;
199     const relocation = cursor.at(output_offset + 8) orelse return;
200     const symbol_index: usize = @intCast(relocation.symbolIndex());
201     const target = try relocationTargetAddress(.serial, objects, layouts, output_sections, globals, symbol_addresses, object_index, symbol_index, relocation, false);
202     try entries.append(allocator, .{
203         .start_ip = try elf.relocation.checkedU64(target.address + target.addend),
204         .fde_address = section_address + output_offset,
205     });
206 }
207 
208 fn startAddress(
209     objects: []const ObjectFile,
210     layouts: []const ObjectLayout,
211     output_sections: []const OutputSection,
212 ) ?u64 {
213     var start: ?u64 = null;
214     for (objects, 0..) |object, object_index| {
215         const object_layout = layouts[object_index];
216         for (object.sections, 0..) |input_section, section_index| {
217             const contribution = contributionAt(object_layout.sections, section_index) orelse continue;
218             if (!section.is(object, section_index, input_section)) continue;
219             const output = output_sections[contribution.outputIndex()];
220             const address = output.address + contribution.offset;
221             if (start == null or address < start.?) start = address;
222         }
223     }
224     return start;
225 }
226 
227 fn compareEntries(_: void, left: Entry, right: Entry) bool {
228     if (left.start_ip != right.start_ip) return left.start_ip < right.start_ip;
229     return left.fde_address < right.fde_address;
230 }