commit 11bb8ab9b3cf832a5e391ac8c73cbc33221db19d (tree)
parent b3747dd707eeaf06e0bacbde259bf71ef525a961
Author: Alex Rønne Petersen <alex@alexrp.com>
Date: Tue, 26 May 2026 12:35:37 +0200
Merge pull request 'std: initial `xtensa-linux-none` port' (#35463) from alexrp/zig:xtensa-linux into master
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/35463
Diffstat:
9 files changed, 260 insertions(+), 3 deletions(-)
diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig
@@ -1411,6 +1411,16 @@ const LinuxThreadImpl = struct {
: [ptr] "{r4}" (@intFromPtr(self.mapped.ptr)),
[len] "{r5}" (self.mapped.len),
: .{ .memory = true }),
+ .xtensa, .xtensaeb => asm volatile (
+ \\ movi a2, 81 // SYS_munmap
+ \\ syscall
+ \\ movi a6, 0
+ \\ movi a2, 118 // SYS_exit
+ \\ syscall
+ :
+ : [ptr] "{a6}" (@intFromPtr(self.mapped.ptr)),
+ [len] "{a3}" (self.mapped.len),
+ : .{ .memory = true }),
else => |cpu_arch| @compileError("Unsupported linux arch: " ++ @tagName(cpu_arch)),
}
unreachable;
diff --git a/lib/std/debug.zig b/lib/std/debug.zig
@@ -994,6 +994,8 @@ const StackIterator = union(enum) {
.sh,
.sheb,
.xcore,
+ .xtensa,
+ .xtensaeb,
=> .useless,
.hexagon,
// The PowerPC ABIs don't actually strictly require a backchain pointer; they allow omitting
diff --git a/lib/std/elf.zig b/lib/std/elf.zig
@@ -224,6 +224,9 @@ pub const DT_PPC64_NUM = 4;
pub const DT_IA_64_PLT_RESERVE = (DT_LOPROC + 0);
pub const DT_IA_64_NUM = 1;
+pub const DT_XTENSA_GOT_LOC_OFF = 0x70000000;
+pub const DT_XTENSA_GOT_LOC_SZ = 0x70000001;
+
pub const DT_NIOS2_GP = 0x70000002;
pub const DF_ORIGIN = 0x00000001;
diff --git a/lib/std/os.zig b/lib/std/os.zig
@@ -28,9 +28,6 @@ pub fn targetRequiresLibC(target: *const std.Target) bool {
.sheb,
// https://codeberg.org/ziglang/zig/issues/30945
.sparc,
- // https://codeberg.org/ziglang/zig/issues/30947
- .xtensa,
- .xtensaeb,
=> true,
else => false,
},
diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig
@@ -56,6 +56,7 @@ const arch_bits = switch (native_arch) {
.gnux32, .muslx32 => @import("linux/x32.zig"),
else => @import("linux/x86_64.zig"),
},
+ .xtensa, .xtensaeb => @import("linux/xtensa.zig"),
else => struct {},
};
@@ -338,6 +339,29 @@ pub const MAP = switch (native_arch) {
_20: u1 = 0,
_: u5 = 0,
},
+ .xtensa, .xtensaeb => packed struct(u32) {
+ TYPE: MAP_TYPE,
+ FIXED: bool = false,
+ _RENAME: bool = false,
+ _AUTOGROW: bool = false,
+ _LOCAL: bool = false,
+ _AUTORSRV: bool = false,
+ _1: u1 = 0,
+ NORESERVE: bool = false,
+ ANONYMOUS: bool = false,
+ GROWSDOWN: bool = false,
+ DENYWRITE: bool = false,
+ EXECUTABLE: bool = false,
+ LOCKED: bool = false,
+ POPULATE: bool = false,
+ NONBLOCK: bool = false,
+ STACK: bool = false,
+ HUGETLB: bool = false,
+ FIXED_NOREPLACE: bool = false,
+ _2: u5 = 0,
+ UNINITIALIZED: bool = false,
+ _3: u5 = 0,
+ },
else => @compileError("missing std.os.linux.MAP constants for this architecture"),
};
@@ -497,6 +521,8 @@ pub const O = switch (native_arch) {
.hexagon,
.or1k,
.s390x,
+ .xtensa,
+ .xtensaeb,
=> packed struct(u32) {
ACCMODE: ACCMODE = .RDONLY,
_2: u4 = 0,
@@ -6617,6 +6643,12 @@ pub const k_sigaction = switch (native_arch) {
mask: sigset_t,
flags: c_int,
},
+ .xtensa, .xtensaeb => extern struct {
+ handler: k_sigaction_funcs.handler,
+ mask: sigset_t,
+ flags: c_ulong,
+ restorer: k_sigaction_funcs.restorer,
+ },
else => extern struct {
handler: k_sigaction_funcs.handler,
flags: c_ulong,
diff --git a/lib/std/os/linux/tls.zig b/lib/std/os/linux/tls.zig
@@ -78,6 +78,8 @@ const current_variant: Variant = switch (native_arch) {
.sheb,
.thumb,
.thumbeb,
+ .xtensa,
+ .xtensaeb,
=> .I_original,
.loongarch32,
.loongarch64,
@@ -152,6 +154,8 @@ const AbiTcb = switch (current_variant) {
.sheb,
.thumb,
.thumbeb,
+ .xtensa,
+ .xtensaeb,
=> extern struct {
/// This is offset by `current_dtv_offset`.
dtv: usize,
@@ -364,6 +368,13 @@ pub fn setThreadPointer(addr: usize) void {
: [addr] "r" (addr),
);
},
+ .xtensa, .xtensaeb => {
+ asm volatile (
+ \\ wur %[addr], threadptr
+ :
+ : [addr] "a" (addr),
+ );
+ },
else => @compileError("Unsupported architecture"),
}
}
diff --git a/lib/std/os/linux/xtensa.zig b/lib/std/os/linux/xtensa.zig
@@ -0,0 +1,171 @@
+const builtin = @import("builtin");
+const std = @import("../../std.zig");
+const SYS = std.os.linux.SYS;
+
+pub const syscall_arg_t = u32;
+
+pub fn syscall0(
+ number: SYS,
+) u32 {
+ return asm volatile ("syscall"
+ : [ret] "={a2}" (-> u32),
+ : [number] "{a2}" (@intFromEnum(number)),
+ : .{ .memory = true });
+}
+
+pub fn syscall1(
+ number: SYS,
+ arg1: syscall_arg_t,
+) u32 {
+ return asm volatile ("syscall"
+ : [ret] "={a2}" (-> u32),
+ : [number] "{a2}" (@intFromEnum(number)),
+ [arg1] "{a6}" (arg1),
+ : .{ .memory = true });
+}
+
+pub fn syscall2(
+ number: SYS,
+ arg1: syscall_arg_t,
+ arg2: syscall_arg_t,
+) u32 {
+ return asm volatile ("syscall"
+ : [ret] "={a2}" (-> u32),
+ : [number] "{a2}" (@intFromEnum(number)),
+ [arg1] "{a6}" (arg1),
+ [arg2] "{a3}" (arg2),
+ : .{ .memory = true });
+}
+
+pub fn syscall3(
+ number: SYS,
+ arg1: syscall_arg_t,
+ arg2: syscall_arg_t,
+ arg3: syscall_arg_t,
+) u32 {
+ return asm volatile ("syscall"
+ : [ret] "={a2}" (-> u32),
+ : [number] "{a2}" (@intFromEnum(number)),
+ [arg1] "{a6}" (arg1),
+ [arg2] "{a3}" (arg2),
+ [arg3] "{a4}" (arg3),
+ : .{ .memory = true });
+}
+
+pub fn syscall4(
+ number: SYS,
+ arg1: syscall_arg_t,
+ arg2: syscall_arg_t,
+ arg3: syscall_arg_t,
+ arg4: syscall_arg_t,
+) u32 {
+ return asm volatile ("syscall"
+ : [ret] "={a2}" (-> u32),
+ : [number] "{a2}" (@intFromEnum(number)),
+ [arg1] "{a6}" (arg1),
+ [arg2] "{a3}" (arg2),
+ [arg3] "{a4}" (arg3),
+ [arg4] "{a5}" (arg4),
+ : .{ .memory = true });
+}
+
+pub fn syscall5(
+ number: SYS,
+ arg1: syscall_arg_t,
+ arg2: syscall_arg_t,
+ arg3: syscall_arg_t,
+ arg4: syscall_arg_t,
+ arg5: syscall_arg_t,
+) u32 {
+ return asm volatile ("syscall"
+ : [ret] "={a2}" (-> u32),
+ : [number] "{a2}" (@intFromEnum(number)),
+ [arg1] "{a6}" (arg1),
+ [arg2] "{a3}" (arg2),
+ [arg3] "{a4}" (arg3),
+ [arg4] "{a5}" (arg4),
+ [arg5] "{a8}" (arg5),
+ : .{ .memory = true });
+}
+
+pub fn syscall6(
+ number: SYS,
+ arg1: syscall_arg_t,
+ arg2: syscall_arg_t,
+ arg3: syscall_arg_t,
+ arg4: syscall_arg_t,
+ arg5: syscall_arg_t,
+ arg6: syscall_arg_t,
+) u32 {
+ return asm volatile ("syscall"
+ : [ret] "={a2}" (-> u32),
+ : [number] "{a2}" (@intFromEnum(number)),
+ [arg1] "{a6}" (arg1),
+ [arg2] "{a3}" (arg2),
+ [arg3] "{a4}" (arg3),
+ [arg4] "{a5}" (arg4),
+ [arg5] "{a8}" (arg5),
+ [arg6] "{a9}" (arg6),
+ : .{ .memory = true });
+}
+
+pub fn clone() callconv(.naked) u32 {
+ // __clone(func, stack, flags, arg, ptid, tls, ctid)
+ // a2, a3, a4, a5, a6, a7, +16
+ //
+ // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
+ // a2 a6, a3, a4, a5, a8
+ asm volatile (
+ \\ entry sp, 16
+ \\
+ \\ movi a8, -16
+ \\ and a3, a3, a8
+ \\
+ \\ mov a9, a2
+ \\ mov a10, a5
+ \\
+ \\ mov a5, a7
+ \\ mov a8, a6
+ \\ mov a6, a4
+ \\ mov a4, a8
+ \\ l32i a8, sp, 16
+ \\ movi a2, 116 // SYS_clone
+ \\ syscall
+ \\
+ \\ beqz a2, 1f
+ \\ // parent
+ \\ retw
+ \\
+ \\ // child
+ \\1:
+ \\ movi a7, 0
+ \\ movi a0, 0
+ \\
+ \\ mov a2, a10
+ \\ callx0 a9
+ \\ movi a2, 118 // SYS_exit
+ \\ syscall
+ );
+}
+
+pub const restore = restore_rt;
+
+pub fn restore_rt() callconv(.naked) noreturn {
+ switch (builtin.zig_backend) {
+ .stage2_c => asm volatile (
+ \\ movi a2, %[number]
+ \\ syscall
+ :
+ : [number] "I" (@intFromEnum(SYS.rt_sigreturn)),
+ ),
+ else => asm volatile (
+ \\ syscall
+ :
+ : [number] "{a2}" (@intFromEnum(SYS.rt_sigreturn)),
+ ),
+ }
+}
+
+pub const VDSO = void;
+
+pub const time_t = i32;
diff --git a/lib/std/pie.zig b/lib/std/pie.zig
@@ -22,6 +22,7 @@ const R_RISCV_RELATIVE = 3;
const R_390_RELATIVE = 12;
const R_SH_RELATIVE = 165;
const R_SPARC_RELATIVE = 22;
+const R_XTENSA_RELATIVE = 5;
const R_RELATIVE = switch (builtin.cpu.arch) {
.x86 => R_386_RELATIVE,
@@ -43,6 +44,7 @@ const R_RELATIVE = switch (builtin.cpu.arch) {
.s390x => R_390_RELATIVE,
.sh, .sheb => R_SH_RELATIVE,
.sparc, .sparc64 => R_SPARC_RELATIVE,
+ .xtensa, .xtensaeb => R_XTENSA_RELATIVE,
else => @compileError("Missing R_RELATIVE definition for this target"),
};
@@ -261,6 +263,25 @@ inline fn getDynamicSymbol() [*]const elf.Dyn {
: [ret] "=r" (-> [*]const elf.Dyn),
:
: .{ .l7 = true }),
+ .xtensa, .xtensaeb => asm volatile (
+ \\ .weak _DYNAMIC
+ \\ .hidden _DYNAMIC
+ // Set things up such that after the `call0`, `a0` will point 1 byte before the
+ // embedded constant. Note that `call0` is a 3-byte instruction, so we need both
+ // `.balign` directives to be safe.
+ \\ .balign 4
+ \\ .begin no-transform
+ \\ call0 1f
+ \\ .end no-transform
+ \\ .balign 4
+ \\ .word _DYNAMIC - .
+ \\1:
+ \\ add a0, a0, 1
+ \\ l32i a8, a0, 0
+ \\ add %[ret], a0, a8
+ : [ret] "=a" (-> [*]const elf.Dyn),
+ :
+ : .{ .a0 = true, .a8 = true }),
else => {
@compileError("PIE startup is not yet supported for this target!");
},
diff --git a/lib/std/start.zig b/lib/std/start.zig
@@ -183,6 +183,7 @@ fn _start() callconv(.naked) noreturn {
.sparc, .sparc64 => ".cfi_undefined %%i7",
.x86 => ".cfi_undefined %%eip",
.x86_64 => ".cfi_undefined %%rip",
+ .xtensa, .xtensaeb => "", // No CFI support.
else => @compileError("unsupported arch"),
});
@@ -486,6 +487,15 @@ fn _start() callconv(.naked) noreturn {
\\ sub %%sp, 2047, %%sp
\\ ba,a %[posixCallMainAndExit]
,
+ .xtensa, .xtensaeb =>
+ // a0 = LR, a7 = FP, a1 = SP
+ \\ movi a0, 0
+ \\ movi a7, 0
+ \\ mov a2, sp
+ \\ movi a8, -16
+ \\ and sp, sp, a8
+ \\ callx0 %[posixCallMainAndExit]
+ ,
else => @compileError("unsupported arch"),
}
: