authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-04-16 21:53:24+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-04-20 23:36:42+02:00
log63a40bff47ce49a0cd27ef2adf99fa5e316fa8f9
treea0ad82e502c3355d0fb3d533a4cd34c7296c84e7
parentb5a781d19d3de1a50cbefe0df6f42a6ac8a05299

link/elf: actually commit merge sections


1 files changed, 292 insertions(+), 0 deletions(-)

src/link/Elf/merge_section.zig created+292
......@@ -0,0 +1,292 @@
1pub const MergeSection = struct {
2 name_offset: u32 = 0,
3 type: u32 = 0,
4 flags: u64 = 0,
5 output_section_index: u32 = 0,
6 bytes: std.ArrayListUnmanaged(u8) = .{},
7 table: std.HashMapUnmanaged(
8 String,
9 MergeSubsection.Index,
10 IndexContext,
11 std.hash_map.default_max_load_percentage,
12 ) = .{},
13 subsections: std.ArrayListUnmanaged(MergeSubsection.Index) = .{},
14
15 pub fn deinit(msec: *MergeSection, allocator: Allocator) void {
16 msec.bytes.deinit(allocator);
17 msec.table.deinit(allocator);
18 msec.subsections.deinit(allocator);
19 }
20
21 pub fn name(msec: MergeSection, elf_file: *Elf) [:0]const u8 {
22 return elf_file.strings.getAssumeExists(msec.name_offset);
23 }
24
25 pub fn address(msec: MergeSection, elf_file: *Elf) i64 {
26 const shdr = elf_file.shdrs.items[msec.output_section_index];
27 return @intCast(shdr.sh_addr);
28 }
29
30 const InsertResult = struct {
31 found_existing: bool,
32 key: String,
33 sub: *MergeSubsection.Index,
34 };
35
36 pub fn insert(msec: *MergeSection, allocator: Allocator, string: []const u8) !InsertResult {
37 const gop = try msec.table.getOrPutContextAdapted(
38 allocator,
39 string,
40 IndexAdapter{ .bytes = msec.bytes.items },
41 IndexContext{ .bytes = msec.bytes.items },
42 );
43 if (!gop.found_existing) {
44 const index: u32 = @intCast(msec.bytes.items.len);
45 try msec.bytes.appendSlice(allocator, string);
46 gop.key_ptr.* = .{ .pos = index, .len = @intCast(string.len) };
47 }
48 return .{ .found_existing = gop.found_existing, .key = gop.key_ptr.*, .sub = gop.value_ptr };
49 }
50
51 pub fn insertZ(msec: *MergeSection, allocator: Allocator, string: []const u8) !InsertResult {
52 const with_null = try allocator.alloc(u8, string.len + 1);
53 defer allocator.free(with_null);
54 @memcpy(with_null[0..string.len], string);
55 with_null[string.len] = 0;
56 return msec.insert(allocator, with_null);
57 }
58
59 /// Sorts all owned subsections.
60 /// Clears string table.
61 pub fn sort(msec: *MergeSection, elf_file: *Elf) !void {
62 const gpa = elf_file.base.comp.gpa;
63 try msec.subsections.ensureTotalCapacityPrecise(gpa, msec.table.count());
64
65 var it = msec.table.iterator();
66 while (it.next()) |entry| {
67 const msub = elf_file.mergeSubsection(entry.value_ptr.*);
68 if (!msub.alive) continue;
69 msec.subsections.appendAssumeCapacity(entry.value_ptr.*);
70 }
71 msec.table.clearAndFree(gpa);
72
73 const sortFn = struct {
74 pub fn sortFn(ctx: *Elf, lhs: MergeSubsection.Index, rhs: MergeSubsection.Index) bool {
75 const lhs_msub = ctx.mergeSubsection(lhs);
76 const rhs_msub = ctx.mergeSubsection(rhs);
77 if (lhs_msub.alignment.compareStrict(.eq, rhs_msub.alignment)) {
78 if (lhs_msub.size == rhs_msub.size) {
79 return mem.order(u8, lhs_msub.getString(ctx), rhs_msub.getString(ctx)) == .lt;
80 }
81 return lhs_msub.size < rhs_msub.size;
82 }
83 return lhs_msub.alignment.compareStrict(.lt, rhs_msub.alignment);
84 }
85 }.sortFn;
86
87 std.mem.sort(MergeSubsection.Index, msec.subsections.items, elf_file, sortFn);
88 }
89
90 pub const IndexContext = struct {
91 bytes: []const u8,
92
93 pub fn eql(_: @This(), a: String, b: String) bool {
94 return a.pos == b.pos;
95 }
96
97 pub fn hash(ctx: @This(), key: String) u64 {
98 const str = ctx.bytes[key.pos..][0..key.len];
99 return std.hash_map.hashString(str);
100 }
101 };
102
103 pub const IndexAdapter = struct {
104 bytes: []const u8,
105
106 pub fn eql(ctx: @This(), a: []const u8, b: String) bool {
107 const str = ctx.bytes[b.pos..][0..b.len];
108 return mem.eql(u8, a, str);
109 }
110
111 pub fn hash(_: @This(), adapted_key: []const u8) u64 {
112 return std.hash_map.hashString(adapted_key);
113 }
114 };
115
116 pub fn format(
117 msec: MergeSection,
118 comptime unused_fmt_string: []const u8,
119 options: std.fmt.FormatOptions,
120 writer: anytype,
121 ) !void {
122 _ = msec;
123 _ = unused_fmt_string;
124 _ = options;
125 _ = writer;
126 @compileError("do not format MergeSection directly");
127 }
128
129 pub fn fmt(msec: MergeSection, elf_file: *Elf) std.fmt.Formatter(format2) {
130 return .{ .data = .{
131 .msec = msec,
132 .elf_file = elf_file,
133 } };
134 }
135
136 const FormatContext = struct {
137 msec: MergeSection,
138 elf_file: *Elf,
139 };
140
141 pub fn format2(
142 ctx: FormatContext,
143 comptime unused_fmt_string: []const u8,
144 options: std.fmt.FormatOptions,
145 writer: anytype,
146 ) !void {
147 _ = options;
148 _ = unused_fmt_string;
149 const msec = ctx.msec;
150 const elf_file = ctx.elf_file;
151 try writer.print("{s} : @{x} : type({x}) : flags({x})\n", .{
152 msec.name(elf_file),
153 msec.address(elf_file),
154 msec.type,
155 msec.flags,
156 });
157 for (msec.subsections.items) |index| {
158 try writer.print(" {}\n", .{elf_file.mergeSubsection(index).fmt(elf_file)});
159 }
160 }
161
162 pub const Index = u32;
163};
164
165pub const MergeSubsection = struct {
166 value: i64 = 0,
167 merge_section_index: MergeSection.Index = 0,
168 string_index: u32 = 0,
169 size: u32 = 0,
170 alignment: Atom.Alignment = .@"1",
171 alive: bool = false,
172
173 pub fn address(msub: MergeSubsection, elf_file: *Elf) i64 {
174 return msub.mergeSection(elf_file).address(elf_file) + msub.value;
175 }
176
177 pub fn mergeSection(msub: MergeSubsection, elf_file: *Elf) *MergeSection {
178 return elf_file.mergeSection(msub.merge_section_index);
179 }
180
181 pub fn getString(msub: MergeSubsection, elf_file: *Elf) []const u8 {
182 const msec = msub.mergeSection(elf_file);
183 return msec.bytes.items[msub.string_index..][0..msub.size];
184 }
185
186 pub fn format(
187 msub: MergeSubsection,
188 comptime unused_fmt_string: []const u8,
189 options: std.fmt.FormatOptions,
190 writer: anytype,
191 ) !void {
192 _ = msub;
193 _ = unused_fmt_string;
194 _ = options;
195 _ = writer;
196 @compileError("do not format MergeSubsection directly");
197 }
198
199 pub fn fmt(msub: MergeSubsection, elf_file: *Elf) std.fmt.Formatter(format2) {
200 return .{ .data = .{
201 .msub = msub,
202 .elf_file = elf_file,
203 } };
204 }
205
206 const FormatContext = struct {
207 msub: MergeSubsection,
208 elf_file: *Elf,
209 };
210
211 pub fn format2(
212 ctx: FormatContext,
213 comptime unused_fmt_string: []const u8,
214 options: std.fmt.FormatOptions,
215 writer: anytype,
216 ) !void {
217 _ = options;
218 _ = unused_fmt_string;
219 const msub = ctx.msub;
220 const elf_file = ctx.elf_file;
221 try writer.print("@{x} : align({x}) : size({x})", .{
222 msub.address(elf_file),
223 msub.alignment,
224 msub.size,
225 });
226 if (!msub.alive) try writer.writeAll(" : [*]");
227 }
228
229 pub const Index = u32;
230};
231
232pub const InputMergeSection = struct {
233 merge_section_index: MergeSection.Index = 0,
234 atom_index: Atom.Index = 0,
235 offsets: std.ArrayListUnmanaged(u32) = .{},
236 subsections: std.ArrayListUnmanaged(MergeSubsection.Index) = .{},
237 bytes: std.ArrayListUnmanaged(u8) = .{},
238 strings: std.ArrayListUnmanaged(String) = .{},
239
240 pub fn deinit(imsec: *InputMergeSection, allocator: Allocator) void {
241 imsec.offsets.deinit(allocator);
242 imsec.subsections.deinit(allocator);
243 imsec.bytes.deinit(allocator);
244 imsec.strings.deinit(allocator);
245 }
246
247 pub fn clearAndFree(imsec: *InputMergeSection, allocator: Allocator) void {
248 imsec.bytes.clearAndFree(allocator);
249 // TODO: imsec.strings.clearAndFree(allocator);
250 }
251
252 pub fn findSubsection(imsec: InputMergeSection, offset: u32) ?struct { MergeSubsection.Index, u32 } {
253 // TODO: binary search
254 for (imsec.offsets.items, 0..) |off, index| {
255 if (offset < off) return .{
256 imsec.subsections.items[index - 1],
257 offset - imsec.offsets.items[index - 1],
258 };
259 }
260 const last = imsec.offsets.items.len - 1;
261 const last_off = imsec.offsets.items[last];
262 const last_len = imsec.strings.items[last].len;
263 if (offset < last_off + last_len) return .{ imsec.subsections.items[last], offset - last_off };
264 return null;
265 }
266
267 pub fn insert(imsec: *InputMergeSection, allocator: Allocator, string: []const u8) !void {
268 const index: u32 = @intCast(imsec.bytes.items.len);
269 try imsec.bytes.appendSlice(allocator, string);
270 try imsec.strings.append(allocator, .{ .pos = index, .len = @intCast(string.len) });
271 }
272
273 pub fn insertZ(imsec: *InputMergeSection, allocator: Allocator, string: []const u8) !void {
274 const index: u32 = @intCast(imsec.bytes.items.len);
275 try imsec.bytes.ensureUnusedCapacity(allocator, string.len + 1);
276 imsec.bytes.appendSliceAssumeCapacity(string);
277 imsec.bytes.appendAssumeCapacity(0);
278 try imsec.strings.append(allocator, .{ .pos = index, .len = @intCast(string.len + 1) });
279 }
280
281 pub const Index = u32;
282};
283
284const String = struct { pos: u32, len: u32 };
285
286const assert = std.debug.assert;
287const mem = std.mem;
288const std = @import("std");
289
290const Allocator = mem.Allocator;
291const Atom = @import("Atom.zig");
292const Elf = @import("../Elf.zig");