tiny.tldr.formats.elf.object.writer
Defined in formats.elf.object.
API (2)
Actions
Public operations.
build: Plans the layout (the planned file offsets for every record), allocates the output once at the planned size, writes it, and frees the layout, for tests and tools that want the object's bytes in one call.write: Writes one described object intoout, which is exactlylayout.sizebytes, using the layout (the planned file offsets for every record).
Source
Source: lib/tldr/src/formats/elf/object/root.zig:3
zig
pub const writer = @import("write.zig");Source: lib/tldr/src/formats/elf/object/write.zig
zig
const std = @import("std");const elf = @import("../root.zig");const model = @import("model.zig");const layout_plan = @import("layout.zig");const Allocator = std.mem.Allocator;const format = elf.format;const shdr_size = format.shdr_size;const sym_size = format.sym_size;const rela_size = format.rela_size;const copyInto = format.copyInto;const Description = model.Description;const Error = model.Error;const Layout = layout_plan.Layout;/// Writes one described object into `out`, which is exactly `layout.size`/// bytes, using the layout (the planned file offsets for every record). A/// caller that owns its output buffer calls this function after `plan` to write/// the description (a value listing its header fields, sections, symbols and/// relocations), and `build` calls it for everyone else. The call allocates/// nothing and does no arithmetic that can fail, because `plan` already proved/// every offset. The function zeroes `out` first, so the padding between/// records is zero. Each described record is written in one bounded pass.pub fn write(description: Description, layout: Layout, out: []u8) void { std.debug.assert(out.len == layout.size); @memset(out, 0); (format.Header{ .type = description.header.type, .machine = description.header.machine, .entry = description.header.entry, .flags = description.header.flags, .shoff = layout.shoff, .shnum = layout.shnum, .shstrndx = layout.shstrtab_index, }).write(out); for (description.sections, 0..) |section, index| { copyInto(out, @intCast(layout.section_offsets[index]), section.bytes); } writeRelocations(description, layout, out); writeSymbols(description, layout, out); writeSectionHeaders(description, layout, out);}/// Plans the layout (the planned file offsets for every record), allocates the/// output once at the planned size, writes it, and frees the layout, for tests/// and tools that want the object's bytes in one call. The returned bytes/// belong to the caller, who frees them with the same allocator.pub fn build(allocator: Allocator, description: Description) Error![]u8 { var layout = try Layout.plan(allocator, description); defer layout.deinit(allocator); const out = try allocator.alloc(u8, layout.size); write(description, layout, out); return out;}/// Writes the relocation section of each described section that has/// relocations, for `write` to emit every relocation section. The function/// walks `layout.order` once, because that order already groups relocations by/// ascending target and keeps input order within a target.fn writeRelocations(description: Description, layout: Layout, out: []u8) void { var placed: usize = 0; for (description.sections, 0..) |_, index| { const count: usize = @intCast(layout.rela_counts[index]); if (count == 0) continue; const start: usize = @intCast(layout.rela_starts[index]); var slot: usize = 0; while (slot < count) : (slot += 1) { const relocation = description.relocations[@intCast(layout.order[placed + slot])]; (format.Rela{ .offset = relocation.offset, .info = relocation.info(), .addend = relocation.addend, }).write(out, start + slot * rela_size); } placed += count; } std.debug.assert(placed == layout.order.len);}/// Writes `.symtab` and `.strtab` in one pass, for `write` to emit the symbol/// table. One cursor gives both each name's position in the name table and the/// offset stored in its symbol entry, so the two always agree.fn writeSymbols(description: Description, layout: Layout, out: []u8) void { const table = out[@intCast(layout.symtab_offset)..]; const names = out[@intCast(layout.strtab_offset)..]; (format.SymbolRecord{}).write(table, 0); var cursor: usize = 1; for (description.symbols, 0..) |symbol, index| { var name_offset: u32 = 0; if (symbol.name.len != 0) { name_offset = @intCast(cursor); copyInto(names, cursor, symbol.name); cursor += symbol.name.len + 1; } (format.SymbolRecord{ .name_offset = name_offset, .info = symbol.info(), .other = symbol.other, .section_index = symbol.section_index, .value = symbol.value, .size = symbol.size, }).write(table, (index + 1) * sym_size); } std.debug.assert(cursor == layout.strtab_size);}/// Writes the section headers and `.shstrtab` in one pass, with one cursor for/// both, the same way `writeSymbols` pairs its two tables, for `write` to emit/// the section header table.fn writeSectionHeaders(description: Description, layout: Layout, out: []u8) void { const table = out[@intCast(layout.shoff)..]; const names = out[@intCast(layout.shstrtab_offset)..]; (format.SectionHeader{}).write(table, 0); var cursor: usize = 1; for (description.sections, 0..) |section, index| { copyInto(names, cursor, section.name); (format.SectionHeader{ .name_offset = @intCast(cursor), .section_type = section.section_type, .flags = section.flags, .address = section.address, .offset = layout.section_offsets[index], .size = section.wireSize(), .link = sectionLink(section, layout), .info = section.info, .alignment = section.alignment, .entry_size = section.entry_size, }).write(table, (index + 1) * shdr_size); cursor += section.name.len + 1; } var rela_index: usize = description.sections.len + 1; for (description.sections, 0..) |section, index| { const count = layout.rela_counts[index]; if (count == 0) continue; copyInto(names, cursor, layout_plan.rela_name_prefix); copyInto(names, cursor + layout_plan.rela_name_prefix.len, section.name); (format.SectionHeader{ .name_offset = @intCast(cursor), .section_type = std.elf.SHT_RELA, .offset = layout.rela_starts[index], .size = count * rela_size, .link = layout.symtab_index, .info = @intCast(index + 1), .alignment = 8, .entry_size = rela_size, }).write(table, rela_index * shdr_size); cursor += layout_plan.rela_name_prefix.len + section.name.len + 1; rela_index += 1; } std.debug.assert(rela_index == layout.symtab_index); writeTrailingHeaders(layout, table, names, cursor);}/// Writes the three headers every object ends with, for `writeSectionHeaders`/// to finish the table, in this fixed order: those of `.symtab`, `.strtab` and/// `.shstrtab`.fn writeTrailingHeaders(layout: Layout, table: []u8, names: []u8, start: usize) void { var cursor = start; copyInto(names, cursor, layout_plan.symtab_name); (format.SectionHeader{ .name_offset = @intCast(cursor), .section_type = std.elf.SHT_SYMTAB, .offset = layout.symtab_offset, .size = layout.symtab_size, .link = layout.strtab_index, .info = layout.first_global, .alignment = 8, .entry_size = sym_size, }).write(table, @as(usize, layout.symtab_index) * shdr_size); cursor += layout_plan.symtab_name.len + 1; copyInto(names, cursor, layout_plan.strtab_name); (format.SectionHeader{ .name_offset = @intCast(cursor), .section_type = std.elf.SHT_STRTAB, .offset = layout.strtab_offset, .size = layout.strtab_size, .alignment = 1, }).write(table, @as(usize, layout.strtab_index) * shdr_size); cursor += layout_plan.strtab_name.len + 1; copyInto(names, cursor, layout_plan.shstrtab_name); (format.SectionHeader{ .name_offset = @intCast(cursor), .section_type = std.elf.SHT_STRTAB, .offset = layout.shstrtab_offset, .size = layout.shstrtab_size, .alignment = 1, }).write(table, @as(usize, layout.shstrtab_index) * shdr_size); cursor += layout_plan.shstrtab_name.len + 1; std.debug.assert(cursor == layout.shstrtab_size);}/// Returns the symbol table's index for a group section, because the group's/// `sh_info` names its signature symbol by an index into that table./// `writeSectionHeaders` calls this function for each described section's/// `sh_link` field. Every other section keeps the link stated in its/// description (a value listing the object's header fields, sections, symbols/// and relocations).fn sectionLink(section: Section, layout: Layout) u32 { if (section.section_type == std.elf.SHT_GROUP) return layout.symtab_index; return section.link;}const Section = model.Section;const Symbol = model.Symbol;const Relocation = model.Relocation;test "ELF object writer emits a relocatable header without program headers" { const allocator = std.testing.allocator; const sections = [_]Section{Section.progbits(".text", "\xc3", 0, 1)}; const bytes = try build(allocator, .{ .sections = §ions }); defer allocator.free(bytes); const header = try format.readHeader(bytes); try std.testing.expectEqual(std.elf.ET.REL, header.type); try std.testing.expectEqual(std.elf.EM.X86_64, header.machine); try std.testing.expectEqual(@as(u64, 0), header.entry); try std.testing.expectEqual(@as(u64, 0), header.phoff); try std.testing.expectEqual(@as(u16, 0), header.phnum); try std.testing.expectEqual(@as(u16, 0), format.readU16(bytes, 54)); try std.testing.expectEqual(@as(u16, 1 + layout_plan.fixed_section_count), header.shnum);}test "ELF object writer places sections, symbols, and relocations where the layout says" { const allocator = std.testing.allocator; const description = Description{ .sections = &.{ Section.progbits(".text", "\xc3\x90\x90\x90", std.elf.SHF_EXECINSTR, 16), Section.nobits(".bss", 128, std.elf.SHF_WRITE, 8), }, .symbols = &.{ Symbol.section(1), Symbol.function("_start", 1, 0, 4), Symbol.undefinedFunction("helper"), }, .relocations = &.{Relocation.x86_64(1, 1, 3, .PLT32, -4)}, }; const bytes = try build(allocator, description); defer allocator.free(bytes); const view = try elf.view.View.parse(bytes); const text = (try view.find(".text")).?; try std.testing.expectEqualStrings("\xc3\x90\x90\x90", try view.sectionBytes(text.header)); try std.testing.expectEqual(@as(u64, 16), text.header.alignment); const reserved = (try view.find(".bss")).?; try std.testing.expectEqual(@as(u64, 128), reserved.header.size); try std.testing.expectEqual(@as(usize, 0), (try view.sectionBytes(reserved.header)).len); const symtab = (try view.find(".symtab")).?; try std.testing.expectEqual(@as(u32, 2), symtab.header.info); try std.testing.expectEqual(@as(usize, 4), try view.symbolCount(symtab.header)); const strtab = view.section(@intCast(symtab.header.link)); const start = try view.symbol(symtab.header, 2); try std.testing.expectEqualStrings("_start", try view.string(strtab, start.name_offset)); try std.testing.expectEqual(@as(u8, 0x12), start.info); const section_symbol = try view.symbol(symtab.header, 1); try std.testing.expectEqualStrings("", try view.string(strtab, section_symbol.name_offset)); const rela = (try view.find(".rela.text")).?; try std.testing.expectEqual(@as(u32, std.elf.SHT_RELA), rela.header.section_type); try std.testing.expectEqual(@as(u32, 1), rela.header.info); try std.testing.expectEqual(@as(u32, symtab.index), rela.header.link); const rela_bytes = try view.sectionBytes(rela.header); try std.testing.expectEqual(@as(usize, rela_size), rela_bytes.len); try std.testing.expectEqual(@as(u64, 1), format.readU64(rela_bytes, 0)); try std.testing.expectEqual(@as(u64, (3 << 32) | 4), format.readU64(rela_bytes, 8)); try std.testing.expectEqual(@as(i64, -4), @as(i64, @bitCast(format.readU64(rela_bytes, 16))));}test "ELF object writer points a group section at the symbol table" { const allocator = std.testing.allocator; var payload: [2 * 4]u8 = undefined; const description = Description{ .sections = &.{ Section.progbits(".text.inline", "\xc3", std.elf.SHF_EXECINSTR, 1), Section.group(".group", model.groupBytes(&payload, &.{1}), 1), }, .symbols = &.{Symbol.function("inlined", 1, 0, 1)}, }; const bytes = try build(allocator, description); defer allocator.free(bytes); const view = try elf.view.View.parse(bytes); const group = (try view.find(".group")).?; const symtab = (try view.find(".symtab")).?; try std.testing.expectEqual(@as(u32, symtab.index), group.header.link); try std.testing.expectEqual(@as(u32, 1), group.header.info); try std.testing.expectEqual(@as(u64, 4), group.header.entry_size); const group_bytes = try view.sectionBytes(group.header); try std.testing.expectEqual(@as(u32, std.elf.GRP_COMDAT), format.readU32(group_bytes, 0)); try std.testing.expectEqual(@as(u32, 1), format.readU32(group_bytes, 4));}test "ELF object writer zero fills the padding it leaves between sections" { const allocator = std.testing.allocator; const description = Description{ .sections = &.{ Section.progbits(".text", "\xc3", std.elf.SHF_EXECINSTR, 1), Section.progbits(".data", "\x01", std.elf.SHF_WRITE, 64), }, }; const bytes = try build(allocator, description); defer allocator.free(bytes); const view = try elf.view.View.parse(bytes); const data = (try view.find(".data")).?; try std.testing.expectEqual(@as(u64, 128), data.header.offset); for (bytes[65..128]) |pad| try std.testing.expectEqual(@as(u8, 0), pad);}Audit
| Definitions | 3 |
|---|---|
| Public names | 5 |
| Members | 0 |
| Version | 26.7.0 |
| Revision | daab053ee433 |