lib/tldr/src/formats/elf/binary.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const root = @import("../../root.zig");
3 const record = @import("record.zig");
4
5 const model = root.model;
6 const native_endian = record.native_endian;
7 const shdr_size = record.shdr_size;
8 const SectionHeader = record.SectionHeader;
9 const Symbol = record.Symbol;
10 const SymbolRecord = record.SymbolRecord;
11 const byte = @import("byte.zig");
12
13 const copyInto = byte.copyInto;
14 const readU16 = byte.readU16;
15 const readU32 = byte.readU32;
16 const readU64 = byte.readU64;
17
18 pub fn sectionBytes(bytes: []const u8, section: SectionHeader) model.Error![]const u8 {
19 if (section.section_type == std.elf.SHT_NOBITS) return &.{};
20 try requireRange(bytes, section.offset, section.size);
21 const start: usize = @intCast(section.offset);
22 const size: usize = @intCast(section.size);
23 return bytes[start .. start + size];
24 }
25
26 pub fn isElf(bytes: []const u8) bool {
27 return bytes.len >= 4 and std.mem.eql(u8, bytes[0..4], std.elf.MAGIC);
28 }
29
30 pub fn requireRange(bytes: []const u8, offset: u64, size: u64) model.Error!void {
31 if (offset > bytes.len) return error.InvalidRange;
32 if (size > bytes.len - offset) return error.InvalidRange;
33 }
34
35 pub fn stringFromTable(table: []const u8, offset: u32) model.Error![]const u8 {
36 if (offset >= table.len) return error.InvalidStringTable;
37 const start: usize = @intCast(offset);
38 const end = std.mem.indexOfScalarPos(u8, table, start, 0) orelse return error.InvalidStringTable;
39 return table[start..end];
40 }
41
42 pub fn decodeSectionHeaders(bytes: []const u8, shoff: u64, sections: []SectionHeader) void {
43 if (native_endian == .little) {
44 const start: usize = @intCast(shoff);
45 const byte_count = sections.len * shdr_size;
46 @memcpy(std.mem.sliceAsBytes(sections), bytes[start..][0..byte_count]);
47 return;
48 }
49
50 for (sections, 0..) |*section, index| {
51 const offset: u64 = shoff + @as(u64, @intCast(index)) * shdr_size;
52 section.* = readSectionHeader(bytes, offset);
53 }
54 }
55
56 pub fn readSectionHeader(bytes: []const u8, offset: u64) SectionHeader {
57 const start: usize = @intCast(offset);
58 return .{
59 .name_offset = readU32(bytes[start..], 0),
60 .section_type = readU32(bytes[start..], 4),
61 .flags = readU64(bytes[start..], 8),
62 .address = readU64(bytes[start..], 16),
63 .offset = readU64(bytes[start..], 24),
64 .size = readU64(bytes[start..], 32),
65 .link = readU32(bytes[start..], 40),
66 .info = readU32(bytes[start..], 44),
67 .alignment = readU64(bytes[start..], 48),
68 .entry_size = readU64(bytes[start..], 56),
69 };
70 }
71
72 /// Decodes one 24-byte ELF64 symbol table entry, stored little-endian, into its
73 /// on-disk record so the entry's byte layout is read in one place. The function
74 /// is the only decoder of that entry: `readSymbol` and `View.symbol` both call
75 /// it.
76 pub fn readSymbolRecord(bytes: []const u8) SymbolRecord {
77 return .{
78 .name_offset = readU32(bytes, 0),
79 .info = bytes[4],
80 .other = bytes[5],
81 .section_index = readU16(bytes, 6),
82 .value = readU64(bytes, 8),
83 .size = readU64(bytes, 16),
84 };
85 }
86
87 /// Decodes one symbol table entry into the parsed symbol shape, by way of
88 /// `readSymbolRecord`. The object parser calls this function for each entry of
89 /// a symbol table before it resolves the names. The result's `name` is empty,
90 /// because the name lives in the symbol string table, which the caller holds
91 /// and looks up afterwards.
92 pub fn readSymbol(bytes: []const u8) Symbol {
93 const wire = readSymbolRecord(bytes);
94 return .{
95 .name_offset = wire.name_offset,
96 .info = wire.info,
97 .other = wire.other,
98 .section_index = wire.section_index,
99 .value = wire.value,
100 .size = wire.size,
101 };
102 }
103
104 pub fn validateAlignment(alignment: u64) model.Error!void {
105 if (alignment == 0) return;
106 if ((alignment & (alignment - 1)) != 0) return error.InvalidAlignment;
107 }
108
109 pub fn alignForward(value: usize, alignment: usize) usize {
110 if (alignment == 0) return value;
111 const mask = alignment - 1;
112 return (value + mask) & ~mask;
113 }
114
115 pub fn alignForwardU64(value: u64, alignment: u64) u64 {
116 if (alignment == 0) return value;
117 const mask = alignment - 1;
118 return (value + mask) & ~mask;
119 }