zld: allocate TextBlocks

temporarily by iterating over all defined TextBlocks. However,
once we merge this with MachO incremental, updates will be done
at the point of creation and/or update.

Also, fix mining TLV knowledge for working out TLV pointers.
This commit is contained in:
Jakub Konka 2021-07-08 00:29:10 +02:00
parent e524f43a6f
commit 7aeedc0912
4 changed files with 83 additions and 55 deletions

View File

@ -7,6 +7,7 @@ const fs = std.fs;
const io = std.io;
const log = std.log.scoped(.object);
const macho = std.macho;
const math = std.math;
const mem = std.mem;
const reloc = @import("reloc.zig");
const sort = std.sort;
@ -436,7 +437,7 @@ const TextBlockParser = struct {
.code = try self.allocator.dupe(u8, code),
.relocs = std.ArrayList(Relocation).init(self.allocator),
.rebases = std.ArrayList(u64).init(self.allocator),
.tlv_offsets = std.ArrayList(u64).init(self.allocator),
.tlv_offsets = std.ArrayList(TextBlock.TlvOffset).init(self.allocator),
.size = size,
.alignment = self.section.@"align",
};
@ -533,6 +534,14 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
}
}
// Update target section's metadata
// TODO should we update segment's size here too?
// How does it tie with incremental space allocs?
const tseg = &zld.load_commands.items[match.seg].Segment;
const tsect = &tseg.sections.items[match.sect];
tsect.size += block.size;
tsect.@"align" = math.max(tsect.@"align", block.alignment);
if (zld.blocks.getPtr(match)) |last| {
last.*.next = block;
block.prev = last.*;
@ -580,7 +589,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
.code = try self.allocator.dupe(u8, code),
.relocs = std.ArrayList(Relocation).init(self.allocator),
.rebases = std.ArrayList(u64).init(self.allocator),
.tlv_offsets = std.ArrayList(u64).init(self.allocator),
.tlv_offsets = std.ArrayList(TextBlock.TlvOffset).init(self.allocator),
.size = sect.size,
.alignment = sect.@"align",
};
@ -589,6 +598,14 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
try self.parseRelocs(zld, relocs, block, 0);
}
// Update target section's metadata
// TODO should we update segment's size here too?
// How does it tie with incremental space allocs?
const tseg = &zld.load_commands.items[match.seg].Segment;
const tsect = &tseg.sections.items[match.sect];
tsect.size += block.size;
tsect.@"align" = math.max(tsect.@"align", block.alignment);
if (zld.blocks.getPtr(match)) |last| {
last.*.next = block;
block.prev = last.*;

View File

@ -10,6 +10,7 @@ const Allocator = mem.Allocator;
const Dylib = @import("Dylib.zig");
const Object = @import("Object.zig");
const StringTable = @import("StringTable.zig");
const Zld = @import("Zld.zig");
/// Symbol name. Owned slice.
name: []const u8,
@ -80,6 +81,20 @@ pub const Regular = struct {
}
try std.fmt.format(writer, "}}", .{});
}
pub fn sectionId(self: Regular, zld: *Zld) u8 {
// TODO there might be a more generic way of doing this.
var section: u8 = 0;
for (zld.load_commands.items) |cmd, cmd_id| {
if (cmd != .Segment) break;
if (cmd_id == self.segment_id) {
section += @intCast(u8, self.section_id) + 1;
break;
}
section += @intCast(u8, cmd.Segment.sections.items.len);
}
return section;
}
};
pub const Tentative = struct {

View File

@ -129,10 +129,15 @@ pub const TextBlock = struct {
size: u64,
alignment: u32,
rebases: std.ArrayList(u64),
tlv_offsets: std.ArrayList(u64),
tlv_offsets: std.ArrayList(TlvOffset),
next: ?*TextBlock = null,
prev: ?*TextBlock = null,
pub const TlvOffset = struct {
local_sym_index: u32,
offset: u64,
};
pub fn deinit(block: *TextBlock, allocator: *Allocator) void {
if (block.aliases) |aliases| {
allocator.free(aliases);
@ -281,10 +286,11 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
try self.addRpaths(args.rpaths);
try self.addDataInCodeLC();
try self.addCodeSignatureLC();
// try self.allocateTextSegment();
// try self.allocateDataConstSegment();
// try self.allocateDataSegment();
// self.allocateLinkeditSegment();
try self.allocateTextSegment();
try self.allocateDataConstSegment();
try self.allocateDataSegment();
self.allocateLinkeditSegment();
try self.allocateTextBlocks();
var it = self.blocks.iterator();
while (it.next()) |entry| {
@ -292,6 +298,7 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
const sect = seg.sections.items[entry.key_ptr.sect];
log.warn("\n\n{s},{s} contents:", .{ segmentName(sect), sectionName(sect) });
log.warn("{}", .{sect});
entry.value_ptr.*.print(self);
}
return error.TODO;
@ -865,14 +872,14 @@ fn sortSections(self: *Zld) !void {
while (it.next()) |entry| {
const old = entry.key_ptr.*;
const sect = if (old.seg == self.text_segment_cmd_index.?)
text_index_mapping.get(old.sect)
text_index_mapping.get(old.sect).?
else if (old.seg == self.data_const_segment_cmd_index.?)
data_const_index_mapping.get(old.sect)
data_const_index_mapping.get(old.sect).?
else
data_index_mapping.get(old.sect);
data_index_mapping.get(old.sect).?;
transient.putAssumeCapacityNoClobber(.{
.seg = old.seg,
.sect = old.sect,
.sect = sect,
}, entry.value_ptr.*);
}
@ -880,6 +887,18 @@ fn sortSections(self: *Zld) !void {
self.blocks.deinit(self.allocator);
self.blocks = transient;
}
for (self.locals.items) |sym, i| {
if (i == 0) continue; // skip the null symbol
assert(sym.payload == .regular);
const reg = &sym.payload.regular;
reg.section_id = if (reg.segment_id == self.text_segment_cmd_index.?)
text_index_mapping.get(reg.section_id).?
else if (reg.segment_id == self.data_const_segment_cmd_index.?)
data_const_index_mapping.get(reg.section_id).?
else
data_index_mapping.get(reg.section_id).?;
}
}
fn allocateTextSegment(self: *Zld) !void {
@ -991,50 +1010,26 @@ fn allocateSegment(self: *Zld, index: u16, offset: u64) !void {
seg.inner.vmsize = seg_size_aligned;
}
fn allocateSymbol(self: *Zld, symbol: *Symbol) !void {
const reg = &symbol.payload.regular;
const object = reg.file orelse return;
const source_sect = &object.sections.items[reg.section];
const target_map = source_sect.target_map orelse {
log.debug("section '{s},{s}' not mapped for symbol '{s}'", .{
segmentName(source_sect.inner),
sectionName(source_sect.inner),
symbol.name,
});
return;
};
fn allocateTextBlocks(self: *Zld) !void {
var it = self.blocks.iterator();
while (it.next()) |entry| {
const match = entry.key_ptr.*;
var block: *TextBlock = entry.value_ptr.*;
const target_seg = self.load_commands.items[target_map.segment_id].Segment;
const target_sect = target_seg.sections.items[target_map.section_id];
const target_addr = target_sect.addr + target_map.offset;
const address = reg.address - source_sect.inner.addr + target_addr;
const seg = self.load_commands.items[match.seg].Segment;
const sect = seg.sections.items[match.sect];
var base_addr: u64 = sect.addr + sect.size;
log.debug("resolving symbol '{s}' at 0x{x}", .{ symbol.name, address });
while (true) {
const sym = self.locals.items[block.local_sym_index];
assert(sym.payload == .regular);
sym.payload.regular.address = base_addr - block.size;
base_addr -= block.size;
// TODO there might be a more generic way of doing this.
var section: u8 = 0;
for (self.load_commands.items) |cmd, cmd_id| {
if (cmd != .Segment) break;
if (cmd_id == target_map.segment_id) {
section += @intCast(u8, target_map.section_id) + 1;
break;
if (block.prev) |prev| {
block = prev;
} else break;
}
section += @intCast(u8, cmd.Segment.sections.items.len);
}
reg.address = address;
reg.section = section;
}
fn allocateSymbols(self: *Zld) !void {
for (self.locals.items) |symbol| {
if (symbol.payload != .regular) continue;
try self.allocateSymbol(symbol);
}
for (self.globals.values()) |symbol| {
if (symbol.payload != .regular) continue;
try self.allocateSymbol(symbol);
}
}
@ -1487,7 +1482,7 @@ fn resolveSymbols(self: *Zld) !void {
.code = code,
.relocs = std.ArrayList(Relocation).init(self.allocator),
.rebases = std.ArrayList(u64).init(self.allocator),
.tlv_offsets = std.ArrayList(u64).init(self.allocator),
.tlv_offsets = std.ArrayList(TextBlock.TlvOffset).init(self.allocator),
.size = size,
.alignment = alignment,
};

View File

@ -674,10 +674,11 @@ pub const Parser = struct {
}
// TLV is handled via a separate offset mechanism.
// Save the offset to the initializer.
// TODO I believe this can be simplified a lot!
if (sect_type == macho.S_THREAD_LOCAL_VARIABLES) {
try self.block.tlv_offsets.append(out_rel.offset);
try self.block.tlv_offsets.append(.{
.local_sym_index = out_rel.target.payload.regular.local_sym_index,
.offset = out_rel.offset,
});
}
},
}