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 }