lib/tldr/src/formats/elf/ehframe/section.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const root = @import("../../../root.zig");
  3 const elf = @import("../root.zig");
  4 const record = @import("record.zig");
  5 
  6 const model = root.model;
  7 const format = elf.format;
  8 const layout = elf.layout;
  9 const parser = elf.parser;
 10 const output_section = elf.output_section;
 11 const section_state = elf.section_state;
 12 const ObjectFile = parser.ObjectFile;
 13 const SectionHeader = format.SectionHeader;
 14 const Rela = format.Rela;
 15 const OutputSection = layout.OutputSection;
 16 const SectionContribution = layout.SectionContribution;
 17 const EhFrameCieLayout = layout.EhFrameCieLayout;
 18 const GlobalSymbol = layout.GlobalSymbol;
 19 const SymbolRef = layout.SymbolRef;
 20 const output_section_count = output_section.output_section_count;
 21 const readU32 = format.readU32;
 22 const validateAlignment = format.validateAlignment;
 23 const alignForwardU64 = format.alignForwardU64;
 24 const contributionReserveSize = layout.contributionReserveSize;
 25 const sectionDiscarded = section_state.sectionDiscarded;
 26 const foldedSection = section_state.foldedSection;
 27 const sectionNameOrEmpty = parser.sectionNameOrEmpty;
 28 const sectionIsAllocated = format.sectionIsAllocated;
 29 
 30 pub fn is(object: ObjectFile, section_index: usize, section: SectionHeader) bool {
 31     if (!sectionIsAllocated(section)) return false;
 32     if (section.section_type == std.elf.SHT_X86_64_UNWIND) return true;
 33     return std.mem.eql(u8, sectionNameOrEmpty(object, section_index), ".eh_frame");
 34 }
 35 
 36 pub fn walk(
 37     objects: []const ObjectFile,
 38     object_index: usize,
 39     bytes: []const u8,
 40     source_relocations: []const Rela,
 41     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
 42     consumer: anytype,
 43 ) model.Error!u64 {
 44     var window_cursor = record.Cursor.init(source_relocations);
 45     var query_cursor = window_cursor;
 46 
 47     var input_offset: u64 = 0;
 48     var output_size: u64 = 0;
 49     while (input_offset < bytes.len) {
 50         const record_start: usize = @intCast(input_offset);
 51         if (bytes.len - record_start < 4) return error.InvalidObject;
 52         const record_length = readU32(bytes, record_start);
 53         if (record_length == 0xffffffff) return error.UnsupportedFormat;
 54         const record_size = @as(u64, record_length) + 4;
 55         if (record_size < 4) return error.InvalidObject;
 56         if (input_offset > std.math.maxInt(u64) - record_size) return error.InvalidObject;
 57         const record_end = input_offset + record_size;
 58         if (record_end > bytes.len) return error.InvalidObject;
 59 
 60         if (record_length == 0) {
 61             emitRelocations(&window_cursor, input_offset, record_size, output_size, consumer);
 62             consumer.piece(.{
 63                 .input_offset = input_offset,
 64                 .size = record_size,
 65                 .output_offset = output_size,
 66             });
 67             output_size += record_size;
 68             break;
 69         }
 70 
 71         if (record_length < 4) return error.InvalidObject;
 72         const record_id = readU32(bytes, record_start + 4);
 73         var keep_record = true;
 74         var patch_cie_pointer = false;
 75         var cie_pointer: u32 = 0;
 76         if (record_id == 0) {
 77             consumer.cie(input_offset, output_size);
 78         } else {
 79             const initial_location_offset = input_offset + 8;
 80             if (query_cursor.at(initial_location_offset)) |relocation| {
 81                 keep_record = !(try relocationTargetDiscarded(objects, object_index, relocation, globals));
 82             }
 83             if (keep_record) {
 84                 const cie_field_offset = input_offset + 4;
 85                 if (record_id > cie_field_offset) return error.InvalidObject;
 86                 const cie_input_offset = cie_field_offset - record_id;
 87                 cie_pointer = try consumer.resolve(cie_input_offset, output_size);
 88                 patch_cie_pointer = true;
 89             }
 90         }
 91 
 92         if (keep_record) {
 93             emitRelocations(&window_cursor, input_offset, record_size, output_size, consumer);
 94             consumer.piece(.{
 95                 .input_offset = input_offset,
 96                 .size = record_size,
 97                 .output_offset = output_size,
 98                 .patch_cie_pointer = patch_cie_pointer,
 99                 .cie_pointer = cie_pointer,
100             });
101             output_size += record_size;
102         }
103         input_offset = record_end;
104     }
105     return output_size;
106 }
107 
108 pub fn assign(
109     output_sections: *[output_section_count]OutputSection,
110     contributions: []SectionContribution,
111     section: SectionHeader,
112     section_index: usize,
113     output_size: u64,
114     options: model.LinkOptions,
115 ) model.Error!void {
116     const output_index = @backingInt(output_section.OutputSectionKind.eh_frame);
117     var output = &output_sections[output_index];
118     try validateAlignment(section.alignment);
119     output.alignment = @max(output.alignment, @max(section.alignment, 1));
120     const alignment = @max(section.alignment, 1);
121     const aligned_offset = if (section.section_type == std.elf.SHT_NOBITS)
122         alignForwardU64(output.memory_size, alignment)
123     else
124         alignForwardU64(output.file_size, alignment);
125     const reserved_size = contributionReserveSize(output_size, alignment, options);
126     contributions[section_index] = SectionContribution.init(
127         output_index,
128         aligned_offset,
129         output_size,
130         reserved_size,
131         alignment,
132     );
133 
134     if (section.section_type == std.elf.SHT_NOBITS) {
135         output.memory_size = aligned_offset + reserved_size;
136     } else {
137         output.file_size = aligned_offset + reserved_size;
138         output.memory_size = output.file_size;
139     }
140 }
141 
142 pub fn cieOutputOffset(cies: []const EhFrameCieLayout, input_offset: u64) ?u64 {
143     for (cies) |cie| {
144         if (cie.input_offset == input_offset) return cie.output_offset;
145     }
146     return null;
147 }
148 
149 fn emitRelocations(
150     cursor: *record.Cursor,
151     input_offset: u64,
152     size: u64,
153     output_offset: u64,
154     consumer: anytype,
155 ) void {
156     const input_end = input_offset + size;
157     if (cursor.window(input_offset, input_end)) |window_relocations| {
158         for (window_relocations) |relocation| {
159             var adjusted = relocation;
160             adjusted.offset = output_offset + relocation.offset - input_offset;
161             consumer.relocation(adjusted);
162         }
163         return;
164     }
165     for (cursor.relocations) |relocation| {
166         if (relocation.offset < input_offset or relocation.offset >= input_end) continue;
167         var adjusted = relocation;
168         adjusted.offset = output_offset + relocation.offset - input_offset;
169         consumer.relocation(adjusted);
170     }
171 }
172 
173 fn relocationTargetDiscarded(
174     objects: []const ObjectFile,
175     object_index: usize,
176     relocation: Rela,
177     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
178 ) model.Error!bool {
179     const object = objects[object_index];
180     const symbol_index = relocation.symbolIndex();
181     if (symbol_index >= object.symbols.len) return error.UndefinedSymbol;
182     const symbol = object.symbols[symbol_index];
183     if (symbol.isUndefined() or symbol.isCommon() or symbol.isAbsolute()) return false;
184     if (symbol.section_index >= object.sections.len) return error.UnsupportedRelocation;
185     if (foldedSection(object, symbol.section_index)) |canonical| {
186         if (canonical.object_index >= objects.len) return error.MissingSection;
187         return sectionDiscarded(objects[canonical.object_index], canonical.section_index);
188     }
189     if (!sectionDiscarded(object, symbol.section_index)) return false;
190     if (symbol.name.len == 0) return true;
191     const global = globals.get(symbol.name) orelse return true;
192     const current_ref = SymbolRef{ .object_index = object_index, .symbol_index = symbol_index };
193     return global.ref.eql(current_ref);
194 }