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 }