mirror of
https://github.com/ziglang/zig.git
synced 2026-02-14 13:30:45 +00:00
Merge pull request #21210 from jacobly0/eh-frame
Dwarf: implement .eh_frame
This commit is contained in:
commit
1a178d4995
@ -64,7 +64,7 @@ stage3-debug/bin/zig build \
|
||||
|
||||
stage3-debug/bin/zig build test docs \
|
||||
--maxrss 21000000000 \
|
||||
-Dlldb=$HOME/deps/lldb-zig/Debug-62538077d/bin/lldb \
|
||||
-Dlldb=$HOME/deps/lldb-zig/Debug-70b8227f1/bin/lldb \
|
||||
-fqemu \
|
||||
-fwasmtime \
|
||||
-Dstatic-llvm \
|
||||
|
||||
@ -64,7 +64,7 @@ stage3-release/bin/zig build \
|
||||
|
||||
stage3-release/bin/zig build test docs \
|
||||
--maxrss 21000000000 \
|
||||
-Dlldb=$HOME/deps/lldb-zig/Release-62538077d/bin/lldb \
|
||||
-Dlldb=$HOME/deps/lldb-zig/Release-70b8227f1/bin/lldb \
|
||||
-fqemu \
|
||||
-fwasmtime \
|
||||
-Dstatic-llvm \
|
||||
|
||||
@ -125,7 +125,7 @@ pub const readILEB128 = readIleb128;
|
||||
pub fn writeIleb128(writer: anytype, arg: anytype) !void {
|
||||
const Arg = @TypeOf(arg);
|
||||
const Int = switch (Arg) {
|
||||
comptime_int => std.math.IntFittingRange(-arg - 1, arg),
|
||||
comptime_int => std.math.IntFittingRange(-@abs(arg), @abs(arg)),
|
||||
else => Arg,
|
||||
};
|
||||
const Signed = if (@typeInfo(Int).Int.bits < 8) i8 else Int;
|
||||
|
||||
@ -114,14 +114,15 @@ pub fn clone() callconv(.Naked) usize {
|
||||
\\ movq %%rcx,(%%rsi)
|
||||
\\ syscall
|
||||
\\ testq %%rax,%%rax
|
||||
\\ jnz 1f
|
||||
\\ jz 1f
|
||||
\\ retq
|
||||
\\1: .cfi_undefined %%rip
|
||||
\\ xorl %%ebp,%%ebp
|
||||
\\ popq %%rdi
|
||||
\\ callq *%%r9
|
||||
\\ movl %%eax,%%edi
|
||||
\\ movl $60,%%eax // SYS_exit
|
||||
\\ syscall
|
||||
\\1: ret
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
@ -249,6 +249,7 @@ fn _start() callconv(.Naked) noreturn {
|
||||
// linker explicitly.
|
||||
asm volatile (switch (native_arch) {
|
||||
.x86_64 =>
|
||||
\\ .cfi_undefined %%rip
|
||||
\\ xorl %%ebp, %%ebp
|
||||
\\ movq %%rsp, %%rdi
|
||||
\\ andq $-16, %%rsp
|
||||
|
||||
@ -13,6 +13,7 @@ const Value = @import("Value.zig");
|
||||
const Type = @import("Type.zig");
|
||||
const InternPool = @import("InternPool.zig");
|
||||
const Zcu = @import("Zcu.zig");
|
||||
const types_resolved = @import("Air/types_resolved.zig");
|
||||
|
||||
instructions: std.MultiArrayList(Inst).Slice,
|
||||
/// The meaning of this data is determined by `Inst.Tag` value.
|
||||
@ -1899,4 +1900,6 @@ pub fn unwrapSwitch(air: *const Air, switch_inst: Inst.Index) UnwrappedSwitch {
|
||||
};
|
||||
}
|
||||
|
||||
pub const typesFullyResolved = @import("Air/types_resolved.zig").typesFullyResolved;
|
||||
pub const typesFullyResolved = types_resolved.typesFullyResolved;
|
||||
pub const typeFullyResolved = types_resolved.checkType;
|
||||
pub const valFullyResolved = types_resolved.checkVal;
|
||||
|
||||
@ -432,8 +432,10 @@ fn checkRef(ref: Air.Inst.Ref, zcu: *Zcu) bool {
|
||||
return checkVal(Value.fromInterned(ip_index), zcu);
|
||||
}
|
||||
|
||||
fn checkVal(val: Value, zcu: *Zcu) bool {
|
||||
if (!checkType(val.typeOf(zcu), zcu)) return false;
|
||||
pub fn checkVal(val: Value, zcu: *Zcu) bool {
|
||||
const ty = val.typeOf(zcu);
|
||||
if (!checkType(ty, zcu)) return false;
|
||||
if (ty.toIntern() == .type_type and !checkType(val.toType(), zcu)) return false;
|
||||
// Check for lazy values
|
||||
switch (zcu.intern_pool.indexToKey(val.toIntern())) {
|
||||
.int => |int| switch (int.storage) {
|
||||
@ -446,9 +448,11 @@ fn checkVal(val: Value, zcu: *Zcu) bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn checkType(ty: Type, zcu: *Zcu) bool {
|
||||
pub fn checkType(ty: Type, zcu: *Zcu) bool {
|
||||
const ip = &zcu.intern_pool;
|
||||
return switch (ty.zigTypeTag(zcu)) {
|
||||
return switch (ty.zigTypeTagOrPoison(zcu) catch |err| switch (err) {
|
||||
error.GenericPoison => return true,
|
||||
}) {
|
||||
.Type,
|
||||
.Void,
|
||||
.Bool,
|
||||
|
||||
@ -192,11 +192,12 @@ pub fn toBigIntAdvanced(
|
||||
zcu: *Zcu,
|
||||
tid: strat.Tid(),
|
||||
) Zcu.CompileError!BigIntConst {
|
||||
const ip = &zcu.intern_pool;
|
||||
return switch (val.toIntern()) {
|
||||
.bool_false => BigIntMutable.init(&space.limbs, 0).toConst(),
|
||||
.bool_true => BigIntMutable.init(&space.limbs, 1).toConst(),
|
||||
.null_value => BigIntMutable.init(&space.limbs, 0).toConst(),
|
||||
else => switch (zcu.intern_pool.indexToKey(val.toIntern())) {
|
||||
else => switch (ip.indexToKey(val.toIntern())) {
|
||||
.int => |int| switch (int.storage) {
|
||||
.u64, .i64, .big_int => int.storage.toBigInt(space),
|
||||
.lazy_align, .lazy_size => |ty| {
|
||||
@ -214,6 +215,7 @@ pub fn toBigIntAdvanced(
|
||||
&space.limbs,
|
||||
(try val.getUnsignedIntInner(strat, zcu, tid)).?,
|
||||
).toConst(),
|
||||
.err => |err| BigIntMutable.init(&space.limbs, ip.getErrorValueIfExists(err.name).?).toConst(),
|
||||
else => unreachable,
|
||||
},
|
||||
};
|
||||
@ -326,15 +328,11 @@ pub fn toBool(val: Value) bool {
|
||||
};
|
||||
}
|
||||
|
||||
fn ptrHasIntAddr(val: Value, zcu: *Zcu) bool {
|
||||
return zcu.intern_pool.getBackingAddrTag(val.toIntern()).? == .int;
|
||||
}
|
||||
|
||||
/// Write a Value's contents to `buffer`.
|
||||
///
|
||||
/// Asserts that buffer.len >= ty.abiSize(). The buffer is allowed to extend past
|
||||
/// the end of the value in memory.
|
||||
pub fn writeToMemory(val: Value, ty: Type, pt: Zcu.PerThread, buffer: []u8) error{
|
||||
pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{
|
||||
ReinterpretDeclRef,
|
||||
IllDefinedMemoryLayout,
|
||||
Unimplemented,
|
||||
@ -343,19 +341,25 @@ pub fn writeToMemory(val: Value, ty: Type, pt: Zcu.PerThread, buffer: []u8) erro
|
||||
const zcu = pt.zcu;
|
||||
const target = zcu.getTarget();
|
||||
const endian = target.cpu.arch.endian();
|
||||
const ip = &zcu.intern_pool;
|
||||
const ty = val.typeOf(zcu);
|
||||
if (val.isUndef(zcu)) {
|
||||
const size: usize = @intCast(ty.abiSize(zcu));
|
||||
@memset(buffer[0..size], 0xaa);
|
||||
return;
|
||||
}
|
||||
const ip = &zcu.intern_pool;
|
||||
switch (ty.zigTypeTag(zcu)) {
|
||||
.Void => {},
|
||||
.Bool => {
|
||||
buffer[0] = @intFromBool(val.toBool());
|
||||
},
|
||||
.Int, .Enum => {
|
||||
const int_info = ty.intInfo(zcu);
|
||||
.Int, .Enum, .ErrorSet, .Pointer => |tag| {
|
||||
const int_ty = if (tag == .Pointer) int_ty: {
|
||||
if (ty.isSlice(zcu)) return error.IllDefinedMemoryLayout;
|
||||
if (ip.getBackingAddrTag(val.toIntern()).? != .int) return error.ReinterpretDeclRef;
|
||||
break :int_ty Type.usize;
|
||||
} else ty;
|
||||
const int_info = int_ty.intInfo(zcu);
|
||||
const bits = int_info.bits;
|
||||
const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
|
||||
|
||||
@ -379,7 +383,7 @@ pub fn writeToMemory(val: Value, ty: Type, pt: Zcu.PerThread, buffer: []u8) erro
|
||||
var buf_off: usize = 0;
|
||||
while (elem_i < len) : (elem_i += 1) {
|
||||
const elem_val = try val.elemValue(pt, elem_i);
|
||||
try elem_val.writeToMemory(elem_ty, pt, buffer[buf_off..]);
|
||||
try elem_val.writeToMemory(pt, buffer[buf_off..]);
|
||||
buf_off += elem_size;
|
||||
}
|
||||
},
|
||||
@ -403,8 +407,7 @@ pub fn writeToMemory(val: Value, ty: Type, pt: Zcu.PerThread, buffer: []u8) erro
|
||||
.elems => |elems| elems[field_index],
|
||||
.repeated_elem => |elem| elem,
|
||||
});
|
||||
const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]);
|
||||
try writeToMemory(field_val, field_ty, pt, buffer[off..]);
|
||||
try writeToMemory(field_val, pt, buffer[off..]);
|
||||
},
|
||||
.@"packed" => {
|
||||
const byte_count = (@as(usize, @intCast(ty.bitSize(zcu))) + 7) / 8;
|
||||
@ -412,22 +415,6 @@ pub fn writeToMemory(val: Value, ty: Type, pt: Zcu.PerThread, buffer: []u8) erro
|
||||
},
|
||||
}
|
||||
},
|
||||
.ErrorSet => {
|
||||
const bits = zcu.errorSetBits();
|
||||
const byte_count: u16 = @intCast((@as(u17, bits) + 7) / 8);
|
||||
|
||||
const name = switch (ip.indexToKey(val.toIntern())) {
|
||||
.err => |err| err.name,
|
||||
.error_union => |error_union| error_union.val.err_name,
|
||||
else => unreachable,
|
||||
};
|
||||
var bigint_buffer: BigIntSpace = undefined;
|
||||
const bigint = BigIntMutable.init(
|
||||
&bigint_buffer.limbs,
|
||||
ip.getErrorValueIfExists(name).?,
|
||||
).toConst();
|
||||
bigint.writeTwosComplement(buffer[0..byte_count], endian);
|
||||
},
|
||||
.Union => switch (ty.containerLayout(zcu)) {
|
||||
.auto => return error.IllDefinedMemoryLayout, // Sema is supposed to have emitted a compile error already
|
||||
.@"extern" => {
|
||||
@ -437,11 +424,11 @@ pub fn writeToMemory(val: Value, ty: Type, pt: Zcu.PerThread, buffer: []u8) erro
|
||||
const field_type = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
|
||||
const field_val = try val.fieldValue(pt, field_index);
|
||||
const byte_count: usize = @intCast(field_type.abiSize(zcu));
|
||||
return writeToMemory(field_val, field_type, pt, buffer[0..byte_count]);
|
||||
return writeToMemory(field_val, pt, buffer[0..byte_count]);
|
||||
} else {
|
||||
const backing_ty = try ty.unionBackingType(pt);
|
||||
const byte_count: usize = @intCast(backing_ty.abiSize(zcu));
|
||||
return writeToMemory(val.unionValue(zcu), backing_ty, pt, buffer[0..byte_count]);
|
||||
return writeToMemory(val.unionValue(zcu), pt, buffer[0..byte_count]);
|
||||
}
|
||||
},
|
||||
.@"packed" => {
|
||||
@ -450,19 +437,13 @@ pub fn writeToMemory(val: Value, ty: Type, pt: Zcu.PerThread, buffer: []u8) erro
|
||||
return writeToPackedMemory(val, ty, pt, buffer[0..byte_count], 0);
|
||||
},
|
||||
},
|
||||
.Pointer => {
|
||||
if (ty.isSlice(zcu)) return error.IllDefinedMemoryLayout;
|
||||
if (!val.ptrHasIntAddr(zcu)) return error.ReinterpretDeclRef;
|
||||
return val.writeToMemory(Type.usize, pt, buffer);
|
||||
},
|
||||
.Optional => {
|
||||
if (!ty.isPtrLikeOptional(zcu)) return error.IllDefinedMemoryLayout;
|
||||
const child = ty.optionalChild(zcu);
|
||||
const opt_val = val.optionalValue(zcu);
|
||||
if (opt_val) |some| {
|
||||
return some.writeToMemory(child, pt, buffer);
|
||||
return some.writeToMemory(pt, buffer);
|
||||
} else {
|
||||
return writeToMemory(try pt.intValue(Type.usize, 0), Type.usize, pt, buffer);
|
||||
return writeToMemory(try pt.intValue(Type.usize, 0), pt, buffer);
|
||||
}
|
||||
},
|
||||
else => return error.Unimplemented,
|
||||
@ -582,7 +563,7 @@ pub fn writeToPackedMemory(
|
||||
},
|
||||
.Pointer => {
|
||||
assert(!ty.isSlice(zcu)); // No well defined layout.
|
||||
if (!val.ptrHasIntAddr(zcu)) return error.ReinterpretDeclRef;
|
||||
if (ip.getBackingAddrTag(val.toIntern()).? != .int) return error.ReinterpretDeclRef;
|
||||
return val.writeToPackedMemory(Type.usize, pt, buffer, bit_offset);
|
||||
},
|
||||
.Optional => {
|
||||
@ -3658,14 +3639,15 @@ pub fn mulAddScalar(
|
||||
|
||||
/// If the value is represented in-memory as a series of bytes that all
|
||||
/// have the same value, return that byte value, otherwise null.
|
||||
pub fn hasRepeatedByteRepr(val: Value, ty: Type, pt: Zcu.PerThread) !?u8 {
|
||||
pub fn hasRepeatedByteRepr(val: Value, pt: Zcu.PerThread) !?u8 {
|
||||
const zcu = pt.zcu;
|
||||
const ty = val.typeOf(zcu);
|
||||
const abi_size = std.math.cast(usize, ty.abiSize(zcu)) orelse return null;
|
||||
assert(abi_size >= 1);
|
||||
const byte_buffer = try zcu.gpa.alloc(u8, abi_size);
|
||||
defer zcu.gpa.free(byte_buffer);
|
||||
|
||||
writeToMemory(val, ty, pt, byte_buffer) catch |err| switch (err) {
|
||||
writeToMemory(val, pt, byte_buffer) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
error.ReinterpretDeclRef => return null,
|
||||
// TODO: The writeToMemory function was originally created for the purpose
|
||||
|
||||
@ -2560,12 +2560,17 @@ pub fn populateTestFunctions(
|
||||
pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
|
||||
const zcu = pt.zcu;
|
||||
const comp = zcu.comp;
|
||||
const ip = &zcu.intern_pool;
|
||||
|
||||
const nav = zcu.intern_pool.getNav(nav_index);
|
||||
const codegen_prog_node = zcu.codegen_prog_node.start(nav.fqn.toSlice(&zcu.intern_pool), 0);
|
||||
const codegen_prog_node = zcu.codegen_prog_node.start(nav.fqn.toSlice(ip), 0);
|
||||
defer codegen_prog_node.end();
|
||||
|
||||
if (comp.bin_file) |lf| {
|
||||
if (!Air.valFullyResolved(zcu.navValue(nav_index), zcu)) {
|
||||
// The value of this nav failed to resolve. This is a transitive failure.
|
||||
// TODO: do we need to mark this failure anywhere? I don't think so, since compilation
|
||||
// will fail due to the type error anyway.
|
||||
} else if (comp.bin_file) |lf| {
|
||||
lf.updateNav(pt, nav_index) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
error.AnalysisFail => {
|
||||
@ -2605,7 +2610,11 @@ pub fn linkerUpdateContainerType(pt: Zcu.PerThread, ty: InternPool.Index) !void
|
||||
const codegen_prog_node = zcu.codegen_prog_node.start(Type.fromInterned(ty).containerTypeName(ip).toSlice(ip), 0);
|
||||
defer codegen_prog_node.end();
|
||||
|
||||
if (comp.bin_file) |lf| {
|
||||
if (!Air.typeFullyResolved(Type.fromInterned(ty), zcu)) {
|
||||
// This type failed to resolve. This is a transitive failure.
|
||||
// TODO: do we need to mark this failure anywhere? I don't think so, since compilation
|
||||
// will fail due to the type error anyway.
|
||||
} else if (comp.bin_file) |lf| {
|
||||
lf.updateContainerType(pt, ty) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
else => |e| log.err("codegen type failed: {s}", .{@errorName(e)}),
|
||||
|
||||
@ -3357,7 +3357,7 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
|
||||
.vector_type => {
|
||||
assert(determineSimdStoreStrategy(ty, zcu, func.target.*) == .direct);
|
||||
var buf: [16]u8 = undefined;
|
||||
val.writeToMemory(ty, pt, &buf) catch unreachable;
|
||||
val.writeToMemory(pt, &buf) catch unreachable;
|
||||
return func.storeSimdImmd(buf);
|
||||
},
|
||||
.struct_type => {
|
||||
|
||||
@ -1491,6 +1491,46 @@ fn asmPseudo(self: *Self, ops: Mir.Inst.Ops) !void {
|
||||
});
|
||||
}
|
||||
|
||||
fn asmPseudoRegister(self: *Self, ops: Mir.Inst.Ops, reg: Register) !void {
|
||||
assert(std.mem.startsWith(u8, @tagName(ops), "pseudo_") and
|
||||
std.mem.endsWith(u8, @tagName(ops), "_r"));
|
||||
_ = try self.addInst(.{
|
||||
.tag = .pseudo,
|
||||
.ops = ops,
|
||||
.data = .{ .r = .{ .r1 = reg } },
|
||||
});
|
||||
}
|
||||
|
||||
fn asmPseudoImmediate(self: *Self, ops: Mir.Inst.Ops, imm: Immediate) !void {
|
||||
assert(std.mem.startsWith(u8, @tagName(ops), "pseudo_") and
|
||||
std.mem.endsWith(u8, @tagName(ops), "_i_s"));
|
||||
_ = try self.addInst(.{
|
||||
.tag = .pseudo,
|
||||
.ops = ops,
|
||||
.data = .{ .i = .{ .i = @bitCast(imm.signed) } },
|
||||
});
|
||||
}
|
||||
|
||||
fn asmPseudoRegisterRegister(self: *Self, ops: Mir.Inst.Ops, reg1: Register, reg2: Register) !void {
|
||||
assert(std.mem.startsWith(u8, @tagName(ops), "pseudo_") and
|
||||
std.mem.endsWith(u8, @tagName(ops), "_rr"));
|
||||
_ = try self.addInst(.{
|
||||
.tag = .pseudo,
|
||||
.ops = ops,
|
||||
.data = .{ .rr = .{ .r1 = reg1, .r2 = reg2 } },
|
||||
});
|
||||
}
|
||||
|
||||
fn asmPseudoRegisterImmediate(self: *Self, ops: Mir.Inst.Ops, reg: Register, imm: Immediate) !void {
|
||||
assert(std.mem.startsWith(u8, @tagName(ops), "pseudo_") and
|
||||
std.mem.endsWith(u8, @tagName(ops), "_ri_s"));
|
||||
_ = try self.addInst(.{
|
||||
.tag = .pseudo,
|
||||
.ops = ops,
|
||||
.data = .{ .ri = .{ .r1 = reg, .i = @bitCast(imm.signed) } },
|
||||
});
|
||||
}
|
||||
|
||||
fn asmRegister(self: *Self, tag: Mir.Inst.FixedTag, reg: Register) !void {
|
||||
_ = try self.addInst(.{
|
||||
.tag = tag[1],
|
||||
@ -1877,7 +1917,10 @@ fn gen(self: *Self) InnerError!void {
|
||||
const cc = abi.resolveCallingConvention(fn_info.cc, self.target.*);
|
||||
if (cc != .Naked) {
|
||||
try self.asmRegister(.{ ._, .push }, .rbp);
|
||||
try self.asmPseudoImmediate(.pseudo_cfi_adjust_cfa_offset_i_s, Immediate.s(8));
|
||||
try self.asmPseudoRegisterImmediate(.pseudo_cfi_rel_offset_ri_s, .rbp, Immediate.s(0));
|
||||
try self.asmRegisterRegister(.{ ._, .mov }, .rbp, .rsp);
|
||||
try self.asmPseudoRegister(.pseudo_cfi_def_cfa_register_r, .rbp);
|
||||
const backpatch_push_callee_preserved_regs = try self.asmPlaceholder();
|
||||
const backpatch_frame_align = try self.asmPlaceholder();
|
||||
const backpatch_frame_align_extra = try self.asmPlaceholder();
|
||||
@ -1962,6 +2005,7 @@ fn gen(self: *Self) InnerError!void {
|
||||
const backpatch_stack_dealloc = try self.asmPlaceholder();
|
||||
const backpatch_pop_callee_preserved_regs = try self.asmPlaceholder();
|
||||
try self.asmRegister(.{ ._, .pop }, .rbp);
|
||||
try self.asmPseudoRegisterImmediate(.pseudo_cfi_def_cfa_ri_s, .rsp, Immediate.s(8));
|
||||
try self.asmOpOnly(.{ ._, .ret });
|
||||
|
||||
const frame_layout = try self.computeFrameLayout(cc);
|
||||
@ -14038,7 +14082,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
var mnem_it = mem.tokenizeAny(u8, line, " \t");
|
||||
var prefix: Instruction.Prefix = .none;
|
||||
const mnem_str = while (mnem_it.next()) |mnem_str| {
|
||||
if (mem.startsWith(u8, mnem_str, "#")) continue :next_line;
|
||||
if (mnem_str[0] == '#') continue :next_line;
|
||||
if (mem.startsWith(u8, mnem_str, "//")) continue :next_line;
|
||||
if (std.meta.stringToEnum(Instruction.Prefix, mnem_str)) |pre| {
|
||||
if (prefix != .none) return self.fail("extra prefix: '{s}'", .{mnem_str});
|
||||
@ -14063,8 +14107,14 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
}
|
||||
label_gop.value_ptr.target = @intCast(self.mir_instructions.len);
|
||||
} else continue;
|
||||
if (mnem_str[0] == '.') {
|
||||
if (prefix != .none) return self.fail("prefixed directive: '{s} {s}'", .{ @tagName(prefix), mnem_str });
|
||||
prefix = .directive;
|
||||
}
|
||||
|
||||
var mnem_size: ?Memory.Size = if (mem.endsWith(u8, mnem_str, "b"))
|
||||
var mnem_size: ?Memory.Size = if (prefix == .directive)
|
||||
null
|
||||
else if (mem.endsWith(u8, mnem_str, "b"))
|
||||
.byte
|
||||
else if (mem.endsWith(u8, mnem_str, "w"))
|
||||
.word
|
||||
@ -14095,7 +14145,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
mnem_size = fixed_mnem_size;
|
||||
}
|
||||
const mnem_name = @tagName(mnem_tag);
|
||||
const mnem_fixed_tag: Mir.Inst.FixedTag = for (std.enums.values(Mir.Inst.Fixes)) |fixes| {
|
||||
const mnem_fixed_tag: Mir.Inst.FixedTag = if (prefix == .directive)
|
||||
.{ ._, .pseudo }
|
||||
else for (std.enums.values(Mir.Inst.Fixes)) |fixes| {
|
||||
const fixes_name = @tagName(fixes);
|
||||
const space_i = mem.indexOfScalar(u8, fixes_name, ' ');
|
||||
const fixes_prefix = if (space_i) |i|
|
||||
@ -14116,7 +14168,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
} else {
|
||||
assert(prefix != .none); // no combination of fixes produced a known mnemonic
|
||||
return self.fail("invalid prefix for mnemonic: '{s} {s}'", .{
|
||||
@tagName(prefix), mnem_str,
|
||||
@tagName(prefix), mnem_name,
|
||||
});
|
||||
};
|
||||
|
||||
@ -14324,7 +14376,62 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
} else return self.fail("invalid operand: '{s}'", .{op_str});
|
||||
} else if (op_it.next()) |op_str| return self.fail("extra operand: '{s}'", .{op_str});
|
||||
|
||||
(switch (ops[0]) {
|
||||
(if (prefix == .directive) switch (mnem_tag) {
|
||||
.@".cfi_def_cfa" => if (ops[0] == .reg and ops[1] == .imm and ops[2] == .none)
|
||||
self.asmPseudoRegisterImmediate(.pseudo_cfi_def_cfa_ri_s, ops[0].reg, ops[1].imm)
|
||||
else
|
||||
error.InvalidInstruction,
|
||||
.@".cfi_def_cfa_register" => if (ops[0] == .reg and ops[1] == .none)
|
||||
self.asmPseudoRegister(.pseudo_cfi_def_cfa_register_r, ops[0].reg)
|
||||
else
|
||||
error.InvalidInstruction,
|
||||
.@".cfi_def_cfa_offset" => if (ops[0] == .imm and ops[1] == .none)
|
||||
self.asmPseudoImmediate(.pseudo_cfi_def_cfa_offset_i_s, ops[0].imm)
|
||||
else
|
||||
error.InvalidInstruction,
|
||||
.@".cfi_adjust_cfa_offset" => if (ops[0] == .imm and ops[1] == .none)
|
||||
self.asmPseudoImmediate(.pseudo_cfi_adjust_cfa_offset_i_s, ops[0].imm)
|
||||
else
|
||||
error.InvalidInstruction,
|
||||
.@".cfi_offset" => if (ops[0] == .reg and ops[1] == .imm and ops[2] == .none)
|
||||
self.asmPseudoRegisterImmediate(.pseudo_cfi_offset_ri_s, ops[0].reg, ops[1].imm)
|
||||
else
|
||||
error.InvalidInstruction,
|
||||
.@".cfi_val_offset" => if (ops[0] == .reg and ops[1] == .imm and ops[2] == .none)
|
||||
self.asmPseudoRegisterImmediate(.pseudo_cfi_val_offset_ri_s, ops[0].reg, ops[1].imm)
|
||||
else
|
||||
error.InvalidInstruction,
|
||||
.@".cfi_rel_offset" => if (ops[0] == .reg and ops[1] == .imm and ops[2] == .none)
|
||||
self.asmPseudoRegisterImmediate(.pseudo_cfi_rel_offset_ri_s, ops[0].reg, ops[1].imm)
|
||||
else
|
||||
error.InvalidInstruction,
|
||||
.@".cfi_register" => if (ops[0] == .reg and ops[1] == .reg and ops[2] == .none)
|
||||
self.asmPseudoRegisterRegister(.pseudo_cfi_register_rr, ops[0].reg, ops[1].reg)
|
||||
else
|
||||
error.InvalidInstruction,
|
||||
.@".cfi_restore" => if (ops[0] == .reg and ops[1] == .none)
|
||||
self.asmPseudoRegister(.pseudo_cfi_restore_r, ops[0].reg)
|
||||
else
|
||||
error.InvalidInstruction,
|
||||
.@".cfi_undefined" => if (ops[0] == .reg and ops[1] == .none)
|
||||
self.asmPseudoRegister(.pseudo_cfi_undefined_r, ops[0].reg)
|
||||
else
|
||||
error.InvalidInstruction,
|
||||
.@".cfi_same_value" => if (ops[0] == .reg and ops[1] == .none)
|
||||
self.asmPseudoRegister(.pseudo_cfi_same_value_r, ops[0].reg)
|
||||
else
|
||||
error.InvalidInstruction,
|
||||
.@".cfi_remember_state" => if (ops[0] == .none)
|
||||
self.asmPseudo(.pseudo_cfi_remember_state_none)
|
||||
else
|
||||
error.InvalidInstruction,
|
||||
.@".cfi_restore_state" => if (ops[0] == .none)
|
||||
self.asmPseudo(.pseudo_cfi_restore_state_none)
|
||||
else
|
||||
error.InvalidInstruction,
|
||||
.@".cfi_escape" => error.InvalidInstruction,
|
||||
else => unreachable,
|
||||
} else switch (ops[0]) {
|
||||
.none => self.asmOpOnly(mnem_fixed_tag),
|
||||
.reg => |reg0| switch (ops[1]) {
|
||||
.none => self.asmRegister(mnem_fixed_tag, reg0),
|
||||
@ -19210,14 +19317,6 @@ fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError {
|
||||
return error.CodegenFail;
|
||||
}
|
||||
|
||||
fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError {
|
||||
@branchHint(.cold);
|
||||
assert(self.err_msg == null);
|
||||
const gpa = self.gpa;
|
||||
self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args);
|
||||
return error.CodegenFail;
|
||||
}
|
||||
|
||||
fn parseRegName(name: []const u8) ?Register {
|
||||
if (@hasDecl(Register, "parseRegName")) {
|
||||
return Register.parseRegName(name);
|
||||
|
||||
@ -30,6 +30,59 @@ pub fn emitMir(emit: *Emit) Error!void {
|
||||
var lowered_relocs = lowered.relocs;
|
||||
for (lowered.insts, 0..) |lowered_inst, lowered_index| {
|
||||
const start_offset: u32 = @intCast(emit.code.items.len);
|
||||
if (lowered_inst.prefix == .directive) {
|
||||
switch (emit.debug_output) {
|
||||
.dwarf => |dwarf| switch (lowered_inst.encoding.mnemonic) {
|
||||
.@".cfi_def_cfa" => try dwarf.genDebugFrame(start_offset, .{ .def_cfa = .{
|
||||
.reg = lowered_inst.ops[0].reg.dwarfNum(),
|
||||
.off = lowered_inst.ops[1].imm.signed,
|
||||
} }),
|
||||
.@".cfi_def_cfa_register" => try dwarf.genDebugFrame(start_offset, .{
|
||||
.def_cfa_register = lowered_inst.ops[0].reg.dwarfNum(),
|
||||
}),
|
||||
.@".cfi_def_cfa_offset" => try dwarf.genDebugFrame(start_offset, .{
|
||||
.def_cfa_offset = lowered_inst.ops[0].imm.signed,
|
||||
}),
|
||||
.@".cfi_adjust_cfa_offset" => try dwarf.genDebugFrame(start_offset, .{
|
||||
.adjust_cfa_offset = lowered_inst.ops[0].imm.signed,
|
||||
}),
|
||||
.@".cfi_offset" => try dwarf.genDebugFrame(start_offset, .{ .offset = .{
|
||||
.reg = lowered_inst.ops[0].reg.dwarfNum(),
|
||||
.off = lowered_inst.ops[1].imm.signed,
|
||||
} }),
|
||||
.@".cfi_val_offset" => try dwarf.genDebugFrame(start_offset, .{ .val_offset = .{
|
||||
.reg = lowered_inst.ops[0].reg.dwarfNum(),
|
||||
.off = lowered_inst.ops[1].imm.signed,
|
||||
} }),
|
||||
.@".cfi_rel_offset" => try dwarf.genDebugFrame(start_offset, .{ .rel_offset = .{
|
||||
.reg = lowered_inst.ops[0].reg.dwarfNum(),
|
||||
.off = lowered_inst.ops[1].imm.signed,
|
||||
} }),
|
||||
.@".cfi_register" => try dwarf.genDebugFrame(start_offset, .{ .register = .{
|
||||
lowered_inst.ops[0].reg.dwarfNum(),
|
||||
lowered_inst.ops[1].reg.dwarfNum(),
|
||||
} }),
|
||||
.@".cfi_restore" => try dwarf.genDebugFrame(start_offset, .{
|
||||
.restore = lowered_inst.ops[0].reg.dwarfNum(),
|
||||
}),
|
||||
.@".cfi_undefined" => try dwarf.genDebugFrame(start_offset, .{
|
||||
.undefined = lowered_inst.ops[0].reg.dwarfNum(),
|
||||
}),
|
||||
.@".cfi_same_value" => try dwarf.genDebugFrame(start_offset, .{
|
||||
.same_value = lowered_inst.ops[0].reg.dwarfNum(),
|
||||
}),
|
||||
.@".cfi_remember_state" => try dwarf.genDebugFrame(start_offset, .remember_state),
|
||||
.@".cfi_restore_state" => try dwarf.genDebugFrame(start_offset, .restore_state),
|
||||
.@".cfi_escape" => try dwarf.genDebugFrame(start_offset, .{
|
||||
.escape = lowered_inst.ops[0].bytes,
|
||||
}),
|
||||
else => unreachable,
|
||||
},
|
||||
.plan9 => {},
|
||||
.none => {},
|
||||
}
|
||||
continue;
|
||||
}
|
||||
try lowered_inst.encode(emit.code.writer(), .{});
|
||||
const end_offset: u32 = @intCast(emit.code.items.len);
|
||||
while (lowered_relocs.len > 0 and
|
||||
|
||||
@ -220,6 +220,21 @@ pub fn format(
|
||||
}
|
||||
|
||||
pub const Mnemonic = enum {
|
||||
// Directives
|
||||
@".cfi_def_cfa",
|
||||
@".cfi_def_cfa_register",
|
||||
@".cfi_def_cfa_offset",
|
||||
@".cfi_adjust_cfa_offset",
|
||||
@".cfi_offset",
|
||||
@".cfi_val_offset",
|
||||
@".cfi_rel_offset",
|
||||
@".cfi_register",
|
||||
@".cfi_restore",
|
||||
@".cfi_undefined",
|
||||
@".cfi_same_value",
|
||||
@".cfi_remember_state",
|
||||
@".cfi_restore_state",
|
||||
@".cfi_escape",
|
||||
// zig fmt: off
|
||||
// General-purpose
|
||||
adc, add, @"and",
|
||||
@ -442,6 +457,7 @@ pub const Op = enum {
|
||||
imm8s, imm16s, imm32s,
|
||||
al, ax, eax, rax,
|
||||
cl,
|
||||
rip, eip, ip,
|
||||
r8, r16, r32, r64,
|
||||
rm8, rm16, rm32, rm64,
|
||||
r32_m8, r32_m16, r64_m16,
|
||||
@ -487,7 +503,12 @@ pub const Op = enum {
|
||||
256 => .ymm,
|
||||
else => unreachable,
|
||||
},
|
||||
.ip => unreachable,
|
||||
.ip => switch (reg) {
|
||||
.rip => .rip,
|
||||
.eip => .eip,
|
||||
.ip => .ip,
|
||||
else => unreachable,
|
||||
},
|
||||
},
|
||||
|
||||
.mem => |mem| switch (mem) {
|
||||
@ -531,13 +552,15 @@ pub const Op = enum {
|
||||
else
|
||||
.imm64,
|
||||
},
|
||||
|
||||
.bytes => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn immBitSize(op: Op) u64 {
|
||||
return switch (op) {
|
||||
.none, .o16, .o32, .o64, .moffs, .m, .sreg => unreachable,
|
||||
.al, .cl, .r8, .rm8, .r32_m8 => unreachable,
|
||||
.al, .cl, .rip, .eip, .ip, .r8, .rm8, .r32_m8 => unreachable,
|
||||
.ax, .r16, .rm16 => unreachable,
|
||||
.eax, .r32, .rm32, .r32_m16 => unreachable,
|
||||
.rax, .r64, .rm64, .r64_m16 => unreachable,
|
||||
@ -560,9 +583,9 @@ pub const Op = enum {
|
||||
.rel8, .rel16, .rel32 => unreachable,
|
||||
.m8, .m16, .m32, .m64, .m80, .m128, .m256 => unreachable,
|
||||
.al, .cl, .r8, .rm8 => 8,
|
||||
.ax, .r16, .rm16 => 16,
|
||||
.eax, .r32, .rm32, .r32_m8, .r32_m16 => 32,
|
||||
.rax, .r64, .rm64, .r64_m16, .mm, .mm_m64 => 64,
|
||||
.ax, .ip, .r16, .rm16 => 16,
|
||||
.eax, .eip, .r32, .rm32, .r32_m8, .r32_m16 => 32,
|
||||
.rax, .rip, .r64, .rm64, .r64_m16, .mm, .mm_m64 => 64,
|
||||
.st => 80,
|
||||
.xmm0, .xmm, .xmm_m8, .xmm_m16, .xmm_m32, .xmm_m64, .xmm_m128 => 128,
|
||||
.ymm, .ymm_m256 => 256,
|
||||
@ -574,7 +597,7 @@ pub const Op = enum {
|
||||
.none, .o16, .o32, .o64, .moffs, .m, .sreg => unreachable,
|
||||
.unity, .imm8, .imm8s, .imm16, .imm16s, .imm32, .imm32s, .imm64 => unreachable,
|
||||
.rel8, .rel16, .rel32 => unreachable,
|
||||
.al, .cl, .r8, .ax, .r16, .eax, .r32, .rax, .r64 => unreachable,
|
||||
.al, .cl, .r8, .ax, .ip, .r16, .eax, .eip, .r32, .rax, .rip, .r64 => unreachable,
|
||||
.st, .mm, .xmm0, .xmm, .ymm => unreachable,
|
||||
.m8, .rm8, .r32_m8, .xmm_m8 => 8,
|
||||
.m16, .rm16, .r32_m16, .r64_m16, .xmm_m16 => 16,
|
||||
@ -602,8 +625,9 @@ pub const Op = enum {
|
||||
pub fn isRegister(op: Op) bool {
|
||||
// zig fmt: off
|
||||
return switch (op) {
|
||||
.cl,
|
||||
.al, .ax, .eax, .rax,
|
||||
.cl,
|
||||
.ip, .eip, .rip,
|
||||
.r8, .r16, .r32, .r64,
|
||||
.rm8, .rm16, .rm32, .rm64,
|
||||
.r32_m8, .r32_m16, .r64_m16,
|
||||
@ -664,6 +688,7 @@ pub const Op = enum {
|
||||
.mm, .mm_m64 => .mmx,
|
||||
.xmm0, .xmm, .xmm_m8, .xmm_m16, .xmm_m32, .xmm_m64, .xmm_m128 => .sse,
|
||||
.ymm, .ymm_m256 => .sse,
|
||||
.rip, .eip, .ip => .ip,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ src_loc: Zcu.LazySrcLoc,
|
||||
result_insts_len: u8 = undefined,
|
||||
result_relocs_len: u8 = undefined,
|
||||
result_insts: [
|
||||
std.mem.max(usize, &.{
|
||||
@max(
|
||||
1, // non-pseudo instructions
|
||||
3, // (ELF only) TLS local dynamic (LD) sequence in PIC mode
|
||||
2, // cmovcc: cmovcc \ cmovcc
|
||||
@ -22,18 +22,18 @@ result_insts: [
|
||||
pseudo_probe_adjust_unrolled_max_insts,
|
||||
pseudo_probe_adjust_setup_insts,
|
||||
pseudo_probe_adjust_loop_insts,
|
||||
abi.Win64.callee_preserved_regs.len, // push_regs/pop_regs
|
||||
abi.SysV.callee_preserved_regs.len, // push_regs/pop_regs
|
||||
})
|
||||
abi.Win64.callee_preserved_regs.len * 2, // push_regs/pop_regs
|
||||
abi.SysV.callee_preserved_regs.len * 2, // push_regs/pop_regs
|
||||
)
|
||||
]Instruction = undefined,
|
||||
result_relocs: [
|
||||
std.mem.max(usize, &.{
|
||||
@max(
|
||||
1, // jmp/jcc/call/mov/lea: jmp/jcc/call/mov/lea
|
||||
2, // jcc: jcc \ jcc
|
||||
2, // test \ jcc \ probe \ sub \ jmp
|
||||
1, // probe \ sub \ jcc
|
||||
3, // (ELF only) TLS local dynamic (LD) sequence in PIC mode
|
||||
})
|
||||
)
|
||||
]Reloc = undefined,
|
||||
|
||||
pub const pseudo_probe_align_insts = 5; // test \ jcc \ probe \ sub \ jmp
|
||||
@ -265,6 +265,50 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
|
||||
.pseudo_push_reg_list => try lower.pushPopRegList(.push, inst),
|
||||
.pseudo_pop_reg_list => try lower.pushPopRegList(.pop, inst),
|
||||
|
||||
.pseudo_cfi_def_cfa_ri_s => try lower.emit(.directive, .@".cfi_def_cfa", &.{
|
||||
.{ .reg = inst.data.ri.r1 },
|
||||
.{ .imm = lower.imm(.ri_s, inst.data.ri.i) },
|
||||
}),
|
||||
.pseudo_cfi_def_cfa_register_r => try lower.emit(.directive, .@".cfi_def_cfa_register", &.{
|
||||
.{ .reg = inst.data.r.r1 },
|
||||
}),
|
||||
.pseudo_cfi_def_cfa_offset_i_s => try lower.emit(.directive, .@".cfi_def_cfa_offset", &.{
|
||||
.{ .imm = lower.imm(.i_s, inst.data.i.i) },
|
||||
}),
|
||||
.pseudo_cfi_adjust_cfa_offset_i_s => try lower.emit(.directive, .@".cfi_adjust_cfa_offset", &.{
|
||||
.{ .imm = lower.imm(.i_s, inst.data.i.i) },
|
||||
}),
|
||||
.pseudo_cfi_offset_ri_s => try lower.emit(.directive, .@".cfi_offset", &.{
|
||||
.{ .reg = inst.data.ri.r1 },
|
||||
.{ .imm = lower.imm(.ri_s, inst.data.ri.i) },
|
||||
}),
|
||||
.pseudo_cfi_val_offset_ri_s => try lower.emit(.directive, .@".cfi_val_offset", &.{
|
||||
.{ .reg = inst.data.ri.r1 },
|
||||
.{ .imm = lower.imm(.ri_s, inst.data.ri.i) },
|
||||
}),
|
||||
.pseudo_cfi_rel_offset_ri_s => try lower.emit(.directive, .@".cfi_rel_offset", &.{
|
||||
.{ .reg = inst.data.ri.r1 },
|
||||
.{ .imm = lower.imm(.ri_s, inst.data.ri.i) },
|
||||
}),
|
||||
.pseudo_cfi_register_rr => try lower.emit(.directive, .@".cfi_register", &.{
|
||||
.{ .reg = inst.data.rr.r1 },
|
||||
.{ .reg = inst.data.rr.r2 },
|
||||
}),
|
||||
.pseudo_cfi_restore_r => try lower.emit(.directive, .@".cfi_restore", &.{
|
||||
.{ .reg = inst.data.r.r1 },
|
||||
}),
|
||||
.pseudo_cfi_undefined_r => try lower.emit(.directive, .@".cfi_undefined", &.{
|
||||
.{ .reg = inst.data.r.r1 },
|
||||
}),
|
||||
.pseudo_cfi_same_value_r => try lower.emit(.directive, .@".cfi_same_value", &.{
|
||||
.{ .reg = inst.data.r.r1 },
|
||||
}),
|
||||
.pseudo_cfi_remember_state_none => try lower.emit(.directive, .@".cfi_remember_state", &.{}),
|
||||
.pseudo_cfi_restore_state_none => try lower.emit(.directive, .@".cfi_restore_state", &.{}),
|
||||
.pseudo_cfi_escape_bytes => try lower.emit(.directive, .@".cfi_escape", &.{
|
||||
.{ .bytes = inst.data.bytes.get(lower.mir) },
|
||||
}),
|
||||
|
||||
.pseudo_dbg_prologue_end_none,
|
||||
.pseudo_dbg_line_line_column,
|
||||
.pseudo_dbg_epilogue_begin_none,
|
||||
@ -280,6 +324,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
|
||||
.pseudo_dbg_local_af,
|
||||
.pseudo_dbg_local_am,
|
||||
.pseudo_dbg_var_args_none,
|
||||
|
||||
.pseudo_dead_none,
|
||||
=> {},
|
||||
else => unreachable,
|
||||
@ -665,12 +710,43 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
|
||||
|
||||
fn pushPopRegList(lower: *Lower, comptime mnemonic: Mnemonic, inst: Mir.Inst) Error!void {
|
||||
const callee_preserved_regs = abi.getCalleePreservedRegs(lower.cc);
|
||||
var it = inst.data.reg_list.iterator(.{ .direction = switch (mnemonic) {
|
||||
.push => .reverse,
|
||||
.pop => .forward,
|
||||
var off: i32 = switch (mnemonic) {
|
||||
.push => 0,
|
||||
.pop => undefined,
|
||||
else => unreachable,
|
||||
} });
|
||||
while (it.next()) |i| try lower.emit(.none, mnemonic, &.{.{ .reg = callee_preserved_regs[i] }});
|
||||
};
|
||||
{
|
||||
var it = inst.data.reg_list.iterator(.{ .direction = switch (mnemonic) {
|
||||
.push => .reverse,
|
||||
.pop => .forward,
|
||||
else => unreachable,
|
||||
} });
|
||||
while (it.next()) |i| {
|
||||
try lower.emit(.none, mnemonic, &.{.{
|
||||
.reg = callee_preserved_regs[i],
|
||||
}});
|
||||
switch (mnemonic) {
|
||||
.push => off -= 8,
|
||||
.pop => {},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (mnemonic) {
|
||||
.push => {
|
||||
var it = inst.data.reg_list.iterator(.{});
|
||||
while (it.next()) |i| {
|
||||
try lower.emit(.directive, .@".cfi_rel_offset", &.{
|
||||
.{ .reg = callee_preserved_regs[i] },
|
||||
.{ .imm = Immediate.s(off) },
|
||||
});
|
||||
off += 8;
|
||||
}
|
||||
assert(off == 0);
|
||||
},
|
||||
.pop => {},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
const page_size: i32 = 1 << 12;
|
||||
|
||||
@ -879,6 +879,7 @@ pub const Inst = struct {
|
||||
/// Probe adjust loop
|
||||
/// Uses `rr` payload.
|
||||
pseudo_probe_adjust_loop_rr,
|
||||
|
||||
/// Push registers
|
||||
/// Uses `reg_list` payload.
|
||||
pseudo_push_reg_list,
|
||||
@ -886,6 +887,47 @@ pub const Inst = struct {
|
||||
/// Uses `reg_list` payload.
|
||||
pseudo_pop_reg_list,
|
||||
|
||||
/// Define cfa rule as offset from register.
|
||||
/// Uses `ri` payload.
|
||||
pseudo_cfi_def_cfa_ri_s,
|
||||
/// Modify cfa rule register.
|
||||
/// Uses `r` payload.
|
||||
pseudo_cfi_def_cfa_register_r,
|
||||
/// Modify cfa rule offset.
|
||||
/// Uses `i` payload.
|
||||
pseudo_cfi_def_cfa_offset_i_s,
|
||||
/// Offset cfa rule offset.
|
||||
/// Uses `i` payload.
|
||||
pseudo_cfi_adjust_cfa_offset_i_s,
|
||||
/// Define register rule as stored at offset from cfa.
|
||||
/// Uses `ri` payload.
|
||||
pseudo_cfi_offset_ri_s,
|
||||
/// Define register rule as offset from cfa.
|
||||
/// Uses `ri` payload.
|
||||
pseudo_cfi_val_offset_ri_s,
|
||||
/// Define register rule as stored at offset from cfa rule register.
|
||||
/// Uses `ri` payload.
|
||||
pseudo_cfi_rel_offset_ri_s,
|
||||
/// Define register rule as register.
|
||||
/// Uses `rr` payload.
|
||||
pseudo_cfi_register_rr,
|
||||
/// Define register rule from initial.
|
||||
/// Uses `r` payload.
|
||||
pseudo_cfi_restore_r,
|
||||
/// Define register rule as undefined.
|
||||
/// Uses `r` payload.
|
||||
pseudo_cfi_undefined_r,
|
||||
/// Define register rule as itself.
|
||||
/// Uses `r` payload.
|
||||
pseudo_cfi_same_value_r,
|
||||
/// Push cfi state.
|
||||
pseudo_cfi_remember_state_none,
|
||||
/// Pop cfi state.
|
||||
pseudo_cfi_restore_state_none,
|
||||
/// Raw cfi bytes.
|
||||
/// Uses `bytes` payload.
|
||||
pseudo_cfi_escape_bytes,
|
||||
|
||||
/// End of prologue
|
||||
pseudo_dbg_prologue_end_none,
|
||||
/// Update debug line
|
||||
@ -1028,8 +1070,13 @@ pub const Inst = struct {
|
||||
fixes: Fixes = ._,
|
||||
payload: u32,
|
||||
},
|
||||
ix: struct {
|
||||
bytes: struct {
|
||||
payload: u32,
|
||||
len: u32,
|
||||
|
||||
pub fn get(bytes: @This(), mir: Mir) []const u8 {
|
||||
return std.mem.sliceAsBytes(mir.extra[bytes.payload..])[0..bytes.len];
|
||||
}
|
||||
},
|
||||
a: struct {
|
||||
air_inst: Air.Inst.Index,
|
||||
|
||||
@ -371,7 +371,7 @@ pub const Register = enum(u7) {
|
||||
.x87 => 33 + @as(u6, reg.enc()),
|
||||
.mmx => 41 + @as(u6, reg.enc()),
|
||||
.segment => 50 + @as(u6, reg.enc()),
|
||||
.ip => unreachable,
|
||||
.ip => 16,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -25,6 +25,7 @@ pub const Instruction = struct {
|
||||
repz,
|
||||
repne,
|
||||
repnz,
|
||||
directive,
|
||||
};
|
||||
|
||||
pub const Immediate = union(enum) {
|
||||
@ -180,6 +181,7 @@ pub const Instruction = struct {
|
||||
reg: Register,
|
||||
mem: Memory,
|
||||
imm: Immediate,
|
||||
bytes: []const u8,
|
||||
|
||||
/// Returns the bitsize of the operand.
|
||||
pub fn bitSize(op: Operand) u64 {
|
||||
@ -188,6 +190,7 @@ pub const Instruction = struct {
|
||||
.reg => |reg| reg.bitSize(),
|
||||
.mem => |mem| mem.bitSize(),
|
||||
.imm => unreachable,
|
||||
.bytes => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
@ -199,6 +202,7 @@ pub const Instruction = struct {
|
||||
.reg => |reg| reg.class() == .segment,
|
||||
.mem => |mem| mem.isSegmentRegister(),
|
||||
.imm => unreachable,
|
||||
.bytes => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
@ -207,6 +211,7 @@ pub const Instruction = struct {
|
||||
.none, .imm => false,
|
||||
.reg => |reg| reg.isExtended(),
|
||||
.mem => |mem| mem.base().isExtended(),
|
||||
.bytes => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
@ -214,6 +219,7 @@ pub const Instruction = struct {
|
||||
return switch (op) {
|
||||
.none, .reg, .imm => false,
|
||||
.mem => |mem| if (mem.scaleIndex()) |si| si.index.isExtended() else false,
|
||||
.bytes => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
@ -299,6 +305,7 @@ pub const Instruction = struct {
|
||||
if (imms < 0) try writer.writeByte('-');
|
||||
try writer.print("0x{x}", .{@abs(imms)});
|
||||
} else try writer.print("0x{x}", .{imm.asUnsigned(enc_op.immBitSize())}),
|
||||
.bytes => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,20 +315,39 @@ pub const Instruction = struct {
|
||||
};
|
||||
|
||||
pub fn new(prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) !Instruction {
|
||||
const encoding = (try Encoding.findByMnemonic(prefix, mnemonic, ops)) orelse {
|
||||
log.err("no encoding found for: {s} {s} {s} {s} {s} {s}", .{
|
||||
@tagName(prefix),
|
||||
@tagName(mnemonic),
|
||||
@tagName(if (ops.len > 0) Encoding.Op.fromOperand(ops[0]) else .none),
|
||||
@tagName(if (ops.len > 1) Encoding.Op.fromOperand(ops[1]) else .none),
|
||||
@tagName(if (ops.len > 2) Encoding.Op.fromOperand(ops[2]) else .none),
|
||||
@tagName(if (ops.len > 3) Encoding.Op.fromOperand(ops[3]) else .none),
|
||||
});
|
||||
return error.InvalidInstruction;
|
||||
const encoding: Encoding = switch (prefix) {
|
||||
else => (try Encoding.findByMnemonic(prefix, mnemonic, ops)) orelse {
|
||||
log.err("no encoding found for: {s} {s} {s} {s} {s} {s}", .{
|
||||
@tagName(prefix),
|
||||
@tagName(mnemonic),
|
||||
@tagName(if (ops.len > 0) Encoding.Op.fromOperand(ops[0]) else .none),
|
||||
@tagName(if (ops.len > 1) Encoding.Op.fromOperand(ops[1]) else .none),
|
||||
@tagName(if (ops.len > 2) Encoding.Op.fromOperand(ops[2]) else .none),
|
||||
@tagName(if (ops.len > 3) Encoding.Op.fromOperand(ops[3]) else .none),
|
||||
});
|
||||
return error.InvalidInstruction;
|
||||
},
|
||||
.directive => .{
|
||||
.mnemonic = mnemonic,
|
||||
.data = .{
|
||||
.op_en = .zo,
|
||||
.ops = .{
|
||||
if (ops.len > 0) Encoding.Op.fromOperand(ops[0]) else .none,
|
||||
if (ops.len > 1) Encoding.Op.fromOperand(ops[1]) else .none,
|
||||
if (ops.len > 2) Encoding.Op.fromOperand(ops[2]) else .none,
|
||||
if (ops.len > 3) Encoding.Op.fromOperand(ops[3]) else .none,
|
||||
},
|
||||
.opc_len = 0,
|
||||
.opc = undefined,
|
||||
.modrm_ext = 0,
|
||||
.mode = .none,
|
||||
.feature = .none,
|
||||
},
|
||||
},
|
||||
};
|
||||
log.debug("selected encoding: {}", .{encoding});
|
||||
|
||||
var inst = Instruction{
|
||||
var inst: Instruction = .{
|
||||
.prefix = prefix,
|
||||
.encoding = encoding,
|
||||
.ops = [1]Operand{.none} ** 4,
|
||||
@ -338,7 +364,10 @@ pub const Instruction = struct {
|
||||
) @TypeOf(writer).Error!void {
|
||||
_ = unused_format_string;
|
||||
_ = options;
|
||||
if (inst.prefix != .none) try writer.print("{s} ", .{@tagName(inst.prefix)});
|
||||
switch (inst.prefix) {
|
||||
.none, .directive => {},
|
||||
else => try writer.print("{s} ", .{@tagName(inst.prefix)}),
|
||||
}
|
||||
try writer.print("{s}", .{@tagName(inst.encoding.mnemonic)});
|
||||
for (inst.ops, inst.encoding.data.ops, 0..) |op, enc, i| {
|
||||
if (op == .none) break;
|
||||
@ -349,6 +378,7 @@ pub const Instruction = struct {
|
||||
}
|
||||
|
||||
pub fn encode(inst: Instruction, writer: anytype, comptime opts: Options) !void {
|
||||
assert(inst.prefix != .directive);
|
||||
const encoder = Encoder(@TypeOf(writer), opts){ .writer = writer };
|
||||
const enc = inst.encoding;
|
||||
const data = enc.data;
|
||||
@ -435,6 +465,7 @@ pub const Instruction = struct {
|
||||
.lock => legacy.prefix_f0 = true,
|
||||
.repne, .repnz => legacy.prefix_f2 = true,
|
||||
.rep, .repe, .repz => legacy.prefix_f3 = true,
|
||||
.directive => unreachable,
|
||||
}
|
||||
|
||||
switch (data.mode) {
|
||||
|
||||
@ -9412,7 +9412,7 @@ pub const FuncGen = struct {
|
||||
// repeating byte pattern, for example, `@as(u64, 0)` has a
|
||||
// repeating byte pattern of 0 bytes. In such case, the memset
|
||||
// intrinsic can be used.
|
||||
if (try elem_val.hasRepeatedByteRepr(elem_ty, pt)) |byte_val| {
|
||||
if (try elem_val.hasRepeatedByteRepr(pt)) |byte_val| {
|
||||
const fill_byte = try o.builder.intValue(.i8, byte_val);
|
||||
const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty);
|
||||
if (intrinsic_len0_traps) {
|
||||
|
||||
@ -589,7 +589,8 @@ pub const File = struct {
|
||||
fs.File.WriteFileError ||
|
||||
fs.File.OpenError ||
|
||||
std.process.Child.SpawnError ||
|
||||
fs.Dir.CopyFileError;
|
||||
fs.Dir.CopyFileError ||
|
||||
FlushDebugInfoError;
|
||||
|
||||
/// Commit pending changes and write headers. Takes into account final output mode
|
||||
/// and `use_lld`, not only `effectiveOutputMode`.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
133
src/link/Elf.zig
133
src/link/Elf.zig
@ -569,9 +569,7 @@ pub fn growAllocSection(self: *Elf, shdr_index: u32, needed_size: u64) !void {
|
||||
|
||||
if (shdr.sh_type != elf.SHT_NOBITS) {
|
||||
const allocated_size = self.allocatedSize(shdr.sh_offset);
|
||||
if (shdr.sh_offset + allocated_size == std.math.maxInt(u64)) {
|
||||
try self.base.file.?.setEndPos(shdr.sh_offset + needed_size);
|
||||
} else if (needed_size > allocated_size) {
|
||||
if (needed_size > allocated_size) {
|
||||
const existing_size = shdr.sh_size;
|
||||
shdr.sh_size = 0;
|
||||
// Must move the entire section.
|
||||
@ -590,6 +588,8 @@ pub fn growAllocSection(self: *Elf, shdr_index: u32, needed_size: u64) !void {
|
||||
|
||||
shdr.sh_offset = new_offset;
|
||||
if (maybe_phdr) |phdr| phdr.p_offset = new_offset;
|
||||
} else if (shdr.sh_offset + allocated_size == std.math.maxInt(u64)) {
|
||||
try self.base.file.?.setEndPos(shdr.sh_offset + needed_size);
|
||||
}
|
||||
if (maybe_phdr) |phdr| phdr.p_filesz = needed_size;
|
||||
}
|
||||
@ -621,9 +621,7 @@ pub fn growNonAllocSection(
|
||||
assert(shdr.sh_flags & elf.SHF_ALLOC == 0);
|
||||
|
||||
const allocated_size = self.allocatedSize(shdr.sh_offset);
|
||||
if (shdr.sh_offset + allocated_size == std.math.maxInt(u64)) {
|
||||
try self.base.file.?.setEndPos(shdr.sh_offset + needed_size);
|
||||
} else if (needed_size > allocated_size) {
|
||||
if (needed_size > allocated_size) {
|
||||
const existing_size = shdr.sh_size;
|
||||
shdr.sh_size = 0;
|
||||
// Move all the symbols to a new file location.
|
||||
@ -646,6 +644,8 @@ pub fn growNonAllocSection(
|
||||
}
|
||||
|
||||
shdr.sh_offset = new_offset;
|
||||
} else if (shdr.sh_offset + allocated_size == std.math.maxInt(u64)) {
|
||||
try self.base.file.?.setEndPos(shdr.sh_offset + needed_size);
|
||||
}
|
||||
shdr.sh_size = needed_size;
|
||||
|
||||
@ -699,7 +699,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
|
||||
const sub_prog_node = prog_node.start("ELF Flush", 0);
|
||||
defer sub_prog_node.end();
|
||||
|
||||
const target = comp.root_mod.resolved_target.result;
|
||||
const target = self.getTarget();
|
||||
const link_mode = comp.config.link_mode;
|
||||
const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type.
|
||||
const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
|
||||
@ -1053,7 +1053,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
|
||||
defer arena_allocator.deinit();
|
||||
const arena = arena_allocator.allocator();
|
||||
|
||||
const target = self.base.comp.root_mod.resolved_target.result;
|
||||
const target = self.getTarget();
|
||||
const link_mode = self.base.comp.config.link_mode;
|
||||
const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type.
|
||||
const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
|
||||
@ -1498,15 +1498,13 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
|
||||
}
|
||||
|
||||
pub fn validateEFlags(self: *Elf, file_index: File.Index, e_flags: elf.Elf64_Word) !void {
|
||||
const target = self.base.comp.root_mod.resolved_target.result;
|
||||
|
||||
if (self.first_eflags == null) {
|
||||
self.first_eflags = e_flags;
|
||||
return; // there isn't anything to conflict with yet
|
||||
}
|
||||
const self_eflags: *elf.Elf64_Word = &self.first_eflags.?;
|
||||
|
||||
switch (target.cpu.arch) {
|
||||
switch (self.getTarget().cpu.arch) {
|
||||
.riscv64 => {
|
||||
if (e_flags != self_eflags.*) {
|
||||
const riscv_eflags: riscv.RiscvEflags = @bitCast(e_flags);
|
||||
@ -1549,7 +1547,7 @@ fn accessLibPath(
|
||||
link_mode: ?std.builtin.LinkMode,
|
||||
) !bool {
|
||||
const sep = fs.path.sep_str;
|
||||
const target = self.base.comp.root_mod.resolved_target.result;
|
||||
const target = self.getTarget();
|
||||
test_path.clearRetainingCapacity();
|
||||
const prefix = if (link_mode != null) "lib" else "";
|
||||
const suffix = if (link_mode) |mode| switch (mode) {
|
||||
@ -1779,7 +1777,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
|
||||
const is_exe_or_dyn_lib = is_dyn_lib or output_mode == .Exe;
|
||||
const have_dynamic_linker = comp.config.link_libc and
|
||||
link_mode == .dynamic and is_exe_or_dyn_lib;
|
||||
const target = comp.root_mod.resolved_target.result;
|
||||
const target = self.getTarget();
|
||||
const compiler_rt_path: ?[]const u8 = blk: {
|
||||
if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
|
||||
if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
|
||||
@ -2353,8 +2351,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
|
||||
|
||||
pub fn writeShdrTable(self: *Elf) !void {
|
||||
const gpa = self.base.comp.gpa;
|
||||
const target = self.base.comp.root_mod.resolved_target.result;
|
||||
const target_endian = target.cpu.arch.endian();
|
||||
const target_endian = self.getTarget().cpu.arch.endian();
|
||||
const foreign_endian = target_endian != builtin.cpu.arch.endian();
|
||||
const shsize: u64 = switch (self.ptr_width) {
|
||||
.p32 => @sizeOf(elf.Elf32_Shdr),
|
||||
@ -2410,8 +2407,7 @@ pub fn writeShdrTable(self: *Elf) !void {
|
||||
|
||||
fn writePhdrTable(self: *Elf) !void {
|
||||
const gpa = self.base.comp.gpa;
|
||||
const target = self.base.comp.root_mod.resolved_target.result;
|
||||
const target_endian = target.cpu.arch.endian();
|
||||
const target_endian = self.getTarget().cpu.arch.endian();
|
||||
const foreign_endian = target_endian != builtin.cpu.arch.endian();
|
||||
const phdr_table = &self.phdrs.items[self.phdr_table_index.?];
|
||||
|
||||
@ -2464,7 +2460,7 @@ pub fn writeElfHeader(self: *Elf) !void {
|
||||
};
|
||||
index += 1;
|
||||
|
||||
const target = comp.root_mod.resolved_target.result;
|
||||
const target = self.getTarget();
|
||||
const endian = target.cpu.arch.endian();
|
||||
hdr_buf[index] = switch (endian) {
|
||||
.little => elf.ELFDATA2LSB,
|
||||
@ -2772,21 +2768,25 @@ fn initOutputSections(self: *Elf) !void {
|
||||
|
||||
fn initSyntheticSections(self: *Elf) !void {
|
||||
const comp = self.base.comp;
|
||||
const target = comp.root_mod.resolved_target.result;
|
||||
const target = self.getTarget();
|
||||
const ptr_size = self.ptrWidthBytes();
|
||||
|
||||
const needs_eh_frame = for (self.objects.items) |index| {
|
||||
if (self.file(index).?.object.cies.items.len > 0) break true;
|
||||
} else false;
|
||||
if (needs_eh_frame) {
|
||||
self.eh_frame_section_index = try self.addSection(.{
|
||||
.name = try self.insertShString(".eh_frame"),
|
||||
.type = elf.SHT_PROGBITS,
|
||||
.flags = elf.SHF_ALLOC,
|
||||
.addralign = ptr_size,
|
||||
.offset = std.math.maxInt(u64),
|
||||
});
|
||||
|
||||
if (self.eh_frame_section_index == null) {
|
||||
self.eh_frame_section_index = try self.addSection(.{
|
||||
.name = try self.insertShString(".eh_frame"),
|
||||
.type = if (target.cpu.arch == .x86_64)
|
||||
elf.SHT_X86_64_UNWIND
|
||||
else
|
||||
elf.SHT_PROGBITS,
|
||||
.flags = elf.SHF_ALLOC,
|
||||
.addralign = ptr_size,
|
||||
.offset = std.math.maxInt(u64),
|
||||
});
|
||||
}
|
||||
if (comp.link_eh_frame_hdr) {
|
||||
self.eh_frame_hdr_section_index = try self.addSection(.{
|
||||
.name = try self.insertShString(".eh_frame_hdr"),
|
||||
@ -3446,7 +3446,6 @@ fn resetShdrIndexes(self: *Elf, backlinks: []const u32) void {
|
||||
}
|
||||
|
||||
fn updateSectionSizes(self: *Elf) !void {
|
||||
const target = self.base.comp.root_mod.resolved_target.result;
|
||||
const slice = self.sections.slice();
|
||||
for (slice.items(.shdr), slice.items(.atom_list)) |*shdr, atom_list| {
|
||||
if (atom_list.items.len == 0) continue;
|
||||
@ -3474,7 +3473,11 @@ fn updateSectionSizes(self: *Elf) !void {
|
||||
|
||||
const shdrs = slice.items(.shdr);
|
||||
if (self.eh_frame_section_index) |index| {
|
||||
shdrs[index].sh_size = try eh_frame.calcEhFrameSize(self);
|
||||
shdrs[index].sh_size = existing_size: {
|
||||
const zo = self.zigObjectPtr() orelse break :existing_size 0;
|
||||
const sym = zo.symbol(zo.eh_frame_index orelse break :existing_size 0);
|
||||
break :existing_size sym.atom(self).?.size;
|
||||
} + try eh_frame.calcEhFrameSize(self);
|
||||
}
|
||||
|
||||
if (self.eh_frame_hdr_section_index) |index| {
|
||||
@ -3517,7 +3520,7 @@ fn updateSectionSizes(self: *Elf) !void {
|
||||
}
|
||||
|
||||
if (self.interp_section_index) |index| {
|
||||
shdrs[index].sh_size = target.dynamic_linker.get().?.len + 1;
|
||||
shdrs[index].sh_size = self.getTarget().dynamic_linker.get().?.len + 1;
|
||||
}
|
||||
|
||||
if (self.hash_section_index) |index| {
|
||||
@ -3759,10 +3762,10 @@ pub fn allocateAllocSections(self: *Elf) !void {
|
||||
}
|
||||
|
||||
const first = slice.items(.shdr)[cover.items[0]];
|
||||
var off = try self.findFreeSpace(filesz, @"align");
|
||||
var new_offset = try self.findFreeSpace(filesz, @"align");
|
||||
const phndx = try self.addPhdr(.{
|
||||
.type = elf.PT_LOAD,
|
||||
.offset = off,
|
||||
.offset = new_offset,
|
||||
.addr = first.sh_addr,
|
||||
.memsz = memsz,
|
||||
.filesz = filesz,
|
||||
@ -3777,9 +3780,28 @@ pub fn allocateAllocSections(self: *Elf) !void {
|
||||
shdr.sh_offset = 0;
|
||||
continue;
|
||||
}
|
||||
off = alignment.@"align"(shndx, shdr.sh_addralign, off);
|
||||
shdr.sh_offset = off;
|
||||
off += shdr.sh_size;
|
||||
new_offset = alignment.@"align"(shndx, shdr.sh_addralign, new_offset);
|
||||
|
||||
if (shndx == self.eh_frame_section_index) eh_frame: {
|
||||
const zo = self.zigObjectPtr() orelse break :eh_frame;
|
||||
const sym = zo.symbol(zo.eh_frame_index orelse break :eh_frame);
|
||||
const existing_size = sym.atom(self).?.size;
|
||||
log.debug("moving {s} from 0x{x} to 0x{x}", .{
|
||||
self.getShString(shdr.sh_name),
|
||||
shdr.sh_offset,
|
||||
new_offset,
|
||||
});
|
||||
const amt = try self.base.file.?.copyRangeAll(
|
||||
shdr.sh_offset,
|
||||
self.base.file.?,
|
||||
new_offset,
|
||||
existing_size,
|
||||
);
|
||||
if (amt != existing_size) return error.InputOutput;
|
||||
}
|
||||
|
||||
shdr.sh_offset = new_offset;
|
||||
new_offset += shdr.sh_size;
|
||||
}
|
||||
|
||||
addr = mem.alignForward(u64, addr, self.page_size);
|
||||
@ -3910,9 +3932,9 @@ fn writeAtoms(self: *Elf) !void {
|
||||
log.debug("writing atoms in '{s}' section", .{self.getShString(shdr.sh_name)});
|
||||
|
||||
// TODO really, really handle debug section separately
|
||||
const base_offset = if (self.isDebugSection(@intCast(shndx))) blk: {
|
||||
const base_offset = if (self.isDebugSection(@intCast(shndx))) base_offset: {
|
||||
const zo = self.zigObjectPtr().?;
|
||||
break :blk for ([_]Symbol.Index{
|
||||
for ([_]Symbol.Index{
|
||||
zo.debug_info_index.?,
|
||||
zo.debug_abbrev_index.?,
|
||||
zo.debug_aranges_index.?,
|
||||
@ -3924,8 +3946,13 @@ fn writeAtoms(self: *Elf) !void {
|
||||
}) |sym_index| {
|
||||
const sym = zo.symbol(sym_index);
|
||||
const atom_ptr = sym.atom(self).?;
|
||||
if (atom_ptr.output_section_index == shndx) break atom_ptr.size;
|
||||
} else 0;
|
||||
if (atom_ptr.output_section_index == shndx) break :base_offset atom_ptr.size;
|
||||
}
|
||||
break :base_offset 0;
|
||||
} else if (@as(u32, @intCast(shndx)) == self.eh_frame_section_index) base_offset: {
|
||||
const zo = self.zigObjectPtr() orelse break :base_offset 0;
|
||||
const sym = zo.symbol(zo.eh_frame_index orelse break :base_offset 0);
|
||||
break :base_offset sym.atom(self).?.size;
|
||||
} else 0;
|
||||
const sh_offset = shdr.sh_offset + base_offset;
|
||||
const sh_size = math.cast(usize, shdr.sh_size - base_offset) orelse return error.Overflow;
|
||||
@ -4082,12 +4109,11 @@ pub fn updateSymtabSize(self: *Elf) !void {
|
||||
|
||||
fn writeSyntheticSections(self: *Elf) !void {
|
||||
const gpa = self.base.comp.gpa;
|
||||
const target = self.getTarget();
|
||||
const slice = self.sections.slice();
|
||||
|
||||
if (self.interp_section_index) |shndx| {
|
||||
var buffer: [256]u8 = undefined;
|
||||
const interp = target.dynamic_linker.get().?;
|
||||
const interp = self.getTarget().dynamic_linker.get().?;
|
||||
@memcpy(buffer[0..interp.len], interp);
|
||||
buffer[interp.len] = 0;
|
||||
const contents = buffer[0 .. interp.len + 1];
|
||||
@ -4144,12 +4170,18 @@ fn writeSyntheticSections(self: *Elf) !void {
|
||||
}
|
||||
|
||||
if (self.eh_frame_section_index) |shndx| {
|
||||
const existing_size = existing_size: {
|
||||
const zo = self.zigObjectPtr() orelse break :existing_size 0;
|
||||
const sym = zo.symbol(zo.eh_frame_index orelse break :existing_size 0);
|
||||
break :existing_size sym.atom(self).?.size;
|
||||
};
|
||||
const shdr = slice.items(.shdr)[shndx];
|
||||
const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
|
||||
var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
|
||||
var buffer = try std.ArrayList(u8).initCapacity(gpa, @intCast(sh_size - existing_size));
|
||||
defer buffer.deinit();
|
||||
try eh_frame.writeEhFrame(self, buffer.writer());
|
||||
try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
|
||||
assert(buffer.items.len == sh_size - existing_size);
|
||||
try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset + existing_size);
|
||||
}
|
||||
|
||||
if (self.eh_frame_hdr_section_index) |shndx| {
|
||||
@ -4222,7 +4254,6 @@ pub fn writeShStrtab(self: *Elf) !void {
|
||||
|
||||
pub fn writeSymtab(self: *Elf) !void {
|
||||
const gpa = self.base.comp.gpa;
|
||||
const target = self.getTarget();
|
||||
const slice = self.sections.slice();
|
||||
const symtab_shdr = slice.items(.shdr)[self.symtab_section_index.?];
|
||||
const strtab_shdr = slice.items(.shdr)[self.strtab_section_index.?];
|
||||
@ -4292,7 +4323,7 @@ pub fn writeSymtab(self: *Elf) !void {
|
||||
self.plt_got.writeSymtab(self);
|
||||
}
|
||||
|
||||
const foreign_endian = target.cpu.arch.endian() != builtin.cpu.arch.endian();
|
||||
const foreign_endian = self.getTarget().cpu.arch.endian() != builtin.cpu.arch.endian();
|
||||
switch (self.ptr_width) {
|
||||
.p32 => {
|
||||
const buf = try gpa.alloc(elf.Elf32_Sym, self.symtab.items.len);
|
||||
@ -4630,10 +4661,8 @@ pub fn isZigSection(self: Elf, shndx: u32) bool {
|
||||
self.zig_data_rel_ro_section_index,
|
||||
self.zig_data_section_index,
|
||||
self.zig_bss_section_index,
|
||||
}) |maybe_index| {
|
||||
if (maybe_index) |index| {
|
||||
if (index == shndx) return true;
|
||||
}
|
||||
}) |index| {
|
||||
if (index == shndx) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -4648,10 +4677,8 @@ pub fn isDebugSection(self: Elf, shndx: u32) bool {
|
||||
self.debug_line_str_section_index,
|
||||
self.debug_loclists_section_index,
|
||||
self.debug_rnglists_section_index,
|
||||
}) |maybe_index| {
|
||||
if (maybe_index) |index| {
|
||||
if (index == shndx) return true;
|
||||
}
|
||||
}) |index| {
|
||||
if (index == shndx) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -49,6 +49,7 @@ debug_line_section_dirty: bool = false,
|
||||
debug_line_str_section_dirty: bool = false,
|
||||
debug_loclists_section_dirty: bool = false,
|
||||
debug_rnglists_section_dirty: bool = false,
|
||||
eh_frame_section_dirty: bool = false,
|
||||
|
||||
debug_info_index: ?Symbol.Index = null,
|
||||
debug_abbrev_index: ?Symbol.Index = null,
|
||||
@ -58,6 +59,7 @@ debug_line_index: ?Symbol.Index = null,
|
||||
debug_line_str_index: ?Symbol.Index = null,
|
||||
debug_loclists_index: ?Symbol.Index = null,
|
||||
debug_rnglists_index: ?Symbol.Index = null,
|
||||
eh_frame_index: ?Symbol.Index = null,
|
||||
|
||||
pub const global_symbol_bit: u32 = 0x80000000;
|
||||
pub const symbol_mask: u32 = 0x7fffffff;
|
||||
@ -72,8 +74,6 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
|
||||
const comp = elf_file.base.comp;
|
||||
const gpa = comp.gpa;
|
||||
const ptr_size = elf_file.ptrWidthBytes();
|
||||
const target = elf_file.getTarget();
|
||||
const ptr_bit_width = target.ptrBitWidth();
|
||||
|
||||
try self.atoms.append(gpa, .{ .extra_index = try self.addAtomExtra(gpa, .{}) }); // null input section
|
||||
try self.relocs.append(gpa, .{}); // null relocs section
|
||||
@ -113,7 +113,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
|
||||
.type = elf.PT_LOAD,
|
||||
.offset = off,
|
||||
.filesz = filesz,
|
||||
.addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000,
|
||||
.addr = if (ptr_size >= 4) 0x4000000 else 0x4000,
|
||||
.memsz = filesz,
|
||||
.@"align" = elf_file.page_size,
|
||||
.flags = elf.PF_X | elf.PF_R | elf.PF_W,
|
||||
@ -128,7 +128,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
|
||||
.type = elf.PT_LOAD,
|
||||
.offset = off,
|
||||
.filesz = filesz,
|
||||
.addr = if (ptr_bit_width >= 32) 0xc000000 else 0xa000,
|
||||
.addr = if (ptr_size >= 4) 0xc000000 else 0xa000,
|
||||
.memsz = filesz,
|
||||
.@"align" = alignment,
|
||||
.flags = elf.PF_R | elf.PF_W,
|
||||
@ -143,7 +143,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
|
||||
.type = elf.PT_LOAD,
|
||||
.offset = off,
|
||||
.filesz = filesz,
|
||||
.addr = if (ptr_bit_width >= 32) 0x10000000 else 0xc000,
|
||||
.addr = if (ptr_size >= 4) 0x10000000 else 0xc000,
|
||||
.memsz = filesz,
|
||||
.@"align" = alignment,
|
||||
.flags = elf.PF_R | elf.PF_W,
|
||||
@ -154,7 +154,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
|
||||
const alignment = elf_file.page_size;
|
||||
elf_file.phdr_zig_load_zerofill_index = try elf_file.addPhdr(.{
|
||||
.type = elf.PT_LOAD,
|
||||
.addr = if (ptr_bit_width >= 32) 0x14000000 else 0xf000,
|
||||
.addr = if (ptr_size >= 4) 0x14000000 else 0xf000,
|
||||
.memsz = 1024,
|
||||
.@"align" = alignment,
|
||||
.flags = elf.PF_R | elf.PF_W,
|
||||
@ -354,6 +354,20 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
|
||||
self.debug_rnglists_index = try addSectionSymbol(self, gpa, ".debug_rnglists", .@"1", elf_file.debug_rnglists_section_index.?);
|
||||
}
|
||||
|
||||
if (elf_file.eh_frame_section_index == null) {
|
||||
elf_file.eh_frame_section_index = try elf_file.addSection(.{
|
||||
.name = try elf_file.insertShString(".eh_frame"),
|
||||
.type = if (elf_file.getTarget().cpu.arch == .x86_64)
|
||||
elf.SHT_X86_64_UNWIND
|
||||
else
|
||||
elf.SHT_PROGBITS,
|
||||
.flags = elf.SHF_ALLOC,
|
||||
.addralign = ptr_size,
|
||||
});
|
||||
self.eh_frame_section_dirty = true;
|
||||
self.eh_frame_index = try addSectionSymbol(self, gpa, ".eh_frame", Atom.Alignment.fromNonzeroByteUnits(ptr_size), elf_file.eh_frame_section_index.?);
|
||||
}
|
||||
|
||||
try dwarf.initMetadata();
|
||||
self.dwarf = dwarf;
|
||||
},
|
||||
@ -460,6 +474,7 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !voi
|
||||
self.debug_line_str_index.?,
|
||||
self.debug_loclists_index.?,
|
||||
self.debug_rnglists_index.?,
|
||||
self.eh_frame_index.?,
|
||||
}, [_]*Dwarf.Section{
|
||||
&dwarf.debug_info.section,
|
||||
&dwarf.debug_abbrev.section,
|
||||
@ -469,7 +484,18 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !voi
|
||||
&dwarf.debug_line_str.section,
|
||||
&dwarf.debug_loclists.section,
|
||||
&dwarf.debug_rnglists.section,
|
||||
}) |sym_index, sect| {
|
||||
&dwarf.debug_frame.section,
|
||||
}, [_]Dwarf.Section.Index{
|
||||
.debug_info,
|
||||
.debug_abbrev,
|
||||
.debug_str,
|
||||
.debug_aranges,
|
||||
.debug_line,
|
||||
.debug_line_str,
|
||||
.debug_loclists,
|
||||
.debug_rnglists,
|
||||
.debug_frame,
|
||||
}) |sym_index, sect, sect_index| {
|
||||
const sym = self.symbol(sym_index);
|
||||
const atom_ptr = self.atom(sym.ref.index).?;
|
||||
if (!atom_ptr.alive) continue;
|
||||
@ -509,6 +535,8 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !voi
|
||||
for (unit.cross_section_relocs.items) |reloc| {
|
||||
const target_sym_index = switch (reloc.target_sec) {
|
||||
.debug_abbrev => self.debug_abbrev_index.?,
|
||||
.debug_aranges => self.debug_aranges_index.?,
|
||||
.debug_frame => self.eh_frame_index.?,
|
||||
.debug_info => self.debug_info_index.?,
|
||||
.debug_line => self.debug_line_index.?,
|
||||
.debug_line_str => self.debug_line_str_index.?,
|
||||
@ -547,7 +575,10 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !voi
|
||||
entry.external_relocs.items.len);
|
||||
for (entry.cross_entry_relocs.items) |reloc| {
|
||||
const r_offset = entry_off + reloc.source_off;
|
||||
const r_addend: i64 = @intCast(unit.off + reloc.target_off + unit.header_len + unit.getEntry(reloc.target_entry).assertNonEmpty(unit, sect, dwarf).off);
|
||||
const r_addend: i64 = @intCast(unit.off + reloc.target_off + (if (reloc.target_entry.unwrap()) |target_entry|
|
||||
unit.header_len + unit.getEntry(target_entry).assertNonEmpty(unit, sect, dwarf).off
|
||||
else
|
||||
0));
|
||||
const r_type = relocation.dwarf.crossSectionRelocType(dwarf.format, cpu_arch);
|
||||
log.debug(" {s} <- r_off={x}, r_add={x}, r_type={}", .{
|
||||
self.symbol(sym_index).name(elf_file),
|
||||
@ -584,6 +615,8 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !voi
|
||||
for (entry.cross_section_relocs.items) |reloc| {
|
||||
const target_sym_index = switch (reloc.target_sec) {
|
||||
.debug_abbrev => self.debug_abbrev_index.?,
|
||||
.debug_aranges => self.debug_aranges_index.?,
|
||||
.debug_frame => self.eh_frame_index.?,
|
||||
.debug_info => self.debug_info_index.?,
|
||||
.debug_line => self.debug_line_index.?,
|
||||
.debug_line_str => self.debug_line_str_index.?,
|
||||
@ -617,7 +650,7 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !voi
|
||||
const target_sym = self.symbol(reloc.target_sym);
|
||||
const r_offset = entry_off + reloc.source_off;
|
||||
const r_addend: i64 = @intCast(reloc.target_off);
|
||||
const r_type = relocation.dwarf.externalRelocType(target_sym.*, dwarf.address_size, cpu_arch);
|
||||
const r_type = relocation.dwarf.externalRelocType(target_sym.*, sect_index, dwarf.address_size, cpu_arch);
|
||||
log.debug(" {s} <- r_off={x}, r_add={x}, r_type={}", .{
|
||||
target_sym.name(elf_file),
|
||||
r_offset,
|
||||
|
||||
@ -289,8 +289,6 @@ fn claimUnresolved(elf_file: *Elf) void {
|
||||
}
|
||||
|
||||
fn initSections(elf_file: *Elf) !void {
|
||||
const ptr_size = elf_file.ptrWidthBytes();
|
||||
|
||||
for (elf_file.objects.items) |index| {
|
||||
const object = elf_file.file(index).?.object;
|
||||
try object.initOutputSections(elf_file);
|
||||
@ -306,13 +304,18 @@ fn initSections(elf_file: *Elf) !void {
|
||||
if (elf_file.file(index).?.object.cies.items.len > 0) break true;
|
||||
} else false;
|
||||
if (needs_eh_frame) {
|
||||
elf_file.eh_frame_section_index = try elf_file.addSection(.{
|
||||
.name = try elf_file.insertShString(".eh_frame"),
|
||||
.type = elf.SHT_PROGBITS,
|
||||
.flags = elf.SHF_ALLOC,
|
||||
.addralign = ptr_size,
|
||||
.offset = std.math.maxInt(u64),
|
||||
});
|
||||
if (elf_file.eh_frame_section_index == null) {
|
||||
elf_file.eh_frame_section_index = try elf_file.addSection(.{
|
||||
.name = try elf_file.insertShString(".eh_frame"),
|
||||
.type = if (elf_file.getTarget().cpu.arch == .x86_64)
|
||||
elf.SHT_X86_64_UNWIND
|
||||
else
|
||||
elf.SHT_PROGBITS,
|
||||
.flags = elf.SHF_ALLOC,
|
||||
.addralign = elf_file.ptrWidthBytes(),
|
||||
.offset = std.math.maxInt(u64),
|
||||
});
|
||||
}
|
||||
elf_file.eh_frame_rela_section_index = try elf_file.addRelaShdr(
|
||||
try elf_file.insertShString(".rela.eh_frame"),
|
||||
elf_file.eh_frame_section_index.?,
|
||||
@ -373,7 +376,11 @@ fn updateSectionSizes(elf_file: *Elf) !void {
|
||||
}
|
||||
|
||||
if (elf_file.eh_frame_section_index) |index| {
|
||||
slice.items(.shdr)[index].sh_size = try eh_frame.calcEhFrameSize(elf_file);
|
||||
slice.items(.shdr)[index].sh_size = existing_size: {
|
||||
const zo = elf_file.zigObjectPtr() orelse break :existing_size 0;
|
||||
const sym = zo.symbol(zo.eh_frame_index orelse break :existing_size 0);
|
||||
break :existing_size sym.atom(elf_file).?.size;
|
||||
} + try eh_frame.calcEhFrameSize(elf_file);
|
||||
}
|
||||
if (elf_file.eh_frame_rela_section_index) |index| {
|
||||
const shdr = &slice.items(.shdr)[index];
|
||||
@ -526,17 +533,22 @@ fn writeSyntheticSections(elf_file: *Elf) !void {
|
||||
}
|
||||
|
||||
if (elf_file.eh_frame_section_index) |shndx| {
|
||||
const existing_size = existing_size: {
|
||||
const zo = elf_file.zigObjectPtr() orelse break :existing_size 0;
|
||||
const sym = zo.symbol(zo.eh_frame_index orelse break :existing_size 0);
|
||||
break :existing_size sym.atom(elf_file).?.size;
|
||||
};
|
||||
const shdr = slice.items(.shdr)[shndx];
|
||||
const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
|
||||
var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
|
||||
var buffer = try std.ArrayList(u8).initCapacity(gpa, @intCast(sh_size - existing_size));
|
||||
defer buffer.deinit();
|
||||
try eh_frame.writeEhFrameObject(elf_file, buffer.writer());
|
||||
log.debug("writing .eh_frame from 0x{x} to 0x{x}", .{
|
||||
shdr.sh_offset,
|
||||
shdr.sh_offset + shdr.sh_size,
|
||||
shdr.sh_offset + existing_size,
|
||||
shdr.sh_offset + sh_size,
|
||||
});
|
||||
assert(buffer.items.len == sh_size);
|
||||
try elf_file.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
|
||||
assert(buffer.items.len == sh_size - existing_size);
|
||||
try elf_file.base.file.?.pwriteAll(buffer.items, shdr.sh_offset + existing_size);
|
||||
}
|
||||
if (elf_file.eh_frame_rela_section_index) |shndx| {
|
||||
const shdr = slice.items(.shdr)[shndx];
|
||||
|
||||
@ -108,20 +108,27 @@ pub const dwarf = struct {
|
||||
|
||||
pub fn externalRelocType(
|
||||
target: Symbol,
|
||||
source_section: Dwarf.Section.Index,
|
||||
address_size: Dwarf.AddressSize,
|
||||
cpu_arch: std.Target.Cpu.Arch,
|
||||
) u32 {
|
||||
return switch (cpu_arch) {
|
||||
.x86_64 => @intFromEnum(switch (address_size) {
|
||||
.@"32" => if (target.flags.is_tls) elf.R_X86_64.DTPOFF32 else .@"32",
|
||||
.@"64" => if (target.flags.is_tls) elf.R_X86_64.DTPOFF64 else .@"64",
|
||||
else => unreachable,
|
||||
}),
|
||||
.riscv64 => @intFromEnum(switch (address_size) {
|
||||
.@"32" => elf.R_RISCV.@"32",
|
||||
.@"64" => elf.R_RISCV.@"64",
|
||||
else => unreachable,
|
||||
}),
|
||||
.x86_64 => @intFromEnum(@as(elf.R_X86_64, switch (source_section) {
|
||||
else => switch (address_size) {
|
||||
.@"32" => if (target.flags.is_tls) .DTPOFF32 else .@"32",
|
||||
.@"64" => if (target.flags.is_tls) .DTPOFF64 else .@"64",
|
||||
else => unreachable,
|
||||
},
|
||||
.debug_frame => .PC32,
|
||||
})),
|
||||
.riscv64 => @intFromEnum(@as(elf.R_RISCV, switch (source_section) {
|
||||
else => switch (address_size) {
|
||||
.@"32" => .@"32",
|
||||
.@"64" => .@"64",
|
||||
else => unreachable,
|
||||
},
|
||||
.debug_frame => unreachable,
|
||||
})),
|
||||
else => @panic("TODO unhandled cpu arch"),
|
||||
};
|
||||
}
|
||||
|
||||
@ -3411,9 +3411,7 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !vo
|
||||
|
||||
if (!sect.isZerofill()) {
|
||||
const allocated_size = self.allocatedSize(sect.offset);
|
||||
if (sect.offset + allocated_size == std.math.maxInt(u64)) {
|
||||
try self.base.file.?.setEndPos(sect.offset + needed_size);
|
||||
} else if (needed_size > allocated_size) {
|
||||
if (needed_size > allocated_size) {
|
||||
const existing_size = sect.size;
|
||||
sect.size = 0;
|
||||
|
||||
@ -3431,6 +3429,8 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !vo
|
||||
try self.copyRangeAllZeroOut(sect.offset, new_offset, existing_size);
|
||||
|
||||
sect.offset = @intCast(new_offset);
|
||||
} else if (sect.offset + allocated_size == std.math.maxInt(u64)) {
|
||||
try self.base.file.?.setEndPos(sect.offset + needed_size);
|
||||
}
|
||||
seg.filesize = needed_size;
|
||||
}
|
||||
@ -3456,9 +3456,7 @@ fn growSectionRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !void
|
||||
|
||||
if (!sect.isZerofill()) {
|
||||
const allocated_size = self.allocatedSize(sect.offset);
|
||||
if (sect.offset + allocated_size == std.math.maxInt(u64)) {
|
||||
try self.base.file.?.setEndPos(sect.offset + needed_size);
|
||||
} else if (needed_size > allocated_size) {
|
||||
if (needed_size > allocated_size) {
|
||||
const existing_size = sect.size;
|
||||
sect.size = 0;
|
||||
|
||||
@ -3480,6 +3478,8 @@ fn growSectionRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !void
|
||||
|
||||
sect.offset = @intCast(new_offset);
|
||||
sect.addr = new_addr;
|
||||
} else if (sect.offset + allocated_size == std.math.maxInt(u64)) {
|
||||
try self.base.file.?.setEndPos(sect.offset + needed_size);
|
||||
}
|
||||
}
|
||||
sect.size = needed_size;
|
||||
|
||||
@ -105,9 +105,7 @@ pub fn growSection(
|
||||
const sect = self.getSectionPtr(sect_index);
|
||||
|
||||
const allocated_size = self.allocatedSize(sect.offset);
|
||||
if (sect.offset + allocated_size == std.math.maxInt(u64)) {
|
||||
try self.file.setEndPos(sect.offset + needed_size);
|
||||
} else if (needed_size > allocated_size) {
|
||||
if (needed_size > allocated_size) {
|
||||
const existing_size = sect.size;
|
||||
sect.size = 0; // free the space
|
||||
const new_offset = try self.findFreeSpace(needed_size, 1);
|
||||
@ -130,6 +128,8 @@ pub fn growSection(
|
||||
}
|
||||
|
||||
sect.offset = @intCast(new_offset);
|
||||
} else if (sect.offset + allocated_size == std.math.maxInt(u64)) {
|
||||
try self.file.setEndPos(sect.offset + needed_size);
|
||||
}
|
||||
|
||||
sect.size = needed_size;
|
||||
|
||||
@ -305,6 +305,82 @@ pub fn addTestsForTarget(db: *Debugger, target: Target) void {
|
||||
\\1 breakpoints deleted; 0 breakpoint locations disabled.
|
||||
},
|
||||
);
|
||||
db.addLldbTest(
|
||||
"strings",
|
||||
target,
|
||||
&.{
|
||||
.{
|
||||
.path = "strings.zig",
|
||||
.source =
|
||||
\\const Strings = struct {
|
||||
\\ c_ptr: [*c]const u8 = "c_ptr\x07\x08\t",
|
||||
\\ many_ptr: [*:0]const u8 = "many_ptr\n\x0b\x0c",
|
||||
\\ ptr_array: *const [12:0]u8 = "ptr_array\x00\r\x1b",
|
||||
\\ slice: [:0]const u8 = "slice\"\'\\\x00",
|
||||
\\};
|
||||
\\fn testStrings(strings: Strings) void {
|
||||
\\ _ = strings;
|
||||
\\}
|
||||
\\pub fn main() void {
|
||||
\\ testStrings(.{});
|
||||
\\}
|
||||
\\
|
||||
,
|
||||
},
|
||||
},
|
||||
\\breakpoint set --file strings.zig --source-pattern-regexp '_ = strings;'
|
||||
\\process launch
|
||||
\\frame variable --show-types strings.slice
|
||||
\\frame variable --show-types --format character strings.slice
|
||||
\\frame variable --show-types --format c-string strings
|
||||
\\breakpoint delete --force 1
|
||||
,
|
||||
&.{
|
||||
\\(lldb) frame variable --show-types strings.slice
|
||||
\\([:0]const u8) strings.slice = len=9 {
|
||||
\\ (u8) [0] = 115
|
||||
\\ (u8) [1] = 108
|
||||
\\ (u8) [2] = 105
|
||||
\\ (u8) [3] = 99
|
||||
\\ (u8) [4] = 101
|
||||
\\ (u8) [5] = 34
|
||||
\\ (u8) [6] = 39
|
||||
\\ (u8) [7] = 92
|
||||
\\ (u8) [8] = 0
|
||||
\\}
|
||||
\\(lldb) frame variable --show-types --format character strings.slice
|
||||
\\([:0]const u8) strings.slice = len=9 {
|
||||
\\ (u8) [0] = 's'
|
||||
\\ (u8) [1] = 'l'
|
||||
\\ (u8) [2] = 'i'
|
||||
\\ (u8) [3] = 'c'
|
||||
\\ (u8) [4] = 'e'
|
||||
\\ (u8) [5] = '\"'
|
||||
\\ (u8) [6] = '\''
|
||||
\\ (u8) [7] = '\\'
|
||||
\\ (u8) [8] = '\x00'
|
||||
\\}
|
||||
\\(lldb) frame variable --show-types --format c-string strings
|
||||
\\(root.strings.Strings) strings = {
|
||||
\\ ([*c]const u8) c_ptr = "c_ptr\x07\x08\t"
|
||||
\\ ([*:0]const u8) many_ptr = "many_ptr\n\x0b\x0c"
|
||||
\\ (*const [12:0]u8) ptr_array = "ptr_array\x00\r\x1b"
|
||||
\\ ([:0]const u8) slice = "slice\"\'\\\x00" len=9 {
|
||||
\\ (u8) [0] = "s"
|
||||
\\ (u8) [1] = "l"
|
||||
\\ (u8) [2] = "i"
|
||||
\\ (u8) [3] = "c"
|
||||
\\ (u8) [4] = "e"
|
||||
\\ (u8) [5] = "\""
|
||||
\\ (u8) [6] = "\'"
|
||||
\\ (u8) [7] = "\\"
|
||||
\\ (u8) [8] = "\x00"
|
||||
\\ }
|
||||
\\}
|
||||
\\(lldb) breakpoint delete --force 1
|
||||
\\1 breakpoints deleted; 0 breakpoint locations disabled.
|
||||
},
|
||||
);
|
||||
db.addLldbTest(
|
||||
"enums",
|
||||
target,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user