diff --git a/patches/busybox-kernel-headers/README.md b/patches/busybox-kernel-headers/README.md new file mode 100644 index 0000000..1d6913a --- /dev/null +++ b/patches/busybox-kernel-headers/README.md @@ -0,0 +1,8 @@ +# Busybox Kernel Headers +Read repo root README.md for an explanation of what this is. + +## Origin +This patch comes from the patch set at https://github.com/sabotage-linux/kernel-headers/tree/4.4.2/patches but is slightly modified to fit a 6.4 kernel. Note that patch 2 in that set fails but can be ignored, patch 4, 6 and 8 partially works and partially needs some merge conflict resolving. All of this is combined into one patch in this repo. It would probably be better to offer a complete new patch set, oh well... + +## License +The same license as for the original source files apply, i.e. "GPL-2.0 WITH Linux-syscall-note". diff --git a/patches/busybox-kernel-headers/busybox-kernel-headers-for-musl.patch b/patches/busybox-kernel-headers/busybox-kernel-headers-for-musl.patch new file mode 100644 index 0000000..e33e623 --- /dev/null +++ b/patches/busybox-kernel-headers/busybox-kernel-headers-for-musl.patch @@ -0,0 +1,1851 @@ +diff -ruN a/linux/if_ether.h b/linux/if_ether.h +--- a/linux/if_ether.h 2024-02-25 19:26:11.628023992 +0100 ++++ b/linux/if_ether.h 2024-02-25 21:24:10.153141474 +0100 +@@ -23,6 +23,7 @@ + #define _LINUX_IF_ETHER_H + + #include ++#include + + /* + * IEEE 802.3 Ethernet magic constants. The frame sizes omit the preamble +diff -ruN a/linux/if_ether.h.orig b/linux/if_ether.h.orig +--- a/linux/if_ether.h.orig 1970-01-01 01:00:00.000000000 +0100 ++++ b/linux/if_ether.h.orig 2024-02-25 20:39:49.611132994 +0100 +@@ -0,0 +1,181 @@ ++/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ ++/* ++ * INET An implementation of the TCP/IP protocol suite for the LINUX ++ * operating system. INET is implemented using the BSD Socket ++ * interface as the means of communication with the user level. ++ * ++ * Global definitions for the Ethernet IEEE 802.3 interface. ++ * ++ * Version: @(#)if_ether.h 1.0.1a 02/08/94 ++ * ++ * Author: Fred N. van Kempen, ++ * Donald Becker, ++ * Alan Cox, ++ * Steve Whitehouse, ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#ifndef _LINUX_IF_ETHER_H ++#define _LINUX_IF_ETHER_H ++ ++#include ++ ++/* ++ * IEEE 802.3 Ethernet magic constants. The frame sizes omit the preamble ++ * and FCS/CRC (frame check sequence). ++ */ ++ ++#define ETH_ALEN 6 /* Octets in one ethernet addr */ ++#define ETH_TLEN 2 /* Octets in ethernet type field */ ++#define ETH_HLEN 14 /* Total octets in header. */ ++#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */ ++#define ETH_DATA_LEN 1500 /* Max. octets in payload */ ++#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ ++#define ETH_FCS_LEN 4 /* Octets in the FCS */ ++ ++#define ETH_MIN_MTU 68 /* Min IPv4 MTU per RFC791 */ ++#define ETH_MAX_MTU 0xFFFFU /* 65535, same as IP_MAX_MTU */ ++ ++/* ++ * These are the defined Ethernet Protocol ID's. ++ */ ++ ++#define ETH_P_LOOP 0x0060 /* Ethernet Loopback packet */ ++#define ETH_P_PUP 0x0200 /* Xerox PUP packet */ ++#define ETH_P_PUPAT 0x0201 /* Xerox PUP Addr Trans packet */ ++#define ETH_P_TSN 0x22F0 /* TSN (IEEE 1722) packet */ ++#define ETH_P_ERSPAN2 0x22EB /* ERSPAN version 2 (type III) */ ++#define ETH_P_IP 0x0800 /* Internet Protocol packet */ ++#define ETH_P_X25 0x0805 /* CCITT X.25 */ ++#define ETH_P_ARP 0x0806 /* Address Resolution packet */ ++#define ETH_P_BPQ 0x08FF /* G8BPQ AX.25 Ethernet Packet [ NOT AN OFFICIALLY REGISTERED ID ] */ ++#define ETH_P_IEEEPUP 0x0a00 /* Xerox IEEE802.3 PUP packet */ ++#define ETH_P_IEEEPUPAT 0x0a01 /* Xerox IEEE802.3 PUP Addr Trans packet */ ++#define ETH_P_BATMAN 0x4305 /* B.A.T.M.A.N.-Advanced packet [ NOT AN OFFICIALLY REGISTERED ID ] */ ++#define ETH_P_DEC 0x6000 /* DEC Assigned proto */ ++#define ETH_P_DNA_DL 0x6001 /* DEC DNA Dump/Load */ ++#define ETH_P_DNA_RC 0x6002 /* DEC DNA Remote Console */ ++#define ETH_P_DNA_RT 0x6003 /* DEC DNA Routing */ ++#define ETH_P_LAT 0x6004 /* DEC LAT */ ++#define ETH_P_DIAG 0x6005 /* DEC Diagnostics */ ++#define ETH_P_CUST 0x6006 /* DEC Customer use */ ++#define ETH_P_SCA 0x6007 /* DEC Systems Comms Arch */ ++#define ETH_P_TEB 0x6558 /* Trans Ether Bridging */ ++#define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */ ++#define ETH_P_ATALK 0x809B /* Appletalk DDP */ ++#define ETH_P_AARP 0x80F3 /* Appletalk AARP */ ++#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ ++#define ETH_P_ERSPAN 0x88BE /* ERSPAN type II */ ++#define ETH_P_IPX 0x8137 /* IPX over DIX */ ++#define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */ ++#define ETH_P_PAUSE 0x8808 /* IEEE Pause frames. See 802.3 31B */ ++#define ETH_P_SLOW 0x8809 /* Slow Protocol. See 802.3ad 43B */ ++#define ETH_P_WCCP 0x883E /* Web-cache coordination protocol ++ * defined in draft-wilson-wrec-wccp-v2-00.txt */ ++#define ETH_P_MPLS_UC 0x8847 /* MPLS Unicast traffic */ ++#define ETH_P_MPLS_MC 0x8848 /* MPLS Multicast traffic */ ++#define ETH_P_ATMMPOA 0x884c /* MultiProtocol Over ATM */ ++#define ETH_P_PPP_DISC 0x8863 /* PPPoE discovery messages */ ++#define ETH_P_PPP_SES 0x8864 /* PPPoE session messages */ ++#define ETH_P_LINK_CTL 0x886c /* HPNA, wlan link local tunnel */ ++#define ETH_P_ATMFATE 0x8884 /* Frame-based ATM Transport ++ * over Ethernet ++ */ ++#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ ++#define ETH_P_PROFINET 0x8892 /* PROFINET */ ++#define ETH_P_REALTEK 0x8899 /* Multiple proprietary protocols */ ++#define ETH_P_AOE 0x88A2 /* ATA over Ethernet */ ++#define ETH_P_ETHERCAT 0x88A4 /* EtherCAT */ ++#define ETH_P_8021AD 0x88A8 /* 802.1ad Service VLAN */ ++#define ETH_P_802_EX1 0x88B5 /* 802.1 Local Experimental 1. */ ++#define ETH_P_PREAUTH 0x88C7 /* 802.11 Preauthentication */ ++#define ETH_P_TIPC 0x88CA /* TIPC */ ++#define ETH_P_LLDP 0x88CC /* Link Layer Discovery Protocol */ ++#define ETH_P_MRP 0x88E3 /* Media Redundancy Protocol */ ++#define ETH_P_MACSEC 0x88E5 /* 802.1ae MACsec */ ++#define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */ ++#define ETH_P_MVRP 0x88F5 /* 802.1Q MVRP */ ++#define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */ ++#define ETH_P_NCSI 0x88F8 /* NCSI protocol */ ++#define ETH_P_PRP 0x88FB /* IEC 62439-3 PRP/HSRv0 */ ++#define ETH_P_CFM 0x8902 /* Connectivity Fault Management */ ++#define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */ ++#define ETH_P_IBOE 0x8915 /* Infiniband over Ethernet */ ++#define ETH_P_TDLS 0x890D /* TDLS */ ++#define ETH_P_FIP 0x8914 /* FCoE Initialization Protocol */ ++#define ETH_P_80221 0x8917 /* IEEE 802.21 Media Independent Handover Protocol */ ++#define ETH_P_HSR 0x892F /* IEC 62439-3 HSRv1 */ ++#define ETH_P_NSH 0x894F /* Network Service Header */ ++#define ETH_P_LOOPBACK 0x9000 /* Ethernet loopback packet, per IEEE 802.3 */ ++#define ETH_P_QINQ1 0x9100 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ ++#define ETH_P_QINQ2 0x9200 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ ++#define ETH_P_QINQ3 0x9300 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ ++#define ETH_P_EDSA 0xDADA /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */ ++#define ETH_P_DSA_8021Q 0xDADB /* Fake VLAN Header for DSA [ NOT AN OFFICIALLY REGISTERED ID ] */ ++#define ETH_P_DSA_A5PSW 0xE001 /* A5PSW Tag Value [ NOT AN OFFICIALLY REGISTERED ID ] */ ++#define ETH_P_IFE 0xED3E /* ForCES inter-FE LFB type */ ++#define ETH_P_AF_IUCV 0xFBFB /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */ ++ ++#define ETH_P_802_3_MIN 0x0600 /* If the value in the ethernet type is more than this value ++ * then the frame is Ethernet II. Else it is 802.3 */ ++ ++/* ++ * Non DIX types. Won't clash for 1500 types. ++ */ ++ ++#define ETH_P_802_3 0x0001 /* Dummy type for 802.3 frames */ ++#define ETH_P_AX25 0x0002 /* Dummy protocol id for AX.25 */ ++#define ETH_P_ALL 0x0003 /* Every packet (be careful!!!) */ ++#define ETH_P_802_2 0x0004 /* 802.2 frames */ ++#define ETH_P_SNAP 0x0005 /* Internal only */ ++#define ETH_P_DDCMP 0x0006 /* DEC DDCMP: Internal only */ ++#define ETH_P_WAN_PPP 0x0007 /* Dummy type for WAN PPP frames*/ ++#define ETH_P_PPP_MP 0x0008 /* Dummy type for PPP MP frames */ ++#define ETH_P_LOCALTALK 0x0009 /* Localtalk pseudo type */ ++#define ETH_P_CAN 0x000C /* CAN: Controller Area Network */ ++#define ETH_P_CANFD 0x000D /* CANFD: CAN flexible data rate*/ ++#define ETH_P_CANXL 0x000E /* CANXL: eXtended frame Length */ ++#define ETH_P_PPPTALK 0x0010 /* Dummy type for Atalk over PPP*/ ++#define ETH_P_TR_802_2 0x0011 /* 802.2 frames */ ++#define ETH_P_MOBITEX 0x0015 /* Mobitex (kaz@cafe.net) */ ++#define ETH_P_CONTROL 0x0016 /* Card specific control frames */ ++#define ETH_P_IRDA 0x0017 /* Linux-IrDA */ ++#define ETH_P_ECONET 0x0018 /* Acorn Econet */ ++#define ETH_P_HDLC 0x0019 /* HDLC frames */ ++#define ETH_P_ARCNET 0x001A /* 1A for ArcNet :-) */ ++#define ETH_P_DSA 0x001B /* Distributed Switch Arch. */ ++#define ETH_P_TRAILER 0x001C /* Trailer switch tagging */ ++#define ETH_P_PHONET 0x00F5 /* Nokia Phonet frames */ ++#define ETH_P_IEEE802154 0x00F6 /* IEEE802.15.4 frame */ ++#define ETH_P_CAIF 0x00F7 /* ST-Ericsson CAIF protocol */ ++#define ETH_P_XDSA 0x00F8 /* Multiplexed DSA protocol */ ++#define ETH_P_MAP 0x00F9 /* Qualcomm multiplexing and ++ * aggregation protocol ++ */ ++#define ETH_P_MCTP 0x00FA /* Management component transport ++ * protocol packets ++ */ ++ ++/* ++ * This is an Ethernet frame header. ++ */ ++ ++/* allow libcs like musl to deactivate this, glibc does not implement this. */ ++#ifndef __UAPI_DEF_ETHHDR ++#define __UAPI_DEF_ETHHDR 1 ++#endif ++ ++#if __UAPI_DEF_ETHHDR ++struct ethhdr { ++ unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ ++ unsigned char h_source[ETH_ALEN]; /* source ether addr */ ++ __be16 h_proto; /* packet type ID field */ ++} __attribute__((packed)); ++#endif ++ ++ ++#endif /* _LINUX_IF_ETHER_H */ +diff -ruN a/linux/if_ether.h.rej b/linux/if_ether.h.rej +--- a/linux/if_ether.h.rej 1970-01-01 01:00:00.000000000 +0100 ++++ b/linux/if_ether.h.rej 2024-02-25 21:24:10.153141474 +0100 +@@ -0,0 +1,16 @@ ++--- linux/if_ether.h +++++ linux/if_ether.h ++@@ -136,11 +137,12 @@ ++ * This is an Ethernet frame header. ++ */ ++ +++#if __UAPI_DEF_ETHHDR ++ struct ethhdr { ++ unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ ++ unsigned char h_source[ETH_ALEN]; /* source ether addr */ ++ __be16 h_proto; /* packet type ID field */ ++ } __attribute__((packed)); ++- +++#endif ++ ++ #endif /* _LINUX_IF_ETHER_H */ +diff -ruN a/linux/if.h.orig b/linux/if.h.orig +--- a/linux/if.h.orig 1970-01-01 01:00:00.000000000 +0100 ++++ b/linux/if.h.orig 2024-02-25 20:39:49.611132994 +0100 +@@ -0,0 +1,296 @@ ++/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ ++/* ++ * INET An implementation of the TCP/IP protocol suite for the LINUX ++ * operating system. INET is implemented using the BSD Socket ++ * interface as the means of communication with the user level. ++ * ++ * Global definitions for the INET interface module. ++ * ++ * Version: @(#)if.h 1.0.2 04/18/93 ++ * ++ * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1982-1988 ++ * Ross Biro ++ * Fred N. van Kempen, ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++#ifndef _LINUX_IF_H ++#define _LINUX_IF_H ++ ++#include /* for compatibility with glibc */ ++#include /* for "__kernel_caddr_t" et al */ ++#include /* for "struct sockaddr" et al */ ++ /* for "__user" et al */ ++ ++#include /* for struct sockaddr. */ ++ ++#if __UAPI_DEF_IF_IFNAMSIZ ++#define IFNAMSIZ 16 ++#endif /* __UAPI_DEF_IF_IFNAMSIZ */ ++#define IFALIASZ 256 ++#define ALTIFNAMSIZ 128 ++#include ++ ++/* For glibc compatibility. An empty enum does not compile. */ ++#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO != 0 || \ ++ __UAPI_DEF_IF_NET_DEVICE_FLAGS != 0 ++/** ++ * enum net_device_flags - &struct net_device flags ++ * ++ * These are the &struct net_device flags, they can be set by drivers, the ++ * kernel and some can be triggered by userspace. Userspace can query and ++ * set these flags using userspace utilities but there is also a sysfs ++ * entry available for all dev flags which can be queried and set. These flags ++ * are shared for all types of net_devices. The sysfs entries are available ++ * via /sys/class/net//flags. Flags which can be toggled through sysfs ++ * are annotated below, note that only a few flags can be toggled and some ++ * other flags are always preserved from the original net_device flags ++ * even if you try to set them via sysfs. Flags which are always preserved ++ * are kept under the flag grouping @IFF_VOLATILE. Flags which are __volatile__ ++ * are annotated below as such. ++ * ++ * You should have a pretty good reason to be extending these flags. ++ * ++ * @IFF_UP: interface is up. Can be toggled through sysfs. ++ * @IFF_BROADCAST: broadcast address valid. Volatile. ++ * @IFF_DEBUG: turn on debugging. Can be toggled through sysfs. ++ * @IFF_LOOPBACK: is a loopback net. Volatile. ++ * @IFF_POINTOPOINT: interface is has p-p link. Volatile. ++ * @IFF_NOTRAILERS: avoid use of trailers. Can be toggled through sysfs. ++ * Volatile. ++ * @IFF_RUNNING: interface RFC2863 OPER_UP. Volatile. ++ * @IFF_NOARP: no ARP protocol. Can be toggled through sysfs. Volatile. ++ * @IFF_PROMISC: receive all packets. Can be toggled through sysfs. ++ * @IFF_ALLMULTI: receive all multicast packets. Can be toggled through ++ * sysfs. ++ * @IFF_MASTER: master of a load balancer. Volatile. ++ * @IFF_SLAVE: slave of a load balancer. Volatile. ++ * @IFF_MULTICAST: Supports multicast. Can be toggled through sysfs. ++ * @IFF_PORTSEL: can set media type. Can be toggled through sysfs. ++ * @IFF_AUTOMEDIA: auto media select active. Can be toggled through sysfs. ++ * @IFF_DYNAMIC: dialup device with changing addresses. Can be toggled ++ * through sysfs. ++ * @IFF_LOWER_UP: driver signals L1 up. Volatile. ++ * @IFF_DORMANT: driver signals dormant. Volatile. ++ * @IFF_ECHO: echo sent packets. Volatile. ++ */ ++enum net_device_flags { ++/* for compatibility with glibc net/if.h */ ++#if __UAPI_DEF_IF_NET_DEVICE_FLAGS ++ IFF_UP = 1<<0, /* sysfs */ ++ IFF_BROADCAST = 1<<1, /* __volatile__ */ ++ IFF_DEBUG = 1<<2, /* sysfs */ ++ IFF_LOOPBACK = 1<<3, /* __volatile__ */ ++ IFF_POINTOPOINT = 1<<4, /* __volatile__ */ ++ IFF_NOTRAILERS = 1<<5, /* sysfs */ ++ IFF_RUNNING = 1<<6, /* __volatile__ */ ++ IFF_NOARP = 1<<7, /* sysfs */ ++ IFF_PROMISC = 1<<8, /* sysfs */ ++ IFF_ALLMULTI = 1<<9, /* sysfs */ ++ IFF_MASTER = 1<<10, /* __volatile__ */ ++ IFF_SLAVE = 1<<11, /* __volatile__ */ ++ IFF_MULTICAST = 1<<12, /* sysfs */ ++ IFF_PORTSEL = 1<<13, /* sysfs */ ++ IFF_AUTOMEDIA = 1<<14, /* sysfs */ ++ IFF_DYNAMIC = 1<<15, /* sysfs */ ++#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */ ++#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO ++ IFF_LOWER_UP = 1<<16, /* __volatile__ */ ++ IFF_DORMANT = 1<<17, /* __volatile__ */ ++ IFF_ECHO = 1<<18, /* __volatile__ */ ++#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ ++}; ++#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO != 0 || __UAPI_DEF_IF_NET_DEVICE_FLAGS != 0 */ ++ ++/* for compatibility with glibc net/if.h */ ++#if __UAPI_DEF_IF_NET_DEVICE_FLAGS ++#define IFF_UP IFF_UP ++#define IFF_BROADCAST IFF_BROADCAST ++#define IFF_DEBUG IFF_DEBUG ++#define IFF_LOOPBACK IFF_LOOPBACK ++#define IFF_POINTOPOINT IFF_POINTOPOINT ++#define IFF_NOTRAILERS IFF_NOTRAILERS ++#define IFF_RUNNING IFF_RUNNING ++#define IFF_NOARP IFF_NOARP ++#define IFF_PROMISC IFF_PROMISC ++#define IFF_ALLMULTI IFF_ALLMULTI ++#define IFF_MASTER IFF_MASTER ++#define IFF_SLAVE IFF_SLAVE ++#define IFF_MULTICAST IFF_MULTICAST ++#define IFF_PORTSEL IFF_PORTSEL ++#define IFF_AUTOMEDIA IFF_AUTOMEDIA ++#define IFF_DYNAMIC IFF_DYNAMIC ++#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */ ++ ++#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO ++#define IFF_LOWER_UP IFF_LOWER_UP ++#define IFF_DORMANT IFF_DORMANT ++#define IFF_ECHO IFF_ECHO ++#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ ++ ++#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\ ++ IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT) ++ ++#define IF_GET_IFACE 0x0001 /* for querying only */ ++#define IF_GET_PROTO 0x0002 ++ ++/* For definitions see hdlc.h */ ++#define IF_IFACE_V35 0x1000 /* V.35 serial interface */ ++#define IF_IFACE_V24 0x1001 /* V.24 serial interface */ ++#define IF_IFACE_X21 0x1002 /* X.21 serial interface */ ++#define IF_IFACE_T1 0x1003 /* T1 telco serial interface */ ++#define IF_IFACE_E1 0x1004 /* E1 telco serial interface */ ++#define IF_IFACE_SYNC_SERIAL 0x1005 /* can't be set by software */ ++#define IF_IFACE_X21D 0x1006 /* X.21 Dual Clocking (FarSite) */ ++ ++/* For definitions see hdlc.h */ ++#define IF_PROTO_HDLC 0x2000 /* raw HDLC protocol */ ++#define IF_PROTO_PPP 0x2001 /* PPP protocol */ ++#define IF_PROTO_CISCO 0x2002 /* Cisco HDLC protocol */ ++#define IF_PROTO_FR 0x2003 /* Frame Relay protocol */ ++#define IF_PROTO_FR_ADD_PVC 0x2004 /* Create FR PVC */ ++#define IF_PROTO_FR_DEL_PVC 0x2005 /* Delete FR PVC */ ++#define IF_PROTO_X25 0x2006 /* X.25 */ ++#define IF_PROTO_HDLC_ETH 0x2007 /* raw HDLC, Ethernet emulation */ ++#define IF_PROTO_FR_ADD_ETH_PVC 0x2008 /* Create FR Ethernet-bridged PVC */ ++#define IF_PROTO_FR_DEL_ETH_PVC 0x2009 /* Delete FR Ethernet-bridged PVC */ ++#define IF_PROTO_FR_PVC 0x200A /* for reading PVC status */ ++#define IF_PROTO_FR_ETH_PVC 0x200B ++#define IF_PROTO_RAW 0x200C /* RAW Socket */ ++ ++/* RFC 2863 operational status */ ++enum { ++ IF_OPER_UNKNOWN, ++ IF_OPER_NOTPRESENT, ++ IF_OPER_DOWN, ++ IF_OPER_LOWERLAYERDOWN, ++ IF_OPER_TESTING, ++ IF_OPER_DORMANT, ++ IF_OPER_UP, ++}; ++ ++/* link modes */ ++enum { ++ IF_LINK_MODE_DEFAULT, ++ IF_LINK_MODE_DORMANT, /* limit upward transition to dormant */ ++ IF_LINK_MODE_TESTING, /* limit upward transition to testing */ ++}; ++ ++/* ++ * Device mapping structure. I'd just gone off and designed a ++ * beautiful scheme using only loadable modules with arguments ++ * for driver options and along come the PCMCIA people 8) ++ * ++ * Ah well. The get() side of this is good for WDSETUP, and it'll ++ * be handy for debugging things. The set side is fine for now and ++ * being very small might be worth keeping for clean configuration. ++ */ ++ ++/* for compatibility with glibc net/if.h */ ++#if __UAPI_DEF_IF_IFMAP ++struct ifmap { ++ unsigned long mem_start; ++ unsigned long mem_end; ++ unsigned short base_addr; ++ unsigned char irq; ++ unsigned char dma; ++ unsigned char port; ++ /* 3 bytes spare */ ++}; ++#endif /* __UAPI_DEF_IF_IFMAP */ ++ ++struct if_settings { ++ unsigned int type; /* Type of physical device or protocol */ ++ unsigned int size; /* Size of the data allocated by the caller */ ++ union { ++ /* {atm/eth/dsl}_settings anyone ? */ ++ raw_hdlc_proto *raw_hdlc; ++ cisco_proto *cisco; ++ fr_proto *fr; ++ fr_proto_pvc *fr_pvc; ++ fr_proto_pvc_info *fr_pvc_info; ++ x25_hdlc_proto *x25; ++ ++ /* interface settings */ ++ sync_serial_settings *sync; ++ te1_settings *te1; ++ } ifs_ifsu; ++}; ++ ++/* ++ * Interface request structure used for socket ++ * ioctl's. All interface ioctl's must have parameter ++ * definitions which begin with ifr_name. The ++ * remainder may be interface specific. ++ */ ++ ++/* for compatibility with glibc net/if.h */ ++#if __UAPI_DEF_IF_IFREQ ++struct ifreq { ++#define IFHWADDRLEN 6 ++ union ++ { ++ char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ ++ } ifr_ifrn; ++ ++ union { ++ struct sockaddr ifru_addr; ++ struct sockaddr ifru_dstaddr; ++ struct sockaddr ifru_broadaddr; ++ struct sockaddr ifru_netmask; ++ struct sockaddr ifru_hwaddr; ++ short ifru_flags; ++ int ifru_ivalue; ++ int ifru_mtu; ++ struct ifmap ifru_map; ++ char ifru_slave[IFNAMSIZ]; /* Just fits the size */ ++ char ifru_newname[IFNAMSIZ]; ++ void * ifru_data; ++ struct if_settings ifru_settings; ++ } ifr_ifru; ++}; ++#endif /* __UAPI_DEF_IF_IFREQ */ ++ ++#define ifr_name ifr_ifrn.ifrn_name /* interface name */ ++#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ ++#define ifr_addr ifr_ifru.ifru_addr /* address */ ++#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */ ++#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ ++#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */ ++#define ifr_flags ifr_ifru.ifru_flags /* flags */ ++#define ifr_metric ifr_ifru.ifru_ivalue /* metric */ ++#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */ ++#define ifr_map ifr_ifru.ifru_map /* device map */ ++#define ifr_slave ifr_ifru.ifru_slave /* slave device */ ++#define ifr_data ifr_ifru.ifru_data /* for use by interface */ ++#define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */ ++#define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */ ++#define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */ ++#define ifr_newname ifr_ifru.ifru_newname /* New name */ ++#define ifr_settings ifr_ifru.ifru_settings /* Device/proto settings*/ ++ ++/* ++ * Structure used in SIOCGIFCONF request. ++ * Used to retrieve interface configuration ++ * for machine (useful for programs which ++ * must know all networks accessible). ++ */ ++ ++/* for compatibility with glibc net/if.h */ ++#if __UAPI_DEF_IF_IFCONF ++struct ifconf { ++ int ifc_len; /* size of buffer */ ++ union { ++ char *ifcu_buf; ++ struct ifreq *ifcu_req; ++ } ifc_ifcu; ++}; ++#endif /* __UAPI_DEF_IF_IFCONF */ ++ ++#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */ ++#define ifc_req ifc_ifcu.ifcu_req /* array of structures */ ++ ++#endif /* _LINUX_IF_H */ +diff -ruN a/linux/if.h.rej b/linux/if.h.rej +--- a/linux/if.h.rej 1970-01-01 01:00:00.000000000 +0100 ++++ b/linux/if.h.rej 2024-02-25 21:26:04.178635811 +0100 +@@ -0,0 +1,100 @@ ++--- linux/if.h +++++ linux/if.h ++@@ -19,14 +19,20 @@ ++ #ifndef _LINUX_IF_H ++ #define _LINUX_IF_H ++ +++#include /* for compatibility with glibc */ ++ #include /* for "__kernel_caddr_t" et al */ ++ #include /* for "struct sockaddr" et al */ ++ /* for "__user" et al */ ++ +++#if __UAPI_DEF_IF_IFNAMSIZ ++ #define IFNAMSIZ 16 +++#endif /* __UAPI_DEF_IF_IFNAMSIZ */ ++ #define IFALIASZ 256 ++ #include ++ +++/* For glibc compatibility. An empty enum does not compile. */ +++#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO != 0 || \ +++ __UAPI_DEF_IF_NET_DEVICE_FLAGS != 0 ++ /** ++ * enum net_device_flags - &struct net_device flags ++ * ++@@ -68,6 +74,8 @@ ++ * @IFF_ECHO: echo sent packets. Volatile. ++ */ ++ enum net_device_flags { +++/* for compatibility with glibc net/if.h */ +++#if __UAPI_DEF_IF_NET_DEVICE_FLAGS ++ IFF_UP = 1<<0, /* sysfs */ ++ IFF_BROADCAST = 1<<1, /* __volatile__ */ ++ IFF_DEBUG = 1<<2, /* sysfs */ ++@@ -84,11 +92,17 @@ enum net_device_flags { ++ IFF_PORTSEL = 1<<13, /* sysfs */ ++ IFF_AUTOMEDIA = 1<<14, /* sysfs */ ++ IFF_DYNAMIC = 1<<15, /* sysfs */ +++#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */ +++#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO ++ IFF_LOWER_UP = 1<<16, /* __volatile__ */ ++ IFF_DORMANT = 1<<17, /* __volatile__ */ ++ IFF_ECHO = 1<<18, /* __volatile__ */ +++#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ ++ }; +++#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO != 0 || __UAPI_DEF_IF_NET_DEVICE_FLAGS != 0 */ ++ +++/* for compatibility with glibc net/if.h */ +++#if __UAPI_DEF_IF_NET_DEVICE_FLAGS ++ #define IFF_UP IFF_UP ++ #define IFF_BROADCAST IFF_BROADCAST ++ #define IFF_DEBUG IFF_DEBUG ++@@ -105,9 +119,13 @@ enum net_device_flags { ++ #define IFF_PORTSEL IFF_PORTSEL ++ #define IFF_AUTOMEDIA IFF_AUTOMEDIA ++ #define IFF_DYNAMIC IFF_DYNAMIC +++#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */ +++ +++#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO ++ #define IFF_LOWER_UP IFF_LOWER_UP ++ #define IFF_DORMANT IFF_DORMANT ++ #define IFF_ECHO IFF_ECHO +++#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ ++ ++ #define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\ ++ IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT) ++@@ -166,6 +184,8 @@ enum { ++ * being very small might be worth keeping for clean configuration. ++ */ ++ +++/* for compatibility with glibc net/if.h */ +++#if __UAPI_DEF_IF_IFMAP ++ struct ifmap { ++ unsigned long mem_start; ++ unsigned long mem_end; ++@@ -201,6 +222,8 @@ struct if_settings { ++ * remainder may be interface specific. ++ */ ++ +++/* for compatibility with glibc net/if.h */ +++#if __UAPI_DEF_IF_IFREQ ++ struct ifreq { ++ #define IFHWADDRLEN 6 ++ union ++@@ -251,6 +275,8 @@ struct ifreq { ++ * must know all networks accessible). ++ */ ++ +++/* for compatibility with glibc net/if.h */ +++#if __UAPI_DEF_IF_IFCONF ++ struct ifconf { ++ int ifc_len; /* size of buffer */ ++ union { ++@@ -258,6 +284,8 @@ struct ifconf { ++ struct ifreq *ifcu_req; ++ } ifc_ifcu; ++ }; +++#endif /* __UAPI_DEF_IF_IFCONF */ +++ ++ #define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */ ++ #define ifc_req ifc_ifcu.ifcu_req /* array of structures */ ++ +diff -ruN a/linux/kernel.h.orig b/linux/kernel.h.orig +--- a/linux/kernel.h.orig 1970-01-01 01:00:00.000000000 +0100 ++++ b/linux/kernel.h.orig 2024-02-25 21:23:53.960928824 +0100 +@@ -0,0 +1,8 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++#ifndef _LINUX_KERNEL_H ++#define _LINUX_KERNEL_H ++ ++#include ++#include ++ ++#endif /* _LINUX_KERNEL_H */ +diff -ruN a/linux/kernel.h.rej b/linux/kernel.h.rej +--- a/linux/kernel.h.rej 1970-01-01 01:00:00.000000000 +0100 ++++ b/linux/kernel.h.rej 2024-02-25 21:23:53.960928824 +0100 +@@ -0,0 +1,11 @@ ++--- linux/kernel.h +++++ linux/kernel.h ++@@ -1,8 +1,6 @@ ++ #ifndef _LINUX_KERNEL_H ++ #define _LINUX_KERNEL_H ++ ++-#include ++- ++ /* ++ * 'kernel.h' contains some often-used function prototypes etc ++ */ +diff -ruN a/linux/libc-compat.h b/linux/libc-compat.h +--- a/linux/libc-compat.h 2024-02-25 19:26:16.004079143 +0100 ++++ b/linux/libc-compat.h 2024-02-25 21:26:04.178635811 +0100 +@@ -49,8 +49,7 @@ + #ifndef _LIBC_COMPAT_H + #define _LIBC_COMPAT_H + +-/* We have included glibc headers... */ +-#if defined(__GLIBC__) ++#ifndef __KERNEL__ /* we're used from userspace */ + + /* Coordinate with glibc net/if.h header. */ + #if defined(_NET_IF_H) && defined(__USE_MISC) +@@ -86,6 +85,52 @@ + + #endif /* _NET_IF_H */ + ++#ifdef _NETINET_IF_ETHER_H /* musl */ ++#define __UAPI_DEF_ETHHDR 0 ++#else /* glibc uses __NETINET_IF_ETHER_H, and includes the kernel header. */ ++#define __UAPI_DEF_ETHHDR 1 ++#endif ++ ++#ifdef _NETINET_TCP_H /* musl */ ++#define __UAPI_DEF_TCPHDR 0 ++#else ++#define __UAPI_DEF_TCPHDR 1 ++#endif ++ ++#ifdef _TIME_H /* musl */ ++#define __UAPI_DEF_TIMESPEC 0 ++#define __UAPI_DEF_ITIMERSPEC 0 ++#else ++#define __UAPI_DEF_TIMESPEC 1 ++#define __UAPI_DEF_ITIMERSPEC 1 ++#endif ++ ++#ifdef _SYS_TIME_H /* musl */ ++#define __UAPI_DEF_TIMEVAL 0 ++#define __UAPI_DEF_ITIMERVAL 0 ++#define __UAPI_DEF_TIMEZONE 0 ++#else ++#define __UAPI_DEF_TIMEVAL 1 ++#define __UAPI_DEF_ITIMERVAL 1 ++#define __UAPI_DEF_TIMEZONE 1 ++#endif ++ ++#ifdef _NET_IF_H /* musl */ ++#define __UAPI_DEF_IF_IFNAMSIZ 0 ++#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 0 ++#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 0 ++#define __UAPI_DEF_IF_IFMAP 0 ++#define __UAPI_DEF_IF_IFREQ 0 ++#define __UAPI_DEF_IF_IFCONF 0 ++#else ++#define __UAPI_DEF_IF_IFNAMSIZ 1 ++#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 ++#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1 ++#define __UAPI_DEF_IF_IFMAP 1 ++#define __UAPI_DEF_IF_IFREQ 1 ++#define __UAPI_DEF_IF_IFCONF 1 ++#endif ++ + /* Coordinate with glibc netinet/in.h header. */ + #if defined(_NETINET_IN_H) + +@@ -103,11 +148,7 @@ + * if the glibc code didn't define them. This guard matches + * the guard in glibc/inet/netinet/in.h which defines the + * additional in6_addr macros e.g. s6_addr16, and s6_addr32. */ +-#if defined(__USE_MISC) || defined (__USE_GNU) + #define __UAPI_DEF_IN6_ADDR_ALT 0 +-#else +-#define __UAPI_DEF_IN6_ADDR_ALT 1 +-#endif + #define __UAPI_DEF_SOCKADDR_IN6 0 + #define __UAPI_DEF_IPV6_MREQ 0 + #define __UAPI_DEF_IPPROTO_V6 0 +@@ -262,6 +303,22 @@ + #define __UAPI_DEF_XATTR 1 + #endif + ++#define __UAPI_DEF_TCPHDR 1 ++#define __UAPI_DEF_ETHHDR 1 ++ ++#define __UAPI_DEF_TIMESPEC 1 ++#define __UAPI_DEF_ITIMERSPEC 1 ++#define __UAPI_DEF_TIMEVAL 1 ++#define __UAPI_DEF_ITIMERVAL 1 ++#define __UAPI_DEF_TIMEZONE 1 ++ ++#define __UAPI_DEF_IF_IFNAMSIZ 1 ++#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 ++#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1 ++#define __UAPI_DEF_IF_IFMAP 1 ++#define __UAPI_DEF_IF_IFREQ 1 ++#define __UAPI_DEF_IF_IFCONF 1 ++ + #endif /* __GLIBC__ */ + + #endif /* _LIBC_COMPAT_H */ +diff -ruN a/linux/libc-compat.h.orig b/linux/libc-compat.h.orig +--- a/linux/libc-compat.h.orig 1970-01-01 01:00:00.000000000 +0100 ++++ b/linux/libc-compat.h.orig 2024-02-25 21:25:54.886514226 +0100 +@@ -0,0 +1,303 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++/* ++ * Compatibility interface for userspace libc header coordination: ++ * ++ * Define compatibility macros that are used to control the inclusion or ++ * exclusion of UAPI structures and definitions in coordination with another ++ * userspace C library. ++ * ++ * This header is intended to solve the problem of UAPI definitions that ++ * conflict with userspace definitions. If a UAPI header has such conflicting ++ * definitions then the solution is as follows: ++ * ++ * * Synchronize the UAPI header and the libc headers so either one can be ++ * used and such that the ABI is preserved. If this is not possible then ++ * no simple compatibility interface exists (you need to write translating ++ * wrappers and rename things) and you can't use this interface. ++ * ++ * Then follow this process: ++ * ++ * (a) Include libc-compat.h in the UAPI header. ++ * e.g. #include ++ * This include must be as early as possible. ++ * ++ * (b) In libc-compat.h add enough code to detect that the comflicting ++ * userspace libc header has been included first. ++ * ++ * (c) If the userspace libc header has been included first define a set of ++ * guard macros of the form __UAPI_DEF_FOO and set their values to 1, else ++ * set their values to 0. ++ * ++ * (d) Back in the UAPI header with the conflicting definitions, guard the ++ * definitions with: ++ * #if __UAPI_DEF_FOO ++ * ... ++ * #endif ++ * ++ * This fixes the situation where the linux headers are included *after* the ++ * libc headers. To fix the problem with the inclusion in the other order the ++ * userspace libc headers must be fixed like this: ++ * ++ * * For all definitions that conflict with kernel definitions wrap those ++ * defines in the following: ++ * #if !__UAPI_DEF_FOO ++ * ... ++ * #endif ++ * ++ * This prevents the redefinition of a construct already defined by the kernel. ++ */ ++#ifndef _LIBC_COMPAT_H ++#define _LIBC_COMPAT_H ++ ++#ifndef __KERNEL__ /* we're used from userspace */ ++ ++/* Coordinate with glibc net/if.h header. */ ++#if defined(_NET_IF_H) && defined(__USE_MISC) ++ ++/* GLIBC headers included first so don't define anything ++ * that would already be defined. */ ++ ++#define __UAPI_DEF_IF_IFCONF 0 ++#define __UAPI_DEF_IF_IFMAP 0 ++#define __UAPI_DEF_IF_IFNAMSIZ 0 ++#define __UAPI_DEF_IF_IFREQ 0 ++/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */ ++#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 0 ++/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */ ++#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO ++#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 ++#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ ++ ++#else /* _NET_IF_H */ ++ ++/* Linux headers included first, and we must define everything ++ * we need. The expectation is that glibc will check the ++ * __UAPI_DEF_* defines and adjust appropriately. */ ++ ++#define __UAPI_DEF_IF_IFCONF 1 ++#define __UAPI_DEF_IF_IFMAP 1 ++#define __UAPI_DEF_IF_IFNAMSIZ 1 ++#define __UAPI_DEF_IF_IFREQ 1 ++/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */ ++#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1 ++/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */ ++#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 ++ ++#endif /* _NET_IF_H */ ++ ++#ifdef _NETINET_IF_ETHER_H /* musl */ ++#define __UAPI_DEF_ETHHDR 0 ++#else /* glibc uses __NETINET_IF_ETHER_H, and includes the kernel header. */ ++#define __UAPI_DEF_ETHHDR 1 ++#endif ++ ++#ifdef _NETINET_TCP_H /* musl */ ++#define __UAPI_DEF_TCPHDR 0 ++#else ++#define __UAPI_DEF_TCPHDR 1 ++#endif ++ ++#ifdef _TIME_H /* musl */ ++#define __UAPI_DEF_TIMESPEC 0 ++#define __UAPI_DEF_ITIMERSPEC 0 ++#else ++#define __UAPI_DEF_TIMESPEC 1 ++#define __UAPI_DEF_ITIMERSPEC 1 ++#endif ++ ++#ifdef _SYS_TIME_H /* musl */ ++#define __UAPI_DEF_TIMEVAL 0 ++#define __UAPI_DEF_ITIMERVAL 0 ++#define __UAPI_DEF_TIMEZONE 0 ++#else ++#define __UAPI_DEF_TIMEVAL 1 ++#define __UAPI_DEF_ITIMERVAL 1 ++#define __UAPI_DEF_TIMEZONE 1 ++#endif ++ ++ ++/* Coordinate with glibc netinet/in.h header. */ ++#if defined(_NETINET_IN_H) ++ ++/* GLIBC headers included first so don't define anything ++ * that would already be defined. */ ++#define __UAPI_DEF_IN_ADDR 0 ++#define __UAPI_DEF_IN_IPPROTO 0 ++#define __UAPI_DEF_IN_PKTINFO 0 ++#define __UAPI_DEF_IP_MREQ 0 ++#define __UAPI_DEF_SOCKADDR_IN 0 ++#define __UAPI_DEF_IN_CLASS 0 ++ ++#define __UAPI_DEF_IN6_ADDR 0 ++/* The exception is the in6_addr macros which must be defined ++ * if the glibc code didn't define them. This guard matches ++ * the guard in glibc/inet/netinet/in.h which defines the ++ * additional in6_addr macros e.g. s6_addr16, and s6_addr32. */ ++#define __UAPI_DEF_IN6_ADDR_ALT 0 ++#define __UAPI_DEF_SOCKADDR_IN6 0 ++#define __UAPI_DEF_IPV6_MREQ 0 ++#define __UAPI_DEF_IPPROTO_V6 0 ++#define __UAPI_DEF_IPV6_OPTIONS 0 ++#define __UAPI_DEF_IN6_PKTINFO 0 ++#define __UAPI_DEF_IP6_MTUINFO 0 ++ ++#else ++ ++/* Linux headers included first, and we must define everything ++ * we need. The expectation is that glibc will check the ++ * __UAPI_DEF_* defines and adjust appropriately. */ ++#define __UAPI_DEF_IN_ADDR 1 ++#define __UAPI_DEF_IN_IPPROTO 1 ++#define __UAPI_DEF_IN_PKTINFO 1 ++#define __UAPI_DEF_IP_MREQ 1 ++#define __UAPI_DEF_SOCKADDR_IN 1 ++#define __UAPI_DEF_IN_CLASS 1 ++ ++#define __UAPI_DEF_IN6_ADDR 1 ++/* We unconditionally define the in6_addr macros and glibc must ++ * coordinate. */ ++#define __UAPI_DEF_IN6_ADDR_ALT 1 ++#define __UAPI_DEF_SOCKADDR_IN6 1 ++#define __UAPI_DEF_IPV6_MREQ 1 ++#define __UAPI_DEF_IPPROTO_V6 1 ++#define __UAPI_DEF_IPV6_OPTIONS 1 ++#define __UAPI_DEF_IN6_PKTINFO 1 ++#define __UAPI_DEF_IP6_MTUINFO 1 ++ ++#endif /* _NETINET_IN_H */ ++ ++/* Coordinate with glibc netipx/ipx.h header. */ ++#if defined(__NETIPX_IPX_H) ++ ++#define __UAPI_DEF_SOCKADDR_IPX 0 ++#define __UAPI_DEF_IPX_ROUTE_DEFINITION 0 ++#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 0 ++#define __UAPI_DEF_IPX_CONFIG_DATA 0 ++#define __UAPI_DEF_IPX_ROUTE_DEF 0 ++ ++#else /* defined(__NETIPX_IPX_H) */ ++ ++#define __UAPI_DEF_SOCKADDR_IPX 1 ++#define __UAPI_DEF_IPX_ROUTE_DEFINITION 1 ++#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 1 ++#define __UAPI_DEF_IPX_CONFIG_DATA 1 ++#define __UAPI_DEF_IPX_ROUTE_DEF 1 ++ ++#endif /* defined(__NETIPX_IPX_H) */ ++ ++/* Definitions for xattr.h */ ++#if defined(_SYS_XATTR_H) ++#define __UAPI_DEF_XATTR 0 ++#else ++#define __UAPI_DEF_XATTR 1 ++#endif ++ ++/* If we did not see any headers from any supported C libraries, ++ * or we are being included in the kernel, then define everything ++ * that we need. Check for previous __UAPI_* definitions to give ++ * unsupported C libraries a way to opt out of any kernel definition. */ ++#else /* !defined(__GLIBC__) */ ++ ++/* Definitions for if.h */ ++#ifndef __UAPI_DEF_IF_IFCONF ++#define __UAPI_DEF_IF_IFCONF 1 ++#endif ++#ifndef __UAPI_DEF_IF_IFMAP ++#define __UAPI_DEF_IF_IFMAP 1 ++#endif ++#ifndef __UAPI_DEF_IF_IFNAMSIZ ++#define __UAPI_DEF_IF_IFNAMSIZ 1 ++#endif ++#ifndef __UAPI_DEF_IF_IFREQ ++#define __UAPI_DEF_IF_IFREQ 1 ++#endif ++/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */ ++#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS ++#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1 ++#endif ++/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */ ++#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO ++#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 ++#endif ++ ++/* Definitions for in.h */ ++#ifndef __UAPI_DEF_IN_ADDR ++#define __UAPI_DEF_IN_ADDR 1 ++#endif ++#ifndef __UAPI_DEF_IN_IPPROTO ++#define __UAPI_DEF_IN_IPPROTO 1 ++#endif ++#ifndef __UAPI_DEF_IN_PKTINFO ++#define __UAPI_DEF_IN_PKTINFO 1 ++#endif ++#ifndef __UAPI_DEF_IP_MREQ ++#define __UAPI_DEF_IP_MREQ 1 ++#endif ++#ifndef __UAPI_DEF_SOCKADDR_IN ++#define __UAPI_DEF_SOCKADDR_IN 1 ++#endif ++#ifndef __UAPI_DEF_IN_CLASS ++#define __UAPI_DEF_IN_CLASS 1 ++#endif ++ ++/* Definitions for in6.h */ ++#ifndef __UAPI_DEF_IN6_ADDR ++#define __UAPI_DEF_IN6_ADDR 1 ++#endif ++#ifndef __UAPI_DEF_IN6_ADDR_ALT ++#define __UAPI_DEF_IN6_ADDR_ALT 1 ++#endif ++#ifndef __UAPI_DEF_SOCKADDR_IN6 ++#define __UAPI_DEF_SOCKADDR_IN6 1 ++#endif ++#ifndef __UAPI_DEF_IPV6_MREQ ++#define __UAPI_DEF_IPV6_MREQ 1 ++#endif ++#ifndef __UAPI_DEF_IPPROTO_V6 ++#define __UAPI_DEF_IPPROTO_V6 1 ++#endif ++#ifndef __UAPI_DEF_IPV6_OPTIONS ++#define __UAPI_DEF_IPV6_OPTIONS 1 ++#endif ++#ifndef __UAPI_DEF_IN6_PKTINFO ++#define __UAPI_DEF_IN6_PKTINFO 1 ++#endif ++#ifndef __UAPI_DEF_IP6_MTUINFO ++#define __UAPI_DEF_IP6_MTUINFO 1 ++#endif ++ ++/* Definitions for ipx.h */ ++#ifndef __UAPI_DEF_SOCKADDR_IPX ++#define __UAPI_DEF_SOCKADDR_IPX 1 ++#endif ++#ifndef __UAPI_DEF_IPX_ROUTE_DEFINITION ++#define __UAPI_DEF_IPX_ROUTE_DEFINITION 1 ++#endif ++#ifndef __UAPI_DEF_IPX_INTERFACE_DEFINITION ++#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 1 ++#endif ++#ifndef __UAPI_DEF_IPX_CONFIG_DATA ++#define __UAPI_DEF_IPX_CONFIG_DATA 1 ++#endif ++#ifndef __UAPI_DEF_IPX_ROUTE_DEF ++#define __UAPI_DEF_IPX_ROUTE_DEF 1 ++#endif ++ ++/* Definitions for xattr.h */ ++#ifndef __UAPI_DEF_XATTR ++#define __UAPI_DEF_XATTR 1 ++#endif ++ ++#define __UAPI_DEF_TCPHDR 1 ++#define __UAPI_DEF_ETHHDR 1 ++ ++#define __UAPI_DEF_TIMESPEC 1 ++#define __UAPI_DEF_ITIMERSPEC 1 ++#define __UAPI_DEF_TIMEVAL 1 ++#define __UAPI_DEF_ITIMERVAL 1 ++#define __UAPI_DEF_TIMEZONE 1 ++ ++ ++#endif /* __GLIBC__ */ ++ ++#endif /* _LIBC_COMPAT_H */ +diff -ruN a/linux/libc-compat.h.rej b/linux/libc-compat.h.rej +--- a/linux/libc-compat.h.rej 1970-01-01 01:00:00.000000000 +0100 ++++ b/linux/libc-compat.h.rej 2024-02-25 21:25:18.614039299 +0100 +@@ -0,0 +1,15 @@ ++--- linux/libc-compat.h +++++ linux/libc-compat.h ++@@ -56,6 +56,12 @@ ++ #define __UAPI_DEF_ETHHDR 1 ++ #endif ++ +++#ifdef _NETINET_TCP_H /* musl */ +++#define __UAPI_DEF_TCPHDR 0 +++#else +++#define __UAPI_DEF_TCPHDR 1 +++#endif +++ ++ /* Coordinate with glibc netinet/in.h header. */ ++ #if defined(_NETINET_IN_H) ++ +diff -ruN a/linux/stat.h b/linux/stat.h +--- a/linux/stat.h 2024-02-25 19:26:20.004129543 +0100 ++++ b/linux/stat.h 2024-02-25 21:23:41.940770885 +0100 +@@ -4,7 +4,7 @@ + + #include + +-#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) ++#if defined(__KERNEL__) || !defined(S_ISLNK) + + #define S_IFMT 00170000 + #define S_IFSOCK 0140000 +diff -ruN a/linux/stat.h.orig b/linux/stat.h.orig +--- a/linux/stat.h.orig 1970-01-01 01:00:00.000000000 +0100 ++++ b/linux/stat.h.orig 2024-02-25 20:39:49.635133283 +0100 +@@ -0,0 +1,192 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++#ifndef _LINUX_STAT_H ++#define _LINUX_STAT_H ++ ++#include ++ ++#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) ++ ++#define S_IFMT 00170000 ++#define S_IFSOCK 0140000 ++#define S_IFLNK 0120000 ++#define S_IFREG 0100000 ++#define S_IFBLK 0060000 ++#define S_IFDIR 0040000 ++#define S_IFCHR 0020000 ++#define S_IFIFO 0010000 ++#define S_ISUID 0004000 ++#define S_ISGID 0002000 ++#define S_ISVTX 0001000 ++ ++#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) ++#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) ++#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) ++#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) ++#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) ++#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) ++#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) ++ ++#define S_IRWXU 00700 ++#define S_IRUSR 00400 ++#define S_IWUSR 00200 ++#define S_IXUSR 00100 ++ ++#define S_IRWXG 00070 ++#define S_IRGRP 00040 ++#define S_IWGRP 00020 ++#define S_IXGRP 00010 ++ ++#define S_IRWXO 00007 ++#define S_IROTH 00004 ++#define S_IWOTH 00002 ++#define S_IXOTH 00001 ++ ++#endif ++ ++/* ++ * Timestamp structure for the timestamps in struct statx. ++ * ++ * tv_sec holds the number of seconds before (negative) or after (positive) ++ * 00:00:00 1st January 1970 UTC. ++ * ++ * tv_nsec holds a number of nanoseconds (0..999,999,999) after the tv_sec time. ++ * ++ * __reserved is held in case we need a yet finer resolution. ++ */ ++struct statx_timestamp { ++ __s64 tv_sec; ++ __u32 tv_nsec; ++ __s32 __reserved; ++}; ++ ++/* ++ * Structures for the extended file attribute retrieval system call ++ * (statx()). ++ * ++ * The caller passes a mask of what they're specifically interested in as a ++ * parameter to statx(). What statx() actually got will be indicated in ++ * st_mask upon return. ++ * ++ * For each bit in the mask argument: ++ * ++ * - if the datum is not supported: ++ * ++ * - the bit will be cleared, and ++ * ++ * - the datum will be set to an appropriate fabricated value if one is ++ * available (eg. CIFS can take a default uid and gid), otherwise ++ * ++ * - the field will be cleared; ++ * ++ * - otherwise, if explicitly requested: ++ * ++ * - the datum will be synchronised to the server if AT_STATX_FORCE_SYNC is ++ * set or if the datum is considered out of date, and ++ * ++ * - the field will be filled in and the bit will be set; ++ * ++ * - otherwise, if not requested, but available in approximate form without any ++ * effort, it will be filled in anyway, and the bit will be set upon return ++ * (it might not be up to date, however, and no attempt will be made to ++ * synchronise the internal state first); ++ * ++ * - otherwise the field and the bit will be cleared before returning. ++ * ++ * Items in STATX_BASIC_STATS may be marked unavailable on return, but they ++ * will have values installed for compatibility purposes so that stat() and ++ * co. can be emulated in userspace. ++ */ ++struct statx { ++ /* 0x00 */ ++ __u32 stx_mask; /* What results were written [uncond] */ ++ __u32 stx_blksize; /* Preferred general I/O size [uncond] */ ++ __u64 stx_attributes; /* Flags conveying information about the file [uncond] */ ++ /* 0x10 */ ++ __u32 stx_nlink; /* Number of hard links */ ++ __u32 stx_uid; /* User ID of owner */ ++ __u32 stx_gid; /* Group ID of owner */ ++ __u16 stx_mode; /* File mode */ ++ __u16 __spare0[1]; ++ /* 0x20 */ ++ __u64 stx_ino; /* Inode number */ ++ __u64 stx_size; /* File size */ ++ __u64 stx_blocks; /* Number of 512-byte blocks allocated */ ++ __u64 stx_attributes_mask; /* Mask to show what's supported in stx_attributes */ ++ /* 0x40 */ ++ struct statx_timestamp stx_atime; /* Last access time */ ++ struct statx_timestamp stx_btime; /* File creation time */ ++ struct statx_timestamp stx_ctime; /* Last attribute change time */ ++ struct statx_timestamp stx_mtime; /* Last data modification time */ ++ /* 0x80 */ ++ __u32 stx_rdev_major; /* Device ID of special file [if bdev/cdev] */ ++ __u32 stx_rdev_minor; ++ __u32 stx_dev_major; /* ID of device containing file [uncond] */ ++ __u32 stx_dev_minor; ++ /* 0x90 */ ++ __u64 stx_mnt_id; ++ __u32 stx_dio_mem_align; /* Memory buffer alignment for direct I/O */ ++ __u32 stx_dio_offset_align; /* File offset alignment for direct I/O */ ++ /* 0xa0 */ ++ __u64 __spare3[12]; /* Spare space for future expansion */ ++ /* 0x100 */ ++}; ++ ++/* ++ * Flags to be stx_mask ++ * ++ * Query request/result mask for statx() and struct statx::stx_mask. ++ * ++ * These bits should be set in the mask argument of statx() to request ++ * particular items when calling statx(). ++ */ ++#define STATX_TYPE 0x00000001U /* Want/got stx_mode & S_IFMT */ ++#define STATX_MODE 0x00000002U /* Want/got stx_mode & ~S_IFMT */ ++#define STATX_NLINK 0x00000004U /* Want/got stx_nlink */ ++#define STATX_UID 0x00000008U /* Want/got stx_uid */ ++#define STATX_GID 0x00000010U /* Want/got stx_gid */ ++#define STATX_ATIME 0x00000020U /* Want/got stx_atime */ ++#define STATX_MTIME 0x00000040U /* Want/got stx_mtime */ ++#define STATX_CTIME 0x00000080U /* Want/got stx_ctime */ ++#define STATX_INO 0x00000100U /* Want/got stx_ino */ ++#define STATX_SIZE 0x00000200U /* Want/got stx_size */ ++#define STATX_BLOCKS 0x00000400U /* Want/got stx_blocks */ ++#define STATX_BASIC_STATS 0x000007ffU /* The stuff in the normal stat struct */ ++#define STATX_BTIME 0x00000800U /* Want/got stx_btime */ ++#define STATX_MNT_ID 0x00001000U /* Got stx_mnt_id */ ++#define STATX_DIOALIGN 0x00002000U /* Want/got direct I/O alignment info */ ++ ++#define STATX__RESERVED 0x80000000U /* Reserved for future struct statx expansion */ ++ ++/* ++ * This is deprecated, and shall remain the same value in the future. To avoid ++ * confusion please use the equivalent (STATX_BASIC_STATS | STATX_BTIME) ++ * instead. ++ */ ++#define STATX_ALL 0x00000fffU ++ ++/* ++ * Attributes to be found in stx_attributes and masked in stx_attributes_mask. ++ * ++ * These give information about the features or the state of a file that might ++ * be of use to ordinary userspace programs such as GUIs or ls rather than ++ * specialised tools. ++ * ++ * Note that the flags marked [I] correspond to the FS_IOC_SETFLAGS flags ++ * semantically. Where possible, the numerical value is picked to correspond ++ * also. Note that the DAX attribute indicates that the file is in the CPU ++ * direct access state. It does not correspond to the per-inode flag that ++ * some filesystems support. ++ * ++ */ ++#define STATX_ATTR_COMPRESSED 0x00000004 /* [I] File is compressed by the fs */ ++#define STATX_ATTR_IMMUTABLE 0x00000010 /* [I] File is marked immutable */ ++#define STATX_ATTR_APPEND 0x00000020 /* [I] File is append-only */ ++#define STATX_ATTR_NODUMP 0x00000040 /* [I] File is not to be dumped */ ++#define STATX_ATTR_ENCRYPTED 0x00000800 /* [I] File requires key to decrypt in fs */ ++#define STATX_ATTR_AUTOMOUNT 0x00001000 /* Dir: Automount trigger */ ++#define STATX_ATTR_MOUNT_ROOT 0x00002000 /* Root of a mount */ ++#define STATX_ATTR_VERITY 0x00100000 /* [I] Verity protected file */ ++#define STATX_ATTR_DAX 0x00200000 /* File is currently in DAX state */ ++ ++ ++#endif /* _LINUX_STAT_H */ +diff -ruN a/linux/tcp.h b/linux/tcp.h +--- a/linux/tcp.h 2024-02-25 19:26:16.364083679 +0100 ++++ b/linux/tcp.h 2024-02-25 21:24:29.185391271 +0100 +@@ -19,9 +19,11 @@ + #define _LINUX_TCP_H + + #include ++#include + #include + #include + ++#if __UAPI_DEF_TCPHDR + struct tcphdr { + __be16 source; + __be16 dest; +@@ -56,6 +58,7 @@ + __sum16 check; + __be16 urg_ptr; + }; ++#endif + + /* + * The union cast uses a gcc extension to avoid aliasing problems +diff -ruN a/linux/tcp.h.orig b/linux/tcp.h.orig +--- a/linux/tcp.h.orig 1970-01-01 01:00:00.000000000 +0100 ++++ b/linux/tcp.h.orig 2024-02-25 20:39:49.627133187 +0100 +@@ -0,0 +1,368 @@ ++/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ ++/* ++ * INET An implementation of the TCP/IP protocol suite for the LINUX ++ * operating system. INET is implemented using the BSD Socket ++ * interface as the means of communication with the user level. ++ * ++ * Definitions for the TCP protocol. ++ * ++ * Version: @(#)tcp.h 1.0.2 04/28/93 ++ * ++ * Author: Fred N. van Kempen, ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++#ifndef _LINUX_TCP_H ++#define _LINUX_TCP_H ++ ++#include ++#include ++#include ++ ++struct tcphdr { ++ __be16 source; ++ __be16 dest; ++ __be32 seq; ++ __be32 ack_seq; ++#if defined(__LITTLE_ENDIAN_BITFIELD) ++ __u16 res1:4, ++ doff:4, ++ fin:1, ++ syn:1, ++ rst:1, ++ psh:1, ++ ack:1, ++ urg:1, ++ ece:1, ++ cwr:1; ++#elif defined(__BIG_ENDIAN_BITFIELD) ++ __u16 doff:4, ++ res1:4, ++ cwr:1, ++ ece:1, ++ urg:1, ++ ack:1, ++ psh:1, ++ rst:1, ++ syn:1, ++ fin:1; ++#else ++#error "Adjust your defines" ++#endif ++ __be16 window; ++ __sum16 check; ++ __be16 urg_ptr; ++}; ++ ++/* ++ * The union cast uses a gcc extension to avoid aliasing problems ++ * (union is compatible to any of its members) ++ * This means this part of the code is -fstrict-aliasing safe now. ++ */ ++union tcp_word_hdr { ++ struct tcphdr hdr; ++ __be32 words[5]; ++}; ++ ++#define tcp_flag_word(tp) (((union tcp_word_hdr *)(tp))->words[3]) ++ ++enum { ++ TCP_FLAG_CWR = __constant_cpu_to_be32(0x00800000), ++ TCP_FLAG_ECE = __constant_cpu_to_be32(0x00400000), ++ TCP_FLAG_URG = __constant_cpu_to_be32(0x00200000), ++ TCP_FLAG_ACK = __constant_cpu_to_be32(0x00100000), ++ TCP_FLAG_PSH = __constant_cpu_to_be32(0x00080000), ++ TCP_FLAG_RST = __constant_cpu_to_be32(0x00040000), ++ TCP_FLAG_SYN = __constant_cpu_to_be32(0x00020000), ++ TCP_FLAG_FIN = __constant_cpu_to_be32(0x00010000), ++ TCP_RESERVED_BITS = __constant_cpu_to_be32(0x0F000000), ++ TCP_DATA_OFFSET = __constant_cpu_to_be32(0xF0000000) ++}; ++ ++/* ++ * TCP general constants ++ */ ++#define TCP_MSS_DEFAULT 536U /* IPv4 (RFC1122, RFC2581) */ ++#define TCP_MSS_DESIRED 1220U /* IPv6 (tunneled), EDNS0 (RFC3226) */ ++ ++/* TCP socket options */ ++#define TCP_NODELAY 1 /* Turn off Nagle's algorithm. */ ++#define TCP_MAXSEG 2 /* Limit MSS */ ++#define TCP_CORK 3 /* Never send partially complete segments */ ++#define TCP_KEEPIDLE 4 /* Start keeplives after this period */ ++#define TCP_KEEPINTVL 5 /* Interval between keepalives */ ++#define TCP_KEEPCNT 6 /* Number of keepalives before death */ ++#define TCP_SYNCNT 7 /* Number of SYN retransmits */ ++#define TCP_LINGER2 8 /* Life time of orphaned FIN-WAIT-2 state */ ++#define TCP_DEFER_ACCEPT 9 /* Wake up listener only when data arrive */ ++#define TCP_WINDOW_CLAMP 10 /* Bound advertised window */ ++#define TCP_INFO 11 /* Information about this connection. */ ++#define TCP_QUICKACK 12 /* Block/reenable quick acks */ ++#define TCP_CONGESTION 13 /* Congestion control algorithm */ ++#define TCP_MD5SIG 14 /* TCP MD5 Signature (RFC2385) */ ++#define TCP_THIN_LINEAR_TIMEOUTS 16 /* Use linear timeouts for thin streams*/ ++#define TCP_THIN_DUPACK 17 /* Fast retrans. after 1 dupack */ ++#define TCP_USER_TIMEOUT 18 /* How long for loss retry before timeout */ ++#define TCP_REPAIR 19 /* TCP sock is under repair right now */ ++#define TCP_REPAIR_QUEUE 20 ++#define TCP_QUEUE_SEQ 21 ++#define TCP_REPAIR_OPTIONS 22 ++#define TCP_FASTOPEN 23 /* Enable FastOpen on listeners */ ++#define TCP_TIMESTAMP 24 ++#define TCP_NOTSENT_LOWAT 25 /* limit number of unsent bytes in write queue */ ++#define TCP_CC_INFO 26 /* Get Congestion Control (optional) info */ ++#define TCP_SAVE_SYN 27 /* Record SYN headers for new connections */ ++#define TCP_SAVED_SYN 28 /* Get SYN headers recorded for connection */ ++#define TCP_REPAIR_WINDOW 29 /* Get/set window parameters */ ++#define TCP_FASTOPEN_CONNECT 30 /* Attempt FastOpen with connect */ ++#define TCP_ULP 31 /* Attach a ULP to a TCP connection */ ++#define TCP_MD5SIG_EXT 32 /* TCP MD5 Signature with extensions */ ++#define TCP_FASTOPEN_KEY 33 /* Set the key for Fast Open (cookie) */ ++#define TCP_FASTOPEN_NO_COOKIE 34 /* Enable TFO without a TFO cookie */ ++#define TCP_ZEROCOPY_RECEIVE 35 ++#define TCP_INQ 36 /* Notify bytes available to read as a cmsg on read */ ++ ++#define TCP_CM_INQ TCP_INQ ++ ++#define TCP_TX_DELAY 37 /* delay outgoing packets by XX usec */ ++ ++ ++#define TCP_REPAIR_ON 1 ++#define TCP_REPAIR_OFF 0 ++#define TCP_REPAIR_OFF_NO_WP -1 /* Turn off without window probes */ ++ ++struct tcp_repair_opt { ++ __u32 opt_code; ++ __u32 opt_val; ++}; ++ ++struct tcp_repair_window { ++ __u32 snd_wl1; ++ __u32 snd_wnd; ++ __u32 max_window; ++ ++ __u32 rcv_wnd; ++ __u32 rcv_wup; ++}; ++ ++enum { ++ TCP_NO_QUEUE, ++ TCP_RECV_QUEUE, ++ TCP_SEND_QUEUE, ++ TCP_QUEUES_NR, ++}; ++ ++/* why fastopen failed from client perspective */ ++enum tcp_fastopen_client_fail { ++ TFO_STATUS_UNSPEC, /* catch-all */ ++ TFO_COOKIE_UNAVAILABLE, /* if not in TFO_CLIENT_NO_COOKIE mode */ ++ TFO_DATA_NOT_ACKED, /* SYN-ACK did not ack SYN data */ ++ TFO_SYN_RETRANSMITTED, /* SYN-ACK did not ack SYN data after timeout */ ++}; ++ ++/* for TCP_INFO socket option */ ++#define TCPI_OPT_TIMESTAMPS 1 ++#define TCPI_OPT_SACK 2 ++#define TCPI_OPT_WSCALE 4 ++#define TCPI_OPT_ECN 8 /* ECN was negociated at TCP session init */ ++#define TCPI_OPT_ECN_SEEN 16 /* we received at least one packet with ECT */ ++#define TCPI_OPT_SYN_DATA 32 /* SYN-ACK acked data in SYN sent or rcvd */ ++ ++/* ++ * Sender's congestion state indicating normal or abnormal situations ++ * in the last round of packets sent. The state is driven by the ACK ++ * information and timer events. ++ */ ++enum tcp_ca_state { ++ /* ++ * Nothing bad has been observed recently. ++ * No apparent reordering, packet loss, or ECN marks. ++ */ ++ TCP_CA_Open = 0, ++#define TCPF_CA_Open (1< +++#include ++ #include ++ #include ++ +++#if __UAPI_DEF_TCPHDR ++ struct tcphdr { ++ __be16 source; ++ __be16 dest; ++@@ -55,6 +57,7 @@ struct tcphdr { ++ __sum16 check; ++ __be16 urg_ptr; ++ }; +++#endif ++ ++ /* ++ * The union cast uses a gcc extension to avoid aliasing problems +diff -ruN a/linux/time.h b/linux/time.h +--- a/linux/time.h 2024-02-25 19:26:17.788101623 +0100 ++++ b/linux/time.h 2024-02-25 21:25:42.730355117 +0100 +@@ -68,6 +68,8 @@ + /* + * The various flags for setting POSIX.1b interval timers: + */ ++#ifndef TIMER_ABSTIME + #define TIMER_ABSTIME 0x01 ++#endif + + #endif /* _LINUX_TIME_H */ +diff -ruN a/linux/time.h.orig b/linux/time.h.orig +--- a/linux/time.h.orig 1970-01-01 01:00:00.000000000 +0100 ++++ b/linux/time.h.orig 2024-02-25 20:39:49.635133283 +0100 +@@ -0,0 +1,73 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++#ifndef _LINUX_TIME_H ++#define _LINUX_TIME_H ++ ++#include ++#include ++ ++#ifndef _STRUCT_TIMESPEC ++#define _STRUCT_TIMESPEC ++struct timespec { ++ __kernel_old_time_t tv_sec; /* seconds */ ++ long tv_nsec; /* nanoseconds */ ++}; ++#endif ++ ++struct timeval { ++ __kernel_old_time_t tv_sec; /* seconds */ ++ __kernel_suseconds_t tv_usec; /* microseconds */ ++}; ++ ++struct itimerspec { ++ struct timespec it_interval;/* timer period */ ++ struct timespec it_value; /* timer expiration */ ++}; ++ ++struct itimerval { ++ struct timeval it_interval;/* timer interval */ ++ struct timeval it_value; /* current value */ ++}; ++ ++struct timezone { ++ int tz_minuteswest; /* minutes west of Greenwich */ ++ int tz_dsttime; /* type of dst correction */ ++}; ++ ++/* ++ * Names of the interval timers, and structure ++ * defining a timer setting: ++ */ ++#define ITIMER_REAL 0 ++#define ITIMER_VIRTUAL 1 ++#define ITIMER_PROF 2 ++ ++/* ++ * The IDs of the various system clocks (for POSIX.1b interval timers): ++ */ ++#define CLOCK_REALTIME 0 ++#define CLOCK_MONOTONIC 1 ++#define CLOCK_PROCESS_CPUTIME_ID 2 ++#define CLOCK_THREAD_CPUTIME_ID 3 ++#define CLOCK_MONOTONIC_RAW 4 ++#define CLOCK_REALTIME_COARSE 5 ++#define CLOCK_MONOTONIC_COARSE 6 ++#define CLOCK_BOOTTIME 7 ++#define CLOCK_REALTIME_ALARM 8 ++#define CLOCK_BOOTTIME_ALARM 9 ++/* ++ * The driver implementing this got removed. The clock ID is kept as a ++ * place holder. Do not reuse! ++ */ ++#define CLOCK_SGI_CYCLE 10 ++#define CLOCK_TAI 11 ++ ++#define MAX_CLOCKS 16 ++#define CLOCKS_MASK (CLOCK_REALTIME | CLOCK_MONOTONIC) ++#define CLOCKS_MONO CLOCK_MONOTONIC ++ ++/* ++ * The various flags for setting POSIX.1b interval timers: ++ */ ++#define TIMER_ABSTIME 0x01 ++ ++#endif /* _LINUX_TIME_H */ +diff -ruN a/linux/time.h.rej b/linux/time.h.rej +--- a/linux/time.h.rej 1970-01-01 01:00:00.000000000 +0100 ++++ b/linux/time.h.rej 2024-02-25 21:25:42.730355117 +0100 +@@ -0,0 +1,55 @@ ++--- linux/time.h +++++ linux/time.h ++@@ -2,25 +2,32 @@ ++ #define _LINUX_TIME_H ++ ++ #include +++#include ++ ++ ++ #ifndef _STRUCT_TIMESPEC ++ #define _STRUCT_TIMESPEC +++#if __UAPI_DEF_TIMESPEC ++ struct timespec { ++ __kernel_time_t tv_sec; /* seconds */ ++ long tv_nsec; /* nanoseconds */ ++ }; ++ #endif +++#endif ++ +++#if __UAPI_DEF_TIMEVAL ++ struct timeval { ++ __kernel_time_t tv_sec; /* seconds */ ++ __kernel_suseconds_t tv_usec; /* microseconds */ ++ }; +++#endif ++ +++#if __UAPI_DEF_TIMEZONE ++ struct timezone { ++ int tz_minuteswest; /* minutes west of Greenwich */ ++ int tz_dsttime; /* type of dst correction */ ++ }; +++#endif ++ ++ ++ /* ++@@ -31,15 +38,19 @@ struct timezone { ++ #define ITIMER_VIRTUAL 1 ++ #define ITIMER_PROF 2 ++ +++#if __UAPI_DEF_ITIMERSPEC ++ struct itimerspec { ++ struct timespec it_interval; /* timer period */ ++ struct timespec it_value; /* timer expiration */ ++ }; +++#endif ++ +++#if __UAPI_DEF_ITIMERVAL ++ struct itimerval { ++ struct timeval it_interval; /* timer interval */ ++ struct timeval it_value; /* current value */ ++ }; +++#endif ++ ++ /* ++ * The IDs of the various system clocks (for POSIX.1b interval timers): diff --git a/patches/busybox/0001-NOMERGE-Hacks-to-build-Wasm-Linux-arch-minimal-and-i.patch b/patches/busybox/0001-NOMERGE-Hacks-to-build-Wasm-Linux-arch-minimal-and-i.patch new file mode 100644 index 0000000..5e342b0 --- /dev/null +++ b/patches/busybox/0001-NOMERGE-Hacks-to-build-Wasm-Linux-arch-minimal-and-i.patch @@ -0,0 +1,1826 @@ +From a9b9d0159ff10b3e13c0ae70de54e0ab8e424069 Mon Sep 17 00:00:00 2001 +From: Joel Severin +Date: Mon, 13 May 2024 23:44:37 +0200 +Subject: [PATCH] NOMERGE: Hacks to build Wasm/Linux arch (minimal and + incomplete) + +--- + Makefile | 16 +- + Makefile.flags | 19 +- + arch/wasm/Makefile | 0 + archival/libarchive/decompress_bunzip2.c | 6 +- + archival/libarchive/decompress_gunzip.c | 4 +- + archival/libarchive/open_transformer.c | 44 +- + configs/wasm_defconfig | 1231 ++++++++++++++++++++++ + coreutils/test.c | 6 +- + editors/vi.c | 3 + + init/init.c | 37 +- + scripts/Makefile.build | 2 +- + scripts/gcc-version.sh | 3 + + scripts/trylink | 8 +- + shell/hush.c | 137 ++- + util-linux/fdisk.c | 6 - + 15 files changed, 1392 insertions(+), 130 deletions(-) + create mode 100644 arch/wasm/Makefile + create mode 100644 configs/wasm_defconfig + +diff --git a/Makefile b/Makefile +index fa8cb76..8fad6ae 100644 +--- a/Makefile ++++ b/Makefile +@@ -288,15 +288,15 @@ MAKEFLAGS += -rR + + # Make variables (CC, etc...) + +-AS = $(CROSS_COMPILE)as +-CC = $(CROSS_COMPILE)gcc +-LD = $(CC) -nostdlib ++AS = $(CROSS_COMPILE)llvm-as ++CC = $(CROSS_COMPILE)clang --target=wasm32-unknown-unknown -Xclang -target-feature -Xclang +atomics -Xclang -target-feature -Xclang +bulk-memory ++LD = $(CROSS_COMPILE)wasm-ld + CPP = $(CC) -E +-AR = $(CROSS_COMPILE)ar +-NM = $(CROSS_COMPILE)nm +-STRIP = $(CROSS_COMPILE)strip +-OBJCOPY = $(CROSS_COMPILE)objcopy +-OBJDUMP = $(CROSS_COMPILE)objdump ++AR = $(CROSS_COMPILE)llvm-ar ++NM = $(CROSS_COMPILE)llvm-nm ++STRIP = $(CROSS_COMPILE)llvm-strip ++OBJCOPY = $(CROSS_COMPILE)llvm-objcopy ++OBJDUMP = $(CROSS_COMPILE)llvm-objdump + PKG_CONFIG ?= $(CROSS_COMPILE)pkg-config + AWK = awk + GENKSYMS = scripts/genksyms/genksyms +diff --git a/Makefile.flags b/Makefile.flags +index 1cec5ba..5014d21 100644 +--- a/Makefile.flags ++++ b/Makefile.flags +@@ -47,28 +47,15 @@ endif + # gcc 3.x emits bogus "old style proto" warning on find.c:alloc_action() + CFLAGS += $(call cc-ifversion, -ge, 0400, -Wold-style-definition) + +-ifneq ($(CC),clang) +-# "clang-9: warning: optimization flag '-finline-limit=0' is not supported +-CFLAGS += $(call cc-option,-finline-limit=0,) +-endif +- + CFLAGS += $(call cc-option,-fno-builtin-strlen -fomit-frame-pointer -ffunction-sections -fdata-sections,) + # -fno-guess-branch-probability: prohibit pseudo-random guessing + # of branch probabilities (hopefully makes bloatcheck more stable): + CFLAGS += $(call cc-option,-fno-guess-branch-probability,) + CFLAGS += $(call cc-option,-funsigned-char,) + +-ifeq ($(CONFIG_STATIC_LIBGCC),y) +-# Disable it, for example, if you get +-# "clang-9: warning: argument unused during compilation: '-static-libgcc'" +-CFLAGS += $(call cc-option,-static-libgcc,) +-endif +- + CFLAGS += $(call cc-option,-falign-functions=1,) +-ifneq ($(CC),clang) +-# "clang-9: warning: optimization flag '-falign-jumps=1' is not supported" (and same for other two) +-CFLAGS += $(call cc-option,-falign-jumps=1 -falign-labels=1 -falign-loops=1,) +-endif ++ ++CFLAGS += $(call cc-option,-falign-loops=1,) + + # Defeat .eh_frame bloat (gcc 4.6.3 x86-32 defconfig: 20% smaller busybox binary): + CFLAGS += $(call cc-option,-fno-unwind-tables,) +@@ -78,9 +65,7 @@ CFLAGS += $(call cc-option,-fno-asynchronous-unwind-tables,) + CFLAGS += $(call cc-option,-fno-builtin-printf,) + + # clang-9 does not like "str" + N and "if (CONFIG_ITEM && cond)" constructs +-ifeq ($(CC),clang) + CFLAGS += $(call cc-option,-Wno-string-plus-int -Wno-constant-logical-operand) +-endif + + # FIXME: These warnings are at least partially to be concerned about and should + # be fixed.. +diff --git a/arch/wasm/Makefile b/arch/wasm/Makefile +new file mode 100644 +index 0000000..e69de29 +diff --git a/archival/libarchive/decompress_bunzip2.c b/archival/libarchive/decompress_bunzip2.c +index 4a2b668..74676f0 100644 +--- a/archival/libarchive/decompress_bunzip2.c ++++ b/archival/libarchive/decompress_bunzip2.c +@@ -127,7 +127,7 @@ static unsigned get_bits(bunzip_data *bd, int bits_wanted) + /* if "no input fd" case: in_fd == -1, read fails, we jump */ + bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE); + if (bd->inbufCount <= 0) +- longjmp(*bd->jmpbuf, RETVAL_UNEXPECTED_INPUT_EOF); ++ abort(); + bd->inbufPos = 0; + } + +@@ -785,7 +785,7 @@ unpack_bz2_stream(transformer_state_t *xstate) + jmp_buf jmpbuf; + + /* Setup for I/O error handling via longjmp */ +- i = setjmp(jmpbuf); ++ i = 0; + if (i == 0) + i = start_bunzip(&jmpbuf, &bd, xstate->src_fd, outbuf + 2, len); + +@@ -856,7 +856,7 @@ unpack_bz2_data(const char *packed, int packed_len, int unpacked_len) + jmp_buf jmpbuf; + + /* Setup for I/O error handling via longjmp */ +- i = setjmp(jmpbuf); ++ i = 0; + if (i == 0) { + i = start_bunzip(&jmpbuf, + &bd, +diff --git a/archival/libarchive/decompress_gunzip.c b/archival/libarchive/decompress_gunzip.c +index d051ecb..77a1e88 100644 +--- a/archival/libarchive/decompress_gunzip.c ++++ b/archival/libarchive/decompress_gunzip.c +@@ -253,7 +253,7 @@ static void abort_unzip(STATE_PARAM_ONLY) NORETURN; + static void abort_unzip(STATE_PARAM_ONLY) + { + huft_free_all(PASS_STATE_ONLY); +- longjmp(error_jmp, 1); ++ abort(); + } + + static unsigned fill_bitbuffer(STATE_PARAM unsigned bitbuffer, unsigned *current, const unsigned required) +@@ -1024,7 +1024,7 @@ inflate_unzip_internal(STATE_PARAM transformer_state_t *xstate) + gunzip_crc = ~0; + + error_msg = "corrupted data"; +- if (setjmp(error_jmp)) { ++ if (0) { + /* Error from deep inside zip machinery */ + bb_simple_error_msg(error_msg); + n = -1; +diff --git a/archival/libarchive/open_transformer.c b/archival/libarchive/open_transformer.c +index 44715ef..d311750 100644 +--- a/archival/libarchive/open_transformer.c ++++ b/archival/libarchive/open_transformer.c +@@ -4,6 +4,7 @@ + */ + #include "libbb.h" + #include "bb_archive.h" ++#include + + void FAST_FUNC init_transformer_state(transformer_state_t *xstate) + { +@@ -102,6 +103,28 @@ void FAST_FUNC fork_transformer(int fd, + IF_DESKTOP(long long) int FAST_FUNC (*transformer)(transformer_state_t *xstate) + ) + #else ++static int fork_transformer_child(void* arg) ++{ ++ void **args = arg; ++ int fd = *(int*)args[0]; ++ const char *transform_prog = args[1]; ++ struct fd_pair *fd_pipe = args[2]; ++ ++ close(fd_pipe->rd); /* we don't want to read from the parent */ ++ // FIXME: error check? ++ ++ char *argv[4]; ++ xmove_fd(fd, 0); ++ xmove_fd(fd_pipe->wr, 1); ++ argv[0] = (char*)transform_prog; ++ argv[1] = (char*)"-cf"; ++ argv[2] = (char*)"-"; ++ argv[3] = NULL; ++ BB_EXECVP(transform_prog, argv); ++ bb_perror_msg_and_die("can't execute '%s'", transform_prog); ++ ++ return 0; ++} + void FAST_FUNC fork_transformer(int fd, const char *transform_prog) + #endif + { +@@ -109,12 +132,12 @@ void FAST_FUNC fork_transformer(int fd, const char *transform_prog) + int pid; + + xpiped_pair(fd_pipe); +- pid = BB_MMU ? xfork() : xvfork(); ++#if BB_MMU ++ pid = xfork(); + if (pid == 0) { + /* Child */ + close(fd_pipe.rd); /* we don't want to read from the parent */ + // FIXME: error check? +-#if BB_MMU + { + IF_DESKTOP(long long) int r; + transformer_state_t xstate; +@@ -130,21 +153,12 @@ void FAST_FUNC fork_transformer(int fd, const char *transform_prog) + /* must be _exit! bug was actually seen here */ + _exit(/*error if:*/ r < 0); + } +-#else +- { +- char *argv[4]; +- xmove_fd(fd, 0); +- xmove_fd(fd_pipe.wr, 1); +- argv[0] = (char*)transform_prog; +- argv[1] = (char*)"-cf"; +- argv[2] = (char*)"-"; +- argv[3] = NULL; +- BB_EXECVP(transform_prog, argv); +- bb_perror_msg_and_die("can't execute '%s'", transform_prog); +- } +-#endif + /* notreached */ + } ++#else ++ void *arg[2] = {&fd, transform_prog, &fd_pipe}; ++ clone(fork_transformer_child, NULL, CLONE_VM | CLONE_VFORK | SIGCHLD, arg); ++#endif + + /* parent process */ + close(fd_pipe.wr); /* don't want to write to the child */ +diff --git a/configs/wasm_defconfig b/configs/wasm_defconfig +new file mode 100644 +index 0000000..ef1dd5a +--- /dev/null ++++ b/configs/wasm_defconfig +@@ -0,0 +1,1231 @@ ++# ++# Automatically generated make config: don't edit ++# Busybox version: 1.36.1 ++# Sat Feb 24 17:21:38 2024 ++# ++CONFIG_HAVE_DOT_CONFIG=y ++ ++# ++# Settings ++# ++# CONFIG_DESKTOP is not set ++# CONFIG_EXTRA_COMPAT is not set ++# CONFIG_FEDORA_COMPAT is not set ++CONFIG_INCLUDE_SUSv2=y ++CONFIG_LONG_OPTS=y ++CONFIG_SHOW_USAGE=y ++CONFIG_FEATURE_VERBOSE_USAGE=y ++CONFIG_FEATURE_COMPRESS_USAGE=y ++CONFIG_LFS=y ++# CONFIG_PAM is not set ++CONFIG_FEATURE_DEVPTS=y ++CONFIG_FEATURE_UTMP=y ++CONFIG_FEATURE_WTMP=y ++CONFIG_FEATURE_PIDFILE=y ++CONFIG_PID_FILE_PATH="/var/run" ++CONFIG_BUSYBOX=y ++CONFIG_FEATURE_SHOW_SCRIPT=y ++CONFIG_FEATURE_INSTALLER=y ++# CONFIG_INSTALL_NO_USR is not set ++CONFIG_FEATURE_SUID=y ++CONFIG_FEATURE_SUID_CONFIG=y ++CONFIG_FEATURE_SUID_CONFIG_QUIET=y ++# CONFIG_FEATURE_PREFER_APPLETS is not set ++CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" ++# CONFIG_SELINUX is not set ++# CONFIG_FEATURE_CLEAN_UP is not set ++CONFIG_FEATURE_SYSLOG_INFO=y ++CONFIG_FEATURE_SYSLOG=y ++ ++# ++# Build Options ++# ++CONFIG_STATIC=y ++# CONFIG_PIE is not set ++CONFIG_NOMMU=y ++# CONFIG_BUILD_LIBBUSYBOX is not set ++# CONFIG_FEATURE_LIBBUSYBOX_STATIC is not set ++# CONFIG_FEATURE_INDIVIDUAL is not set ++# CONFIG_FEATURE_SHARED_BUSYBOX is not set ++CONFIG_CROSS_COMPILER_PREFIX="" ++CONFIG_SYSROOT="" ++CONFIG_EXTRA_CFLAGS="" ++CONFIG_EXTRA_LDFLAGS="" ++CONFIG_EXTRA_LDLIBS="" ++# CONFIG_USE_PORTABLE_CODE is not set ++CONFIG_STACK_OPTIMIZATION_386=y ++CONFIG_STATIC_LIBGCC=y ++ ++# ++# Installation Options ("make install" behavior) ++# ++CONFIG_INSTALL_APPLET_SYMLINKS=y ++# CONFIG_INSTALL_APPLET_HARDLINKS is not set ++# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set ++# CONFIG_INSTALL_APPLET_DONT is not set ++# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set ++# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set ++# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set ++CONFIG_PREFIX="./_install" ++ ++# ++# Debugging Options ++# ++# CONFIG_DEBUG is not set ++# CONFIG_DEBUG_PESSIMIZE is not set ++# CONFIG_DEBUG_SANITIZE is not set ++# CONFIG_UNIT_TEST is not set ++# CONFIG_WERROR is not set ++# CONFIG_WARN_SIMPLE_MSG is not set ++CONFIG_NO_DEBUG_LIB=y ++# CONFIG_DMALLOC is not set ++# CONFIG_EFENCE is not set ++ ++# ++# Library Tuning ++# ++# CONFIG_FEATURE_USE_BSS_TAIL is not set ++CONFIG_FLOAT_DURATION=y ++CONFIG_FEATURE_RTMINMAX=y ++CONFIG_FEATURE_RTMINMAX_USE_LIBC_DEFINITIONS=y ++CONFIG_FEATURE_BUFFERS_USE_MALLOC=y ++# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set ++# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set ++CONFIG_PASSWORD_MINLEN=6 ++CONFIG_MD5_SMALL=1 ++CONFIG_SHA1_SMALL=3 ++CONFIG_SHA1_HWACCEL=y ++CONFIG_SHA256_HWACCEL=y ++CONFIG_SHA3_SMALL=1 ++CONFIG_FEATURE_NON_POSIX_CP=y ++# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set ++CONFIG_FEATURE_USE_SENDFILE=y ++CONFIG_FEATURE_COPYBUF_KB=4 ++CONFIG_MONOTONIC_SYSCALL=y ++CONFIG_IOCTL_HEX2STR_ERROR=y ++CONFIG_FEATURE_EDITING=y ++CONFIG_FEATURE_EDITING_MAX_LEN=1024 ++# CONFIG_FEATURE_EDITING_VI is not set ++CONFIG_FEATURE_EDITING_HISTORY=255 ++CONFIG_FEATURE_EDITING_SAVEHISTORY=y ++# CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set ++CONFIG_FEATURE_REVERSE_SEARCH=y ++CONFIG_FEATURE_TAB_COMPLETION=y ++CONFIG_FEATURE_USERNAME_COMPLETION=y ++CONFIG_FEATURE_EDITING_FANCY_PROMPT=y ++CONFIG_FEATURE_EDITING_WINCH=y ++# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set ++# CONFIG_LOCALE_SUPPORT is not set ++CONFIG_UNICODE_SUPPORT=y ++# CONFIG_UNICODE_USING_LOCALE is not set ++# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set ++CONFIG_SUBST_WCHAR=63 ++CONFIG_LAST_SUPPORTED_WCHAR=767 ++# CONFIG_UNICODE_COMBINING_WCHARS is not set ++# CONFIG_UNICODE_WIDE_WCHARS is not set ++# CONFIG_UNICODE_BIDI_SUPPORT is not set ++# CONFIG_UNICODE_NEUTRAL_TABLE is not set ++# CONFIG_UNICODE_PRESERVE_BROKEN is not set ++# CONFIG_LOOP_CONFIGURE is not set ++# CONFIG_NO_LOOP_CONFIGURE is not set ++CONFIG_TRY_LOOP_CONFIGURE=y ++ ++# ++# Applets ++# ++ ++# ++# Archival Utilities ++# ++CONFIG_FEATURE_SEAMLESS_XZ=y ++CONFIG_FEATURE_SEAMLESS_LZMA=y ++CONFIG_FEATURE_SEAMLESS_BZ2=y ++CONFIG_FEATURE_SEAMLESS_GZ=y ++# CONFIG_FEATURE_SEAMLESS_Z is not set ++# CONFIG_AR is not set ++# CONFIG_FEATURE_AR_LONG_FILENAMES is not set ++# CONFIG_FEATURE_AR_CREATE is not set ++# CONFIG_UNCOMPRESS is not set ++CONFIG_GUNZIP=y ++CONFIG_ZCAT=y ++CONFIG_FEATURE_GUNZIP_LONG_OPTIONS=y ++CONFIG_BUNZIP2=y ++CONFIG_BZCAT=y ++CONFIG_UNLZMA=y ++CONFIG_LZCAT=y ++CONFIG_LZMA=y ++CONFIG_UNXZ=y ++CONFIG_XZCAT=y ++CONFIG_XZ=y ++CONFIG_BZIP2=y ++CONFIG_BZIP2_SMALL=8 ++CONFIG_FEATURE_BZIP2_DECOMPRESS=y ++CONFIG_CPIO=y ++CONFIG_FEATURE_CPIO_O=y ++CONFIG_FEATURE_CPIO_P=y ++CONFIG_FEATURE_CPIO_IGNORE_DEVNO=y ++CONFIG_FEATURE_CPIO_RENUMBER_INODES=y ++CONFIG_DPKG=y ++CONFIG_DPKG_DEB=y ++CONFIG_GZIP=y ++CONFIG_FEATURE_GZIP_LONG_OPTIONS=y ++CONFIG_GZIP_FAST=0 ++# CONFIG_FEATURE_GZIP_LEVELS is not set ++CONFIG_FEATURE_GZIP_DECOMPRESS=y ++CONFIG_LZOP=y ++# CONFIG_UNLZOP is not set ++# CONFIG_LZOPCAT is not set ++# CONFIG_LZOP_COMPR_HIGH is not set ++CONFIG_RPM=y ++CONFIG_RPM2CPIO=y ++CONFIG_TAR=y ++CONFIG_FEATURE_TAR_LONG_OPTIONS=y ++CONFIG_FEATURE_TAR_CREATE=y ++CONFIG_FEATURE_TAR_AUTODETECT=y ++CONFIG_FEATURE_TAR_FROM=y ++CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y ++CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y ++CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y ++CONFIG_FEATURE_TAR_TO_COMMAND=y ++CONFIG_FEATURE_TAR_UNAME_GNAME=y ++CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y ++# CONFIG_FEATURE_TAR_SELINUX is not set ++CONFIG_UNZIP=y ++CONFIG_FEATURE_UNZIP_CDF=y ++# CONFIG_FEATURE_UNZIP_BZIP2 is not set ++# CONFIG_FEATURE_UNZIP_LZMA is not set ++# CONFIG_FEATURE_UNZIP_XZ is not set ++# CONFIG_FEATURE_LZMA_FAST is not set ++ ++# ++# Coreutils ++# ++CONFIG_FEATURE_VERBOSE=y ++ ++# ++# Common options for date and touch ++# ++# CONFIG_FEATURE_TIMEZONE is not set ++ ++# ++# Common options for cp and mv ++# ++CONFIG_FEATURE_PRESERVE_HARDLINKS=y ++ ++# ++# Common options for df, du, ls ++# ++CONFIG_FEATURE_HUMAN_READABLE=y ++CONFIG_BASENAME=y ++CONFIG_CAT=y ++CONFIG_FEATURE_CATN=y ++CONFIG_FEATURE_CATV=y ++CONFIG_CHGRP=y ++CONFIG_CHMOD=y ++CONFIG_CHOWN=y ++CONFIG_FEATURE_CHOWN_LONG_OPTIONS=y ++CONFIG_CHROOT=y ++CONFIG_CKSUM=y ++CONFIG_CRC32=y ++CONFIG_COMM=y ++CONFIG_CP=y ++CONFIG_FEATURE_CP_LONG_OPTIONS=y ++CONFIG_FEATURE_CP_REFLINK=y ++CONFIG_CUT=y ++CONFIG_FEATURE_CUT_REGEX=y ++CONFIG_DATE=y ++CONFIG_FEATURE_DATE_ISOFMT=y ++# CONFIG_FEATURE_DATE_NANO is not set ++CONFIG_FEATURE_DATE_COMPAT=y ++CONFIG_DD=y ++CONFIG_FEATURE_DD_SIGNAL_HANDLING=y ++CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y ++CONFIG_FEATURE_DD_IBS_OBS=y ++CONFIG_FEATURE_DD_STATUS=y ++CONFIG_DF=y ++CONFIG_FEATURE_DF_FANCY=y ++CONFIG_FEATURE_SKIP_ROOTFS=y ++CONFIG_DIRNAME=y ++CONFIG_DOS2UNIX=y ++CONFIG_UNIX2DOS=y ++CONFIG_DU=y ++CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y ++CONFIG_ECHO=y ++CONFIG_FEATURE_FANCY_ECHO=y ++CONFIG_ENV=y ++CONFIG_EXPAND=y ++CONFIG_UNEXPAND=y ++CONFIG_EXPR=y ++CONFIG_EXPR_MATH_SUPPORT_64=y ++CONFIG_FACTOR=y ++CONFIG_FALSE=y ++CONFIG_FOLD=y ++CONFIG_HEAD=y ++CONFIG_FEATURE_FANCY_HEAD=y ++CONFIG_HOSTID=y ++CONFIG_ID=y ++CONFIG_GROUPS=y ++CONFIG_INSTALL=y ++CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y ++CONFIG_LINK=y ++CONFIG_LN=y ++CONFIG_LOGNAME=y ++CONFIG_LS=y ++CONFIG_FEATURE_LS_FILETYPES=y ++CONFIG_FEATURE_LS_FOLLOWLINKS=y ++CONFIG_FEATURE_LS_RECURSIVE=y ++CONFIG_FEATURE_LS_WIDTH=y ++CONFIG_FEATURE_LS_SORTFILES=y ++CONFIG_FEATURE_LS_TIMESTAMPS=y ++CONFIG_FEATURE_LS_USERNAME=y ++CONFIG_FEATURE_LS_COLOR=y ++CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y ++CONFIG_MD5SUM=y ++CONFIG_SHA1SUM=y ++CONFIG_SHA256SUM=y ++CONFIG_SHA512SUM=y ++CONFIG_SHA3SUM=y ++ ++# ++# Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum ++# ++CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y ++CONFIG_MKDIR=y ++CONFIG_MKFIFO=y ++CONFIG_MKNOD=y ++CONFIG_MKTEMP=y ++CONFIG_MV=y ++CONFIG_NICE=y ++CONFIG_NL=y ++CONFIG_NOHUP=y ++CONFIG_NPROC=y ++CONFIG_OD=y ++CONFIG_PASTE=y ++CONFIG_PRINTENV=y ++CONFIG_PRINTF=y ++CONFIG_PWD=y ++CONFIG_READLINK=y ++CONFIG_FEATURE_READLINK_FOLLOW=y ++CONFIG_REALPATH=y ++CONFIG_RM=y ++CONFIG_RMDIR=y ++CONFIG_SEQ=y ++CONFIG_SHRED=y ++CONFIG_SHUF=y ++CONFIG_SLEEP=y ++CONFIG_FEATURE_FANCY_SLEEP=y ++CONFIG_SORT=y ++CONFIG_FEATURE_SORT_BIG=y ++# CONFIG_FEATURE_SORT_OPTIMIZE_MEMORY is not set ++CONFIG_SPLIT=y ++CONFIG_FEATURE_SPLIT_FANCY=y ++CONFIG_STAT=y ++CONFIG_FEATURE_STAT_FORMAT=y ++CONFIG_FEATURE_STAT_FILESYSTEM=y ++CONFIG_STTY=y ++CONFIG_SUM=y ++CONFIG_SYNC=y ++CONFIG_FEATURE_SYNC_FANCY=y ++CONFIG_FSYNC=y ++CONFIG_TAC=y ++CONFIG_TAIL=y ++CONFIG_FEATURE_FANCY_TAIL=y ++CONFIG_TEE=y ++CONFIG_FEATURE_TEE_USE_BLOCK_IO=y ++CONFIG_TEST=y ++CONFIG_TEST1=y ++CONFIG_TEST2=y ++CONFIG_FEATURE_TEST_64=y ++CONFIG_TIMEOUT=y ++CONFIG_TOUCH=y ++CONFIG_FEATURE_TOUCH_SUSV3=y ++CONFIG_TR=y ++CONFIG_FEATURE_TR_CLASSES=y ++CONFIG_FEATURE_TR_EQUIV=y ++CONFIG_TRUE=y ++CONFIG_TRUNCATE=y ++CONFIG_TSORT=y ++CONFIG_TTY=y ++CONFIG_UNAME=y ++CONFIG_UNAME_OSNAME="GNU/Linux" ++CONFIG_BB_ARCH=y ++CONFIG_UNIQ=y ++CONFIG_UNLINK=y ++CONFIG_USLEEP=y ++CONFIG_UUDECODE=y ++CONFIG_BASE32=y ++CONFIG_BASE64=y ++CONFIG_UUENCODE=y ++CONFIG_WC=y ++CONFIG_FEATURE_WC_LARGE=y ++CONFIG_WHO=y ++CONFIG_W=y ++CONFIG_USERS=y ++CONFIG_WHOAMI=y ++CONFIG_YES=y ++ ++# ++# Console Utilities ++# ++CONFIG_CHVT=y ++CONFIG_CLEAR=y ++CONFIG_DEALLOCVT=y ++CONFIG_DUMPKMAP=y ++CONFIG_FGCONSOLE=y ++CONFIG_KBD_MODE=y ++CONFIG_LOADFONT=y ++CONFIG_SETFONT=y ++CONFIG_FEATURE_SETFONT_TEXTUAL_MAP=y ++CONFIG_DEFAULT_SETFONT_DIR="" ++ ++# ++# Common options for loadfont and setfont ++# ++CONFIG_FEATURE_LOADFONT_PSF2=y ++CONFIG_FEATURE_LOADFONT_RAW=y ++CONFIG_LOADKMAP=y ++CONFIG_OPENVT=y ++CONFIG_RESET=y ++CONFIG_RESIZE=y ++CONFIG_FEATURE_RESIZE_PRINT=y ++CONFIG_SETCONSOLE=y ++CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS=y ++CONFIG_SETKEYCODES=y ++CONFIG_SETLOGCONS=y ++CONFIG_SHOWKEY=y ++ ++# ++# Debian Utilities ++# ++CONFIG_PIPE_PROGRESS=y ++CONFIG_RUN_PARTS=y ++CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS=y ++CONFIG_FEATURE_RUN_PARTS_FANCY=y ++CONFIG_START_STOP_DAEMON=y ++CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS=y ++CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y ++CONFIG_WHICH=y ++ ++# ++# klibc-utils ++# ++# CONFIG_MINIPS is not set ++# CONFIG_NUKE is not set ++CONFIG_RESUME=y ++CONFIG_RUN_INIT=y ++ ++# ++# Editors ++# ++CONFIG_AWK=y ++CONFIG_FEATURE_AWK_LIBM=y ++CONFIG_FEATURE_AWK_GNU_EXTENSIONS=y ++CONFIG_CMP=y ++CONFIG_DIFF=y ++CONFIG_FEATURE_DIFF_LONG_OPTIONS=y ++CONFIG_FEATURE_DIFF_DIR=y ++CONFIG_ED=y ++CONFIG_PATCH=y ++CONFIG_SED=y ++CONFIG_VI=y ++CONFIG_FEATURE_VI_MAX_LEN=4096 ++# CONFIG_FEATURE_VI_8BIT is not set ++CONFIG_FEATURE_VI_COLON=y ++CONFIG_FEATURE_VI_COLON_EXPAND=y ++CONFIG_FEATURE_VI_YANKMARK=y ++CONFIG_FEATURE_VI_SEARCH=y ++# CONFIG_FEATURE_VI_REGEX_SEARCH is not set ++# CONFIG_FEATURE_VI_USE_SIGNALS is not set ++CONFIG_FEATURE_VI_DOT_CMD=y ++CONFIG_FEATURE_VI_READONLY=y ++CONFIG_FEATURE_VI_SETOPTS=y ++CONFIG_FEATURE_VI_SET=y ++CONFIG_FEATURE_VI_WIN_RESIZE=y ++CONFIG_FEATURE_VI_ASK_TERMINAL=y ++CONFIG_FEATURE_VI_UNDO=y ++CONFIG_FEATURE_VI_UNDO_QUEUE=y ++CONFIG_FEATURE_VI_UNDO_QUEUE_MAX=256 ++CONFIG_FEATURE_VI_VERBOSE_STATUS=y ++CONFIG_FEATURE_ALLOW_EXEC=y ++ ++# ++# Finding Utilities ++# ++CONFIG_FIND=y ++CONFIG_FEATURE_FIND_PRINT0=y ++CONFIG_FEATURE_FIND_MTIME=y ++CONFIG_FEATURE_FIND_ATIME=y ++CONFIG_FEATURE_FIND_CTIME=y ++CONFIG_FEATURE_FIND_MMIN=y ++CONFIG_FEATURE_FIND_AMIN=y ++CONFIG_FEATURE_FIND_CMIN=y ++CONFIG_FEATURE_FIND_PERM=y ++CONFIG_FEATURE_FIND_TYPE=y ++CONFIG_FEATURE_FIND_EXECUTABLE=y ++CONFIG_FEATURE_FIND_XDEV=y ++CONFIG_FEATURE_FIND_MAXDEPTH=y ++CONFIG_FEATURE_FIND_NEWER=y ++CONFIG_FEATURE_FIND_INUM=y ++CONFIG_FEATURE_FIND_SAMEFILE=y ++CONFIG_FEATURE_FIND_EXEC=y ++CONFIG_FEATURE_FIND_EXEC_PLUS=y ++CONFIG_FEATURE_FIND_USER=y ++CONFIG_FEATURE_FIND_GROUP=y ++CONFIG_FEATURE_FIND_NOT=y ++CONFIG_FEATURE_FIND_DEPTH=y ++CONFIG_FEATURE_FIND_PAREN=y ++CONFIG_FEATURE_FIND_SIZE=y ++CONFIG_FEATURE_FIND_PRUNE=y ++CONFIG_FEATURE_FIND_QUIT=y ++CONFIG_FEATURE_FIND_DELETE=y ++CONFIG_FEATURE_FIND_EMPTY=y ++CONFIG_FEATURE_FIND_PATH=y ++CONFIG_FEATURE_FIND_REGEX=y ++# CONFIG_FEATURE_FIND_CONTEXT is not set ++CONFIG_FEATURE_FIND_LINKS=y ++CONFIG_GREP=y ++CONFIG_EGREP=y ++CONFIG_FGREP=y ++CONFIG_FEATURE_GREP_CONTEXT=y ++CONFIG_XARGS=y ++CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y ++CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y ++CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y ++CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y ++CONFIG_FEATURE_XARGS_SUPPORT_REPL_STR=y ++CONFIG_FEATURE_XARGS_SUPPORT_PARALLEL=y ++CONFIG_FEATURE_XARGS_SUPPORT_ARGS_FILE=y ++ ++# ++# Init Utilities ++# ++CONFIG_BOOTCHARTD=y ++CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER=y ++CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE=y ++CONFIG_HALT=y ++CONFIG_POWEROFF=y ++CONFIG_REBOOT=y ++CONFIG_FEATURE_WAIT_FOR_INIT=y ++# CONFIG_FEATURE_CALL_TELINIT is not set ++CONFIG_TELINIT_PATH="" ++CONFIG_INIT=y ++CONFIG_LINUXRC=y ++CONFIG_FEATURE_USE_INITTAB=y ++# CONFIG_FEATURE_KILL_REMOVED is not set ++CONFIG_FEATURE_KILL_DELAY=0 ++CONFIG_FEATURE_INIT_SCTTY=y ++CONFIG_FEATURE_INIT_SYSLOG=y ++CONFIG_FEATURE_INIT_QUIET=y ++# CONFIG_FEATURE_INIT_COREDUMPS is not set ++CONFIG_INIT_TERMINAL_TYPE="linux" ++CONFIG_FEATURE_INIT_MODIFY_CMDLINE=y ++ ++# ++# Login/Password Management Utilities ++# ++CONFIG_FEATURE_SHADOWPASSWDS=y ++CONFIG_USE_BB_PWD_GRP=y ++CONFIG_USE_BB_SHADOW=y ++CONFIG_USE_BB_CRYPT=y ++CONFIG_USE_BB_CRYPT_SHA=y ++CONFIG_ADD_SHELL=y ++CONFIG_REMOVE_SHELL=y ++CONFIG_ADDGROUP=y ++CONFIG_FEATURE_ADDUSER_TO_GROUP=y ++CONFIG_ADDUSER=y ++# CONFIG_FEATURE_CHECK_NAMES is not set ++CONFIG_LAST_ID=60000 ++CONFIG_FIRST_SYSTEM_ID=100 ++CONFIG_LAST_SYSTEM_ID=999 ++CONFIG_CHPASSWD=y ++CONFIG_FEATURE_DEFAULT_PASSWD_ALGO="des" ++CONFIG_CRYPTPW=y ++CONFIG_MKPASSWD=y ++CONFIG_DELUSER=y ++CONFIG_DELGROUP=y ++CONFIG_FEATURE_DEL_USER_FROM_GROUP=y ++CONFIG_GETTY=y ++CONFIG_LOGIN=y ++# CONFIG_LOGIN_SESSION_AS_CHILD is not set ++CONFIG_LOGIN_SCRIPTS=y ++CONFIG_FEATURE_NOLOGIN=y ++CONFIG_FEATURE_SECURETTY=y ++CONFIG_PASSWD=y ++CONFIG_FEATURE_PASSWD_WEAK_CHECK=y ++CONFIG_SU=y ++CONFIG_FEATURE_SU_SYSLOG=y ++CONFIG_FEATURE_SU_CHECKS_SHELLS=y ++# CONFIG_FEATURE_SU_BLANK_PW_NEEDS_SECURE_TTY is not set ++CONFIG_SULOGIN=y ++CONFIG_VLOCK=y ++ ++# ++# Linux Ext2 FS Progs ++# ++CONFIG_CHATTR=y ++CONFIG_FSCK=y ++CONFIG_LSATTR=y ++# CONFIG_TUNE2FS is not set ++ ++# ++# Linux Module Utilities ++# ++CONFIG_MODPROBE_SMALL=y ++CONFIG_DEPMOD=y ++CONFIG_INSMOD=y ++CONFIG_LSMOD=y ++# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set ++CONFIG_MODINFO=y ++CONFIG_MODPROBE=y ++# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set ++CONFIG_RMMOD=y ++ ++# ++# Options common to multiple modutils ++# ++CONFIG_FEATURE_CMDLINE_MODULE_OPTIONS=y ++CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED=y ++# CONFIG_FEATURE_2_4_MODULES is not set ++# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set ++# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set ++# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set ++# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set ++# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set ++# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set ++# CONFIG_FEATURE_INSMOD_TRY_MMAP is not set ++# CONFIG_FEATURE_MODUTILS_ALIAS is not set ++# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set ++CONFIG_DEFAULT_MODULES_DIR="/lib/modules" ++CONFIG_DEFAULT_DEPMOD_FILE="modules.dep" ++ ++# ++# Linux System Utilities ++# ++CONFIG_ACPID=y ++CONFIG_FEATURE_ACPID_COMPAT=y ++CONFIG_BLKDISCARD=y ++CONFIG_BLKID=y ++CONFIG_FEATURE_BLKID_TYPE=y ++CONFIG_BLOCKDEV=y ++CONFIG_CAL=y ++CONFIG_CHRT=y ++CONFIG_DMESG=y ++CONFIG_FEATURE_DMESG_PRETTY=y ++CONFIG_EJECT=y ++CONFIG_FEATURE_EJECT_SCSI=y ++CONFIG_FALLOCATE=y ++CONFIG_FATATTR=y ++CONFIG_FBSET=y ++CONFIG_FEATURE_FBSET_FANCY=y ++CONFIG_FEATURE_FBSET_READMODE=y ++CONFIG_FDFORMAT=y ++CONFIG_FDISK=y ++# CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set ++CONFIG_FEATURE_FDISK_WRITABLE=y ++# CONFIG_FEATURE_AIX_LABEL is not set ++# CONFIG_FEATURE_SGI_LABEL is not set ++# CONFIG_FEATURE_SUN_LABEL is not set ++# CONFIG_FEATURE_OSF_LABEL is not set ++# CONFIG_FEATURE_GPT_LABEL is not set ++CONFIG_FEATURE_FDISK_ADVANCED=y ++CONFIG_FINDFS=y ++CONFIG_FLOCK=y ++CONFIG_FDFLUSH=y ++CONFIG_FREERAMDISK=y ++CONFIG_FSCK_MINIX=y ++CONFIG_FSFREEZE=y ++CONFIG_FSTRIM=y ++CONFIG_GETOPT=y ++CONFIG_FEATURE_GETOPT_LONG=y ++CONFIG_HEXDUMP=y ++CONFIG_HD=y ++CONFIG_XXD=y ++CONFIG_HWCLOCK=y ++# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set ++CONFIG_IONICE=y ++CONFIG_IPCRM=y ++CONFIG_IPCS=y ++CONFIG_LAST=y ++CONFIG_FEATURE_LAST_FANCY=y ++CONFIG_LOSETUP=y ++CONFIG_LSPCI=y ++CONFIG_LSUSB=y ++CONFIG_MDEV=y ++CONFIG_FEATURE_MDEV_CONF=y ++CONFIG_FEATURE_MDEV_RENAME=y ++CONFIG_FEATURE_MDEV_RENAME_REGEXP=y ++CONFIG_FEATURE_MDEV_EXEC=y ++CONFIG_FEATURE_MDEV_LOAD_FIRMWARE=y ++CONFIG_FEATURE_MDEV_DAEMON=y ++CONFIG_MESG=y ++CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y ++CONFIG_MKE2FS=y ++CONFIG_MKFS_EXT2=y ++CONFIG_MKFS_MINIX=y ++CONFIG_FEATURE_MINIX2=y ++# CONFIG_MKFS_REISER is not set ++CONFIG_MKDOSFS=y ++CONFIG_MKFS_VFAT=y ++CONFIG_MKSWAP=y ++CONFIG_FEATURE_MKSWAP_UUID=y ++CONFIG_MORE=y ++CONFIG_MOUNT=y ++CONFIG_FEATURE_MOUNT_FAKE=y ++CONFIG_FEATURE_MOUNT_VERBOSE=y ++# CONFIG_FEATURE_MOUNT_HELPERS is not set ++CONFIG_FEATURE_MOUNT_LABEL=y ++# CONFIG_FEATURE_MOUNT_NFS is not set ++CONFIG_FEATURE_MOUNT_CIFS=y ++CONFIG_FEATURE_MOUNT_FLAGS=y ++CONFIG_FEATURE_MOUNT_FSTAB=y ++CONFIG_FEATURE_MOUNT_OTHERTAB=y ++CONFIG_MOUNTPOINT=y ++CONFIG_NOLOGIN=y ++# CONFIG_NOLOGIN_DEPENDENCIES is not set ++CONFIG_NSENTER=y ++CONFIG_PIVOT_ROOT=y ++CONFIG_RDATE=y ++CONFIG_RDEV=y ++CONFIG_READPROFILE=y ++CONFIG_RENICE=y ++CONFIG_REV=y ++CONFIG_RTCWAKE=y ++CONFIG_SCRIPT=y ++CONFIG_SCRIPTREPLAY=y ++CONFIG_SETARCH=y ++CONFIG_LINUX32=y ++CONFIG_LINUX64=y ++CONFIG_SETPRIV=y ++CONFIG_FEATURE_SETPRIV_DUMP=y ++CONFIG_FEATURE_SETPRIV_CAPABILITIES=y ++CONFIG_FEATURE_SETPRIV_CAPABILITY_NAMES=y ++CONFIG_SETSID=y ++CONFIG_SWAPON=y ++CONFIG_FEATURE_SWAPON_DISCARD=y ++CONFIG_FEATURE_SWAPON_PRI=y ++CONFIG_SWAPOFF=y ++CONFIG_FEATURE_SWAPONOFF_LABEL=y ++CONFIG_SWITCH_ROOT=y ++CONFIG_TASKSET=y ++CONFIG_FEATURE_TASKSET_FANCY=y ++CONFIG_FEATURE_TASKSET_CPULIST=y ++CONFIG_UEVENT=y ++CONFIG_UMOUNT=y ++CONFIG_FEATURE_UMOUNT_ALL=y ++# CONFIG_UNSHARE is not set ++CONFIG_WALL=y ++ ++# ++# Common options for mount/umount ++# ++CONFIG_FEATURE_MOUNT_LOOP=y ++CONFIG_FEATURE_MOUNT_LOOP_CREATE=y ++# CONFIG_FEATURE_MTAB_SUPPORT is not set ++CONFIG_VOLUMEID=y ++ ++# ++# Filesystem/Volume identification ++# ++CONFIG_FEATURE_VOLUMEID_BCACHE=y ++CONFIG_FEATURE_VOLUMEID_BTRFS=y ++CONFIG_FEATURE_VOLUMEID_CRAMFS=y ++CONFIG_FEATURE_VOLUMEID_EROFS=y ++CONFIG_FEATURE_VOLUMEID_EXFAT=y ++CONFIG_FEATURE_VOLUMEID_EXT=y ++CONFIG_FEATURE_VOLUMEID_F2FS=y ++CONFIG_FEATURE_VOLUMEID_FAT=y ++CONFIG_FEATURE_VOLUMEID_HFS=y ++CONFIG_FEATURE_VOLUMEID_ISO9660=y ++CONFIG_FEATURE_VOLUMEID_JFS=y ++CONFIG_FEATURE_VOLUMEID_LFS=y ++CONFIG_FEATURE_VOLUMEID_LINUXRAID=y ++CONFIG_FEATURE_VOLUMEID_LINUXSWAP=y ++CONFIG_FEATURE_VOLUMEID_LUKS=y ++CONFIG_FEATURE_VOLUMEID_MINIX=y ++CONFIG_FEATURE_VOLUMEID_NILFS=y ++CONFIG_FEATURE_VOLUMEID_NTFS=y ++CONFIG_FEATURE_VOLUMEID_OCFS2=y ++CONFIG_FEATURE_VOLUMEID_REISERFS=y ++CONFIG_FEATURE_VOLUMEID_ROMFS=y ++CONFIG_FEATURE_VOLUMEID_SQUASHFS=y ++CONFIG_FEATURE_VOLUMEID_SYSV=y ++CONFIG_FEATURE_VOLUMEID_UBIFS=y ++CONFIG_FEATURE_VOLUMEID_UDF=y ++CONFIG_FEATURE_VOLUMEID_XFS=y ++ ++# ++# Miscellaneous Utilities ++# ++CONFIG_ADJTIMEX=y ++CONFIG_ASCII=y ++# CONFIG_BBCONFIG is not set ++# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set ++CONFIG_BC=y ++CONFIG_DC=y ++CONFIG_FEATURE_DC_BIG=y ++# CONFIG_FEATURE_DC_LIBM is not set ++CONFIG_FEATURE_BC_INTERACTIVE=y ++CONFIG_FEATURE_BC_LONG_OPTIONS=y ++CONFIG_BEEP=y ++CONFIG_FEATURE_BEEP_FREQ=4000 ++CONFIG_FEATURE_BEEP_LENGTH_MS=30 ++CONFIG_CHAT=y ++CONFIG_FEATURE_CHAT_NOFAIL=y ++# CONFIG_FEATURE_CHAT_TTY_HIFI is not set ++CONFIG_FEATURE_CHAT_IMPLICIT_CR=y ++CONFIG_FEATURE_CHAT_SWALLOW_OPTS=y ++CONFIG_FEATURE_CHAT_SEND_ESCAPES=y ++CONFIG_FEATURE_CHAT_VAR_ABORT_LEN=y ++CONFIG_FEATURE_CHAT_CLR_ABORT=y ++CONFIG_CONSPY=y ++CONFIG_CROND=y ++CONFIG_FEATURE_CROND_D=y ++CONFIG_FEATURE_CROND_CALL_SENDMAIL=y ++CONFIG_FEATURE_CROND_SPECIAL_TIMES=y ++CONFIG_FEATURE_CROND_DIR="/var/spool/cron" ++CONFIG_CRONTAB=y ++# CONFIG_DEVFSD is not set ++# CONFIG_DEVFSD_MODLOAD is not set ++# CONFIG_DEVFSD_FG_NP is not set ++# CONFIG_DEVFSD_VERBOSE is not set ++# CONFIG_FEATURE_DEVFS is not set ++CONFIG_DEVMEM=y ++CONFIG_FBSPLASH=y ++# CONFIG_FLASH_ERASEALL is not set ++# CONFIG_FLASH_LOCK is not set ++# CONFIG_FLASH_UNLOCK is not set ++# CONFIG_FLASHCP is not set ++CONFIG_HDPARM=y ++CONFIG_FEATURE_HDPARM_GET_IDENTITY=y ++CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF=y ++CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF=y ++CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET=y ++CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF=y ++CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA=y ++CONFIG_HEXEDIT=y ++CONFIG_I2CGET=y ++CONFIG_I2CSET=y ++CONFIG_I2CDUMP=y ++CONFIG_I2CDETECT=y ++CONFIG_I2CTRANSFER=y ++# CONFIG_INOTIFYD is not set ++CONFIG_LESS=y ++CONFIG_FEATURE_LESS_MAXLINES=9999999 ++CONFIG_FEATURE_LESS_BRACKETS=y ++CONFIG_FEATURE_LESS_FLAGS=y ++CONFIG_FEATURE_LESS_TRUNCATE=y ++CONFIG_FEATURE_LESS_MARKS=y ++CONFIG_FEATURE_LESS_REGEXP=y ++CONFIG_FEATURE_LESS_WINCH=y ++CONFIG_FEATURE_LESS_ASK_TERMINAL=y ++CONFIG_FEATURE_LESS_DASHCMD=y ++CONFIG_FEATURE_LESS_LINENUMS=y ++CONFIG_FEATURE_LESS_RAW=y ++CONFIG_FEATURE_LESS_ENV=y ++CONFIG_LSSCSI=y ++CONFIG_MAKEDEVS=y ++# CONFIG_FEATURE_MAKEDEVS_LEAF is not set ++CONFIG_FEATURE_MAKEDEVS_TABLE=y ++CONFIG_MAN=y ++CONFIG_MICROCOM=y ++CONFIG_MIM=y ++CONFIG_MT=y ++CONFIG_NANDWRITE=y ++CONFIG_NANDDUMP=y ++CONFIG_PARTPROBE=y ++CONFIG_RAIDAUTORUN=y ++CONFIG_READAHEAD=y ++# CONFIG_RFKILL is not set ++CONFIG_RUNLEVEL=y ++CONFIG_RX=y ++CONFIG_SEEDRNG=y ++CONFIG_SETFATTR=y ++CONFIG_SETSERIAL=y ++CONFIG_STRINGS=y ++CONFIG_TIME=y ++CONFIG_TREE=y ++CONFIG_TS=y ++CONFIG_TTYSIZE=y ++CONFIG_UBIATTACH=y ++CONFIG_UBIDETACH=y ++CONFIG_UBIMKVOL=y ++CONFIG_UBIRMVOL=y ++CONFIG_UBIRSVOL=y ++CONFIG_UBIUPDATEVOL=y ++CONFIG_UBIRENAME=y ++CONFIG_VOLNAME=y ++CONFIG_WATCHDOG=y ++# CONFIG_FEATURE_WATCHDOG_OPEN_TWICE is not set ++ ++# ++# Networking Utilities ++# ++CONFIG_FEATURE_IPV6=y ++# CONFIG_FEATURE_UNIX_LOCAL is not set ++CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y ++# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set ++# CONFIG_FEATURE_ETC_NETWORKS is not set ++# CONFIG_FEATURE_ETC_SERVICES is not set ++CONFIG_FEATURE_HWIB=y ++# CONFIG_FEATURE_TLS_SHA1 is not set ++CONFIG_ARP=y ++CONFIG_ARPING=y ++CONFIG_BRCTL=y ++CONFIG_FEATURE_BRCTL_FANCY=y ++CONFIG_FEATURE_BRCTL_SHOW=y ++CONFIG_DNSD=y ++CONFIG_ETHER_WAKE=y ++CONFIG_FTPD=y ++CONFIG_FEATURE_FTPD_WRITE=y ++CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y ++CONFIG_FEATURE_FTPD_AUTHENTICATION=y ++CONFIG_FTPGET=y ++CONFIG_FTPPUT=y ++CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y ++CONFIG_HOSTNAME=y ++CONFIG_DNSDOMAINNAME=y ++CONFIG_HTTPD=y ++CONFIG_FEATURE_HTTPD_PORT_DEFAULT=80 ++CONFIG_FEATURE_HTTPD_RANGES=y ++CONFIG_FEATURE_HTTPD_SETUID=y ++CONFIG_FEATURE_HTTPD_BASIC_AUTH=y ++CONFIG_FEATURE_HTTPD_AUTH_MD5=y ++CONFIG_FEATURE_HTTPD_CGI=y ++CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y ++CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y ++CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y ++CONFIG_FEATURE_HTTPD_ERROR_PAGES=y ++CONFIG_FEATURE_HTTPD_PROXY=y ++CONFIG_FEATURE_HTTPD_GZIP=y ++CONFIG_FEATURE_HTTPD_ETAG=y ++CONFIG_FEATURE_HTTPD_LAST_MODIFIED=y ++CONFIG_FEATURE_HTTPD_DATE=y ++CONFIG_FEATURE_HTTPD_ACL_IP=y ++CONFIG_IFCONFIG=y ++CONFIG_FEATURE_IFCONFIG_STATUS=y ++CONFIG_FEATURE_IFCONFIG_SLIP=y ++CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y ++CONFIG_FEATURE_IFCONFIG_HW=y ++CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y ++CONFIG_IFENSLAVE=y ++CONFIG_IFPLUGD=y ++CONFIG_IFUP=y ++CONFIG_IFDOWN=y ++CONFIG_IFUPDOWN_IFSTATE_PATH="/var/run/ifstate" ++CONFIG_FEATURE_IFUPDOWN_IP=y ++CONFIG_FEATURE_IFUPDOWN_IPV4=y ++CONFIG_FEATURE_IFUPDOWN_IPV6=y ++CONFIG_FEATURE_IFUPDOWN_MAPPING=y ++# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set ++CONFIG_INETD=y ++CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO=y ++CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD=y ++CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME=y ++CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME=y ++CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN=y ++# CONFIG_FEATURE_INETD_RPC is not set ++CONFIG_IP=y ++CONFIG_IPADDR=y ++CONFIG_IPLINK=y ++CONFIG_IPROUTE=y ++CONFIG_IPTUNNEL=y ++CONFIG_IPRULE=y ++CONFIG_IPNEIGH=y ++CONFIG_FEATURE_IP_ADDRESS=y ++CONFIG_FEATURE_IP_LINK=y ++CONFIG_FEATURE_IP_ROUTE=y ++CONFIG_FEATURE_IP_ROUTE_DIR="/etc/iproute2" ++CONFIG_FEATURE_IP_TUNNEL=y ++CONFIG_FEATURE_IP_RULE=y ++CONFIG_FEATURE_IP_NEIGH=y ++# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set ++CONFIG_IPCALC=y ++CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y ++CONFIG_FEATURE_IPCALC_FANCY=y ++CONFIG_FAKEIDENTD=y ++CONFIG_NAMEIF=y ++CONFIG_FEATURE_NAMEIF_EXTENDED=y ++CONFIG_NBDCLIENT=y ++CONFIG_NC=y ++# CONFIG_NETCAT is not set ++CONFIG_NC_SERVER=y ++CONFIG_NC_EXTRA=y ++CONFIG_NC_110_COMPAT=y ++CONFIG_NETSTAT=y ++CONFIG_FEATURE_NETSTAT_WIDE=y ++CONFIG_FEATURE_NETSTAT_PRG=y ++CONFIG_NSLOOKUP=y ++CONFIG_FEATURE_NSLOOKUP_BIG=y ++CONFIG_FEATURE_NSLOOKUP_LONG_OPTIONS=y ++CONFIG_NTPD=y ++CONFIG_FEATURE_NTPD_SERVER=y ++CONFIG_FEATURE_NTPD_CONF=y ++CONFIG_FEATURE_NTP_AUTH=y ++CONFIG_PING=y ++CONFIG_PING6=y ++CONFIG_FEATURE_FANCY_PING=y ++CONFIG_PSCAN=y ++CONFIG_ROUTE=y ++CONFIG_SLATTACH=y ++CONFIG_SSL_CLIENT=y ++CONFIG_TC=y ++CONFIG_FEATURE_TC_INGRESS=y ++CONFIG_TCPSVD=y ++CONFIG_UDPSVD=y ++CONFIG_TELNET=y ++CONFIG_FEATURE_TELNET_TTYPE=y ++CONFIG_FEATURE_TELNET_AUTOLOGIN=y ++CONFIG_FEATURE_TELNET_WIDTH=y ++CONFIG_TELNETD=y ++CONFIG_FEATURE_TELNETD_STANDALONE=y ++CONFIG_FEATURE_TELNETD_PORT_DEFAULT=23 ++CONFIG_FEATURE_TELNETD_INETD_WAIT=y ++CONFIG_TFTP=y ++CONFIG_FEATURE_TFTP_PROGRESS_BAR=y ++CONFIG_FEATURE_TFTP_HPA_COMPAT=y ++CONFIG_TFTPD=y ++CONFIG_FEATURE_TFTP_GET=y ++CONFIG_FEATURE_TFTP_PUT=y ++CONFIG_FEATURE_TFTP_BLOCKSIZE=y ++# CONFIG_TFTP_DEBUG is not set ++CONFIG_TLS=y ++CONFIG_TRACEROUTE=y ++CONFIG_TRACEROUTE6=y ++CONFIG_FEATURE_TRACEROUTE_VERBOSE=y ++CONFIG_FEATURE_TRACEROUTE_USE_ICMP=y ++CONFIG_TUNCTL=y ++CONFIG_FEATURE_TUNCTL_UG=y ++CONFIG_VCONFIG=y ++CONFIG_WGET=y ++CONFIG_FEATURE_WGET_LONG_OPTIONS=y ++CONFIG_FEATURE_WGET_STATUSBAR=y ++CONFIG_FEATURE_WGET_FTP=y ++CONFIG_FEATURE_WGET_AUTHENTICATION=y ++CONFIG_FEATURE_WGET_TIMEOUT=y ++CONFIG_FEATURE_WGET_HTTPS=y ++CONFIG_FEATURE_WGET_OPENSSL=y ++CONFIG_WHOIS=y ++CONFIG_ZCIP=y ++CONFIG_UDHCPD=y ++# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set ++CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY=y ++CONFIG_DHCPD_LEASES_FILE="/var/lib/misc/udhcpd.leases" ++CONFIG_DUMPLEASES=y ++CONFIG_DHCPRELAY=y ++CONFIG_UDHCPC=y ++CONFIG_FEATURE_UDHCPC_ARPING=y ++CONFIG_FEATURE_UDHCPC_SANITIZEOPT=y ++CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script" ++CONFIG_UDHCPC6_DEFAULT_SCRIPT="/usr/share/udhcpc/default6.script" ++CONFIG_UDHCPC6=y ++CONFIG_FEATURE_UDHCPC6_RFC3646=y ++CONFIG_FEATURE_UDHCPC6_RFC4704=y ++CONFIG_FEATURE_UDHCPC6_RFC4833=y ++CONFIG_FEATURE_UDHCPC6_RFC5970=y ++ ++# ++# Common options for DHCP applets ++# ++CONFIG_UDHCPC_DEFAULT_INTERFACE="eth0" ++# CONFIG_FEATURE_UDHCP_PORT is not set ++CONFIG_UDHCP_DEBUG=2 ++CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80 ++CONFIG_FEATURE_UDHCP_RFC3397=y ++CONFIG_FEATURE_UDHCP_8021Q=y ++CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="-R -n" ++ ++# ++# Print Utilities ++# ++CONFIG_LPD=y ++CONFIG_LPR=y ++CONFIG_LPQ=y ++ ++# ++# Mail Utilities ++# ++CONFIG_FEATURE_MIME_CHARSET="us-ascii" ++CONFIG_MAKEMIME=y ++CONFIG_POPMAILDIR=y ++CONFIG_FEATURE_POPMAILDIR_DELIVERY=y ++CONFIG_REFORMIME=y ++CONFIG_FEATURE_REFORMIME_COMPAT=y ++CONFIG_SENDMAIL=y ++ ++# ++# Process Utilities ++# ++# CONFIG_FEATURE_FAST_TOP is not set ++CONFIG_FEATURE_SHOW_THREADS=y ++CONFIG_FREE=y ++CONFIG_FUSER=y ++CONFIG_IOSTAT=y ++CONFIG_KILL=y ++CONFIG_KILLALL=y ++CONFIG_KILLALL5=y ++CONFIG_LSOF=y ++CONFIG_MPSTAT=y ++CONFIG_NMETER=y ++CONFIG_PGREP=y ++CONFIG_PKILL=y ++CONFIG_PIDOF=y ++CONFIG_FEATURE_PIDOF_SINGLE=y ++CONFIG_FEATURE_PIDOF_OMIT=y ++CONFIG_PMAP=y ++CONFIG_POWERTOP=y ++CONFIG_FEATURE_POWERTOP_INTERACTIVE=y ++CONFIG_PS=y ++CONFIG_FEATURE_PS_WIDE=y ++CONFIG_FEATURE_PS_LONG=y ++# CONFIG_FEATURE_PS_TIME is not set ++# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set ++# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set ++CONFIG_PSTREE=y ++CONFIG_PWDX=y ++CONFIG_SMEMCAP=y ++CONFIG_BB_SYSCTL=y ++CONFIG_TOP=y ++CONFIG_FEATURE_TOP_INTERACTIVE=y ++CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y ++CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y ++CONFIG_FEATURE_TOP_SMP_CPU=y ++CONFIG_FEATURE_TOP_DECIMALS=y ++CONFIG_FEATURE_TOP_SMP_PROCESS=y ++CONFIG_FEATURE_TOPMEM=y ++CONFIG_UPTIME=y ++CONFIG_FEATURE_UPTIME_UTMP_SUPPORT=y ++CONFIG_WATCH=y ++ ++# ++# Runit Utilities ++# ++CONFIG_CHPST=y ++CONFIG_SETUIDGID=y ++CONFIG_ENVUIDGID=y ++CONFIG_ENVDIR=y ++CONFIG_SOFTLIMIT=y ++CONFIG_RUNSV=y ++CONFIG_RUNSVDIR=y ++# CONFIG_FEATURE_RUNSVDIR_LOG is not set ++CONFIG_SV=y ++CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service" ++CONFIG_SVC=y ++CONFIG_SVOK=y ++CONFIG_SVLOGD=y ++# CONFIG_CHCON is not set ++# CONFIG_GETENFORCE is not set ++# CONFIG_GETSEBOOL is not set ++# CONFIG_LOAD_POLICY is not set ++# CONFIG_MATCHPATHCON is not set ++# CONFIG_RUNCON is not set ++# CONFIG_SELINUXENABLED is not set ++# CONFIG_SESTATUS is not set ++# CONFIG_SETENFORCE is not set ++# CONFIG_SETFILES is not set ++# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set ++# CONFIG_RESTORECON is not set ++# CONFIG_SETSEBOOL is not set ++ ++# ++# Shells ++# ++# CONFIG_SH_IS_ASH is not set ++CONFIG_SH_IS_HUSH=y ++# CONFIG_SH_IS_NONE is not set ++# CONFIG_BASH_IS_ASH is not set ++# CONFIG_BASH_IS_HUSH is not set ++CONFIG_BASH_IS_NONE=y ++# CONFIG_SHELL_ASH is not set ++# CONFIG_ASH is not set ++# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set ++# CONFIG_ASH_INTERNAL_GLOB is not set ++# CONFIG_ASH_BASH_COMPAT is not set ++# CONFIG_ASH_BASH_SOURCE_CURDIR is not set ++# CONFIG_ASH_BASH_NOT_FOUND_HOOK is not set ++# CONFIG_ASH_JOB_CONTROL is not set ++# CONFIG_ASH_ALIAS is not set ++# CONFIG_ASH_RANDOM_SUPPORT is not set ++# CONFIG_ASH_EXPAND_PRMT is not set ++# CONFIG_ASH_IDLE_TIMEOUT is not set ++# CONFIG_ASH_MAIL is not set ++# CONFIG_ASH_ECHO is not set ++# CONFIG_ASH_PRINTF is not set ++# CONFIG_ASH_TEST is not set ++# CONFIG_ASH_SLEEP is not set ++# CONFIG_ASH_HELP is not set ++# CONFIG_ASH_GETOPTS is not set ++# CONFIG_ASH_CMDCMD is not set ++CONFIG_CTTYHACK=y ++CONFIG_HUSH=y ++CONFIG_SHELL_HUSH=y ++CONFIG_HUSH_BASH_COMPAT=y ++CONFIG_HUSH_BRACE_EXPANSION=y ++# CONFIG_HUSH_BASH_SOURCE_CURDIR is not set ++CONFIG_HUSH_LINENO_VAR=y ++CONFIG_HUSH_INTERACTIVE=y ++CONFIG_HUSH_SAVEHISTORY=y ++CONFIG_HUSH_JOB=y ++CONFIG_HUSH_TICK=y ++CONFIG_HUSH_IF=y ++CONFIG_HUSH_LOOPS=y ++CONFIG_HUSH_CASE=y ++CONFIG_HUSH_FUNCTIONS=y ++CONFIG_HUSH_LOCAL=y ++CONFIG_HUSH_RANDOM_SUPPORT=y ++CONFIG_HUSH_MODE_X=y ++CONFIG_HUSH_ECHO=y ++CONFIG_HUSH_PRINTF=y ++CONFIG_HUSH_TEST=y ++CONFIG_HUSH_HELP=y ++CONFIG_HUSH_EXPORT=y ++CONFIG_HUSH_EXPORT_N=y ++CONFIG_HUSH_READONLY=y ++CONFIG_HUSH_KILL=y ++CONFIG_HUSH_WAIT=y ++CONFIG_HUSH_COMMAND=y ++CONFIG_HUSH_TRAP=y ++CONFIG_HUSH_TYPE=y ++CONFIG_HUSH_TIMES=y ++CONFIG_HUSH_READ=y ++CONFIG_HUSH_SET=y ++CONFIG_HUSH_UNSET=y ++CONFIG_HUSH_ULIMIT=y ++CONFIG_HUSH_UMASK=y ++CONFIG_HUSH_GETOPTS=y ++# CONFIG_HUSH_MEMLEAK is not set ++ ++# ++# Options common to all shells ++# ++CONFIG_FEATURE_SH_MATH=y ++CONFIG_FEATURE_SH_MATH_64=y ++CONFIG_FEATURE_SH_MATH_BASE=y ++CONFIG_FEATURE_SH_EXTRA_QUIET=y ++# CONFIG_FEATURE_SH_STANDALONE is not set ++# CONFIG_FEATURE_SH_NOFORK is not set ++CONFIG_FEATURE_SH_READ_FRAC=y ++CONFIG_FEATURE_SH_HISTFILESIZE=y ++CONFIG_FEATURE_SH_EMBEDDED_SCRIPTS=y ++ ++# ++# System Logging Utilities ++# ++CONFIG_KLOGD=y ++ ++# ++# klogd should not be used together with syslog to kernel printk buffer ++# ++CONFIG_FEATURE_KLOGD_KLOGCTL=y ++CONFIG_LOGGER=y ++CONFIG_LOGREAD=y ++CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y ++CONFIG_SYSLOGD=y ++CONFIG_FEATURE_ROTATE_LOGFILE=y ++CONFIG_FEATURE_REMOTE_LOG=y ++CONFIG_FEATURE_SYSLOGD_DUP=y ++CONFIG_FEATURE_SYSLOGD_CFG=y ++# CONFIG_FEATURE_SYSLOGD_PRECISE_TIMESTAMPS is not set ++CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=256 ++CONFIG_FEATURE_IPC_SYSLOG=y ++CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16 ++CONFIG_FEATURE_KMSG_SYSLOG=y +diff --git a/coreutils/test.c b/coreutils/test.c +index 1d1e6d1..57157db 100644 +--- a/coreutils/test.c ++++ b/coreutils/test.c +@@ -463,7 +463,7 @@ static void syntax(const char *op, const char *msg) + } else { + bb_error_msg("%s: %s"+4, msg); + } +- longjmp(leaving, 2); ++ exit(2); + } + + /* atoi with error detection */ +@@ -599,7 +599,7 @@ static int binop(void) + memset(&re_buffer, 0, sizeof(re_buffer)); + if (regcomp(&re_buffer, opnd2, REG_EXTENDED)) { // REG_NEWLINE? + /* Bad regex */ +- longjmp(leaving, 2); /* [[ a =~ * ]]; echo $? - prints 2 (silently, no error msg) */ ++ exit(2); /* [[ a =~ * ]]; echo $? - prints 2 (silently, no error msg) */ + } + val1 = regexec(&re_buffer, opnd1, 0, NULL, 0); + regfree(&re_buffer); +@@ -929,7 +929,7 @@ int test_main(int argc, char **argv) + bash_test2 = bt2; + #endif + +- res = setjmp(leaving); ++ res = 0; + if (res) + goto ret; + +diff --git a/editors/vi.c b/editors/vi.c +index 2645afe..2f389d7 100644 +--- a/editors/vi.c ++++ b/editors/vi.c +@@ -4855,6 +4855,9 @@ static void edit_file(char *fn) + // int_handler() can jump to "restart", + // must install handler *after* initializing "restart" + signal(SIGINT, int_handler); ++#else ++ // It's fair to not quit vi by accidental ctrl+c... ++ signal(SIGINT, SIG_IGN); + #endif + + cmd_mode = 0; // 0=command 1=insert 2='R'eplace +diff --git a/init/init.c b/init/init.c +index 1e1ce83..0cf6da4 100644 +--- a/init/init.c ++++ b/init/init.c +@@ -135,6 +135,7 @@ + # include + #endif + #include "reboot.h" /* reboot() constants */ ++#include + + #if DEBUG_SEGV_HANDLER + # undef _GNU_SOURCE +@@ -475,22 +476,9 @@ static void init_exec(const char *command) + /* returns if execvp fails */ + } + +-/* Used only by run_actions */ +-static pid_t run(const struct init_action *a) ++static pid_t run_child(void *arg) + { +- pid_t pid; +- +- if (BB_MMU && (a->action_type & ASKFIRST)) +- pid = fork(); +- else +- pid = vfork(); +- if (pid) { +- if (pid < 0) +- message(L_LOG | L_CONSOLE, "can't fork"); +- return pid; /* Parent or error */ +- } +- +- /* Child */ ++ const struct init_action *a = arg; + + /* Reset signal handlers that were set by the parent process */ + reset_sighandlers_and_unblock_sigs(); +@@ -553,6 +541,25 @@ static pid_t run(const struct init_action *a) + _exit(-1); + } + ++/* Used only by run_actions */ ++static pid_t run(const struct init_action *a) ++{ ++ pid_t pid; ++ ++ if (BB_MMU && (a->action_type & ASKFIRST)) ++ pid = fork(); ++ else ++ pid = clone(run_child, NULL, CLONE_VM | CLONE_VFORK | SIGCHLD, a); ++ if (pid) { ++ if (pid < 0) ++ message(L_LOG | L_CONSOLE, "can't fork"); ++ return pid; /* Parent or error */ ++ } ++ ++ abort(); ++ return -1; ++} ++ + static struct init_action *mark_terminated(pid_t pid) + { + struct init_action *a; +diff --git a/scripts/Makefile.build b/scripts/Makefile.build +index 5eac45f..5dea130 100644 +--- a/scripts/Makefile.build ++++ b/scripts/Makefile.build +@@ -257,7 +257,7 @@ quiet_cmd_link_o_target = LD $@ + # If the list of objects to link is empty, just create an empty built-in.o + # -nostdlib is added to make "make LD=gcc ..." work (some people use that) + cmd_link_o_target = $(if $(strip $(obj-y)),\ +- $(LD) -nostdlib $(ld_flags) -r -o $@ $(filter $(obj-y), $^),\ ++ $(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^),\ + rm -f $@; $(AR) rcs $@) + + $(builtin-target): $(obj-y) FORCE +diff --git a/scripts/gcc-version.sh b/scripts/gcc-version.sh +index 0eb27c7..70887a6 100755 +--- a/scripts/gcc-version.sh ++++ b/scripts/gcc-version.sh +@@ -6,6 +6,9 @@ + # such as `0295' for gcc-2.95, `0303' for gcc-3.3, etc. + # + ++printf '1600' ++exit 0 ++ + compiler="$*" + # tr -d '\r': fix up msdos-style line endings (Cygwin et al) + MAJ_MIN=$(echo __GNUC__ __GNUC_MINOR__ | $compiler -E -xc - | tr -d '\r' | tail -n 1) +diff --git a/scripts/trylink b/scripts/trylink +index 2255dee..cafb1ab 100755 +--- a/scripts/trylink ++++ b/scripts/trylink +@@ -75,7 +75,7 @@ check_libc_is_glibc() { + EXE="$1" + CC="$2" + CFLAGS="$3" +-LDFLAGS="$4" ++LDFLAGS="$4 -Wl,--export-all -Wl,--import-table -Wl,--import-memory -Wl,--shared-memory -Wl,--max-memory=4294967296 -Wl,--no-merge-data-segments -Wl,-no-gc-sections -Wl,--import-undefined -Wl,-shared" + O_FILES="$5" + A_FILES="$6" + # We try to drop libraries from LDLIBS if build works without them, +@@ -93,10 +93,10 @@ if ! check_cc "-Wl,--sort-section,alignment"; then + SORT_SECTION="" + fi + +-START_GROUP="-Wl,--start-group" +-END_GROUP="-Wl,--end-group" ++START_GROUP="" ++END_GROUP="" + INFO_OPTS() { +- echo "-Wl,--warn-common -Wl,-Map,$EXE.map -Wl,--verbose" ++ echo "-Wl,-Map,$EXE.map -Wl,--verbose" + } + + # gold may not support --sort-common (yet) +diff --git a/shell/hush.c b/shell/hush.c +index d111f0c..c634ffa 100644 +--- a/shell/hush.c ++++ b/shell/hush.c +@@ -353,6 +353,7 @@ + #if ENABLE_HUSH_CASE + # include + #endif ++#include + #include + #include /* for setting $HOSTNAME */ + +@@ -9229,6 +9230,71 @@ static int redirect_and_varexp_helper( + + return setup_redirects(command, sqp); + } ++ ++static int run_pipe_child(void *arg) { ++ struct pipe *pi = ((void **)arg)[0]; ++ struct fd_pair *pipefds = ((void **)arg)[1]; ++ int next_infd = *(int*)((void **)arg)[2]; ++ volatile nommu_save_t *nommu_save = ((void **)arg)[3]; ++ struct command *command = ((void **)arg)[4]; ++ char **argv_expanded = ((void **)arg)[5]; ++ ++#if ENABLE_HUSH_JOB ++ disable_restore_tty_pgrp_on_exit(); ++ CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */ ++ ++ /* Every child adds itself to new process group ++ * with pgid == pid_of_first_child_in_pipe */ ++ if (G.run_list_level == 1 && G_interactive_fd) { ++ pid_t pgrp; ++ pgrp = pi->pgrp; ++ if (pgrp < 0) /* true for 1st process only */ ++ pgrp = getpid(); ++ if (setpgid(0, pgrp) == 0 ++ && pi->followup != PIPE_BG ++ && G_saved_tty_pgrp /* we have ctty */ ++ ) { ++ /* We do it in *every* child, not just first, ++ * to avoid races */ ++ tcsetpgrp(G_interactive_fd, pgrp); ++ } ++ } ++#endif ++ if (pi->alive_cmds == 0 && pi->followup == PIPE_BG) { ++ /* 1st cmd in backgrounded pipe ++ * should have its stdin /dev/null'ed */ ++ close(0); ++ if (open(bb_dev_null, O_RDONLY)) ++ xopen("/", O_RDONLY); ++ } else { ++ xmove_fd(next_infd, 0); ++ } ++ xmove_fd(pipefds->wr, 1); ++ if (pipefds->rd > 1) ++ close(pipefds->rd); ++ /* Like bash, explicit redirects override pipes, ++ * and the pipe fd (fd#1) is available for dup'ing: ++ * "cmd1 2>&1 | cmd2": fd#1 is duped to fd#2, thus stderr ++ * of cmd1 goes into pipe. ++ */ ++ if (setup_redirects(command, NULL)) { ++ /* Happens when redir file can't be opened: ++ * $ hush -c 'echo FOO >&2 | echo BAR 3>/qwe/rty; echo BAZ' ++ * FOO ++ * hush: can't open '/qwe/rty': No such file or directory ++ * BAZ ++ * (echo BAR is not executed, it hits _exit(1) below) ++ */ ++ return 1; ++ } ++ ++ /* Stores to nommu_save list of env vars putenv'ed ++ * (NOMMU, on MMU we don't need that) */ ++ /* cast away volatility... */ ++ pseudo_exec((nommu_save_t*)nommu_save, command, argv_expanded); ++ /* pseudo_exec() does not return */ ++} ++ + static NOINLINE int run_pipe(struct pipe *pi) + { + static const char *const null_ptr = NULL; +@@ -9584,63 +9650,22 @@ static NOINLINE int run_pipe(struct pipe *pi) + G.execute_lineno = command->lineno; + #endif + +- command->pid = BB_MMU ? fork() : vfork(); +- if (!command->pid) { /* child */ +-#if ENABLE_HUSH_JOB +- disable_restore_tty_pgrp_on_exit(); +- CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */ +- +- /* Every child adds itself to new process group +- * with pgid == pid_of_first_child_in_pipe */ +- if (G.run_list_level == 1 && G_interactive_fd) { +- pid_t pgrp; +- pgrp = pi->pgrp; +- if (pgrp < 0) /* true for 1st process only */ +- pgrp = getpid(); +- if (setpgid(0, pgrp) == 0 +- && pi->followup != PIPE_BG +- && G_saved_tty_pgrp /* we have ctty */ +- ) { +- /* We do it in *every* child, not just first, +- * to avoid races */ +- tcsetpgrp(G_interactive_fd, pgrp); +- } +- } +-#endif +- if (pi->alive_cmds == 0 && pi->followup == PIPE_BG) { +- /* 1st cmd in backgrounded pipe +- * should have its stdin /dev/null'ed */ +- close(0); +- if (open(bb_dev_null, O_RDONLY)) +- xopen("/", O_RDONLY); +- } else { +- xmove_fd(next_infd, 0); +- } +- xmove_fd(pipefds.wr, 1); +- if (pipefds.rd > 1) +- close(pipefds.rd); +- /* Like bash, explicit redirects override pipes, +- * and the pipe fd (fd#1) is available for dup'ing: +- * "cmd1 2>&1 | cmd2": fd#1 is duped to fd#2, thus stderr +- * of cmd1 goes into pipe. +- */ +- if (setup_redirects(command, NULL)) { +- /* Happens when redir file can't be opened: +- * $ hush -c 'echo FOO >&2 | echo BAR 3>/qwe/rty; echo BAZ' +- * FOO +- * hush: can't open '/qwe/rty': No such file or directory +- * BAZ +- * (echo BAR is not executed, it hits _exit(1) below) +- */ +- _exit(1); +- } +- +- /* Stores to nommu_save list of env vars putenv'ed +- * (NOMMU, on MMU we don't need that) */ +- /* cast away volatility... */ +- pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded); +- /* pseudo_exec() does not return */ ++ void *arg[] = { ++ pi, ++ &pipefds, ++ &next_infd, ++ &nommu_save, ++ command, ++ argv_expanded ++ }; ++#if BB_MMU ++ command->pid = fork(); ++ if (command->pid == 0) { ++ run_pipe_child(arg); + } ++#else ++ command->pid = clone(run_pipe_child, NULL, CLONE_VM | CLONE_VFORK | SIGCHLD, arg); ++#endif + + /* parent or error */ + #if ENABLE_HUSH_FAST +diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c +index 20e7d56..b4af9a0 100644 +--- a/util-linux/fdisk.c ++++ b/util-linux/fdisk.c +@@ -611,10 +611,6 @@ valid_part_table_flag(const char *mbuffer) + + static void fdisk_fatal(const char *why) + { +- if (listing) { +- close_dev_fd(); +- longjmp(listingbuf, 1); +- } + bb_error_msg_and_die(why, disk_device); + } + +@@ -2926,8 +2922,6 @@ open_list_and_close(const char *device, int user_specified) + int gb; + + disk_device = device; +- if (setjmp(listingbuf)) +- return; + if (!user_specified) + if (is_ide_cdrom_or_tape(device)) + return; +-- +2.25.1 + diff --git a/patches/initramfs/init b/patches/initramfs/init new file mode 100755 index 0000000..cb7e498 --- /dev/null +++ b/patches/initramfs/init @@ -0,0 +1,14 @@ +#!/bin/sh + +mount -t proc none /proc +mount -t sysfs none /sys + +echo +echo "Welcome to Linux running on Wasm!" +echo "Known bugs:" +echo " * Terminal input freezes after a while (timer issue)." +echo " * dup_fd, wq_worker_comm, rcu_os crash (memory corruption (fixed?))." +echo " * longjmp() does not work (not implemented yet)." +echo + +exec /sbin/init -s diff --git a/patches/initramfs/initramfs-base.cpio b/patches/initramfs/initramfs-base.cpio new file mode 100644 index 0000000..623161c Binary files /dev/null and b/patches/initramfs/initramfs-base.cpio differ diff --git a/patches/kernel/0001-Always-access-the-instruction-pointer-intrinsic-via-.patch b/patches/kernel/0001-Always-access-the-instruction-pointer-intrinsic-via-.patch new file mode 100644 index 0000000..1d7dc72 --- /dev/null +++ b/patches/kernel/0001-Always-access-the-instruction-pointer-intrinsic-via-.patch @@ -0,0 +1,1681 @@ +From a6e4d4d45eaa7dd9878754156643400d0aa4b887 Mon Sep 17 00:00:00 2001 +From: Joel Severin +Date: Sat, 16 Dec 2023 13:14:24 +0100 +Subject: [PATCH] Always access the instruction pointer intrinsic via macros + +This allows architectures that do not really have an instruction pointer, +at least not a visible one, to use 0 instead (as a sentinel/dummy value). + +The instruction pointer is used for debugging purposes in general kernel +code. Code that actually uses it as a jump target is arch-specific, and +there is thus no risk in not using the real program counter value. +--- + arch/Kconfig | 5 ++++ + arch/arm/kernel/return_address.c | 2 +- + arch/arm/kernel/stacktrace.c | 2 +- + arch/arm/kernel/unwind.c | 3 +-- + drivers/dma/idxd/bus.c | 2 +- + drivers/gpu/drm/drm_print.c | 12 +++++----- + .../drm/i915/gem/selftests/i915_gem_context.c | 4 ++-- + drivers/gpu/drm/i915/i915_utils.c | 2 +- + drivers/gpu/drm/i915/intel_pcode.c | 4 ++-- + .../gpu/drm/i915/selftests/igt_flush_test.c | 4 ++-- + drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 2 +- + drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 2 +- + .../gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.c | 6 ++--- + drivers/infiniband/hw/irdma/cm.c | 18 +++++++------- + drivers/infiniband/hw/irdma/hw.c | 4 ++-- + drivers/infiniband/hw/irdma/verbs.c | 4 ++-- + drivers/iommu/dma-iommu.c | 4 ++-- + drivers/isdn/hardware/mISDN/avmfritz.c | 2 +- + drivers/isdn/hardware/mISDN/hfcmulti.c | 4 ++-- + drivers/isdn/hardware/mISDN/hfcpci.c | 4 ++-- + drivers/isdn/hardware/mISDN/hfcsusb.c | 4 ++-- + drivers/isdn/hardware/mISDN/mISDNipac.c | 6 ++--- + drivers/isdn/hardware/mISDN/netjet.c | 2 +- + drivers/isdn/hardware/mISDN/speedfax.c | 2 +- + drivers/isdn/hardware/mISDN/w6692.c | 4 ++-- + drivers/isdn/mISDN/l1oip_core.c | 4 ++-- + drivers/mfd/mc13xxx-core.c | 6 ++--- + drivers/mtd/ubi/misc.c | 4 ++-- + drivers/nvdimm/bus.c | 2 +- + drivers/nvdimm/dimm_devs.c | 2 +- + drivers/parisc/superio.c | 2 +- + drivers/scsi/arm/fas216.c | 2 +- + drivers/scsi/esp_scsi.c | 2 +- + drivers/usb/gadget/function/u_serial.c | 2 +- + drivers/usb/musb/musb_core.c | 12 +++++----- + fs/afs/rxrpc.c | 10 ++++---- + fs/f2fs/f2fs.h | 2 +- + fs/hfs/bfind.c | 4 ++-- + fs/hfsplus/bfind.c | 4 ++-- + fs/jfs/super.c | 2 +- + fs/ocfs2/cluster/heartbeat.c | 4 ++-- + fs/ocfs2/dlm/dlmmaster.c | 8 +++---- + fs/ubifs/misc.c | 4 ++-- + fs/xfs/xfs_buf.h | 2 +- + fs/xfs/xfs_linux.h | 6 ++++- + include/linux/ftrace.h | 2 +- + include/linux/instruction_pointer.h | 5 ++++ + kernel/bpf/syscall.c | 2 +- + kernel/cred.c | 2 +- + kernel/dma/direct.c | 2 +- + kernel/dma/pool.c | 2 +- + kernel/fork.c | 2 +- + kernel/module/main.c | 2 +- + kernel/panic.c | 7 +++--- + kernel/sched/core.c | 2 +- + kernel/scs.c | 2 +- + lib/smp_processor_id.c | 2 +- + lib/test_kprobes.c | 10 ++++---- + lib/test_vmalloc.c | 6 ++--- + mm/ioremap.c | 2 +- + mm/kasan/shadow.c | 4 ++-- + mm/kmsan/instrumentation.c | 2 +- + mm/util.c | 2 +- + mm/vmalloc.c | 24 +++++++++---------- + net/core/skbuff.c | 14 +++++------ + net/netfilter/ipvs/ip_vs_conn.c | 2 +- + net/netfilter/ipvs/ip_vs_ctl.c | 4 ++-- + net/smc/smc_core.c | 4 ++-- + net/tipc/crypto.c | 2 +- + samples/bpf/tracex2.bpf.c | 2 +- + sound/core/device.c | 4 ++-- + 71 files changed, 159 insertions(+), 147 deletions(-) + +diff --git a/arch/Kconfig b/arch/Kconfig +index 171e6b5e6..2111578c8 100644 +--- a/arch/Kconfig ++++ b/arch/Kconfig +@@ -225,6 +225,11 @@ config TRACE_IRQFLAGS_SUPPORT + config TRACE_IRQFLAGS_NMI_SUPPORT + bool + ++config NO_IP ++ bool ++ help ++ Enable for architectures without (visible) instruction pointer. ++ + # + # An arch should select this if it provides all these things: + # +diff --git a/arch/arm/kernel/return_address.c b/arch/arm/kernel/return_address.c +index ac15db66d..ae0f70c52 100644 +--- a/arch/arm/kernel/return_address.c ++++ b/arch/arm/kernel/return_address.c +@@ -42,7 +42,7 @@ void *return_address(unsigned int level) + frame.sp = current_stack_pointer; + frame.lr = (unsigned long)__builtin_return_address(0); + here: +- frame.pc = (unsigned long)&&here; ++ frame.pc = _THIS_IP_; + #ifdef CONFIG_KRETPROBES + frame.kr_cur = NULL; + frame.tsk = current; +diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c +index 620aa82e3..af8c2d6fd 100644 +--- a/arch/arm/kernel/stacktrace.c ++++ b/arch/arm/kernel/stacktrace.c +@@ -186,7 +186,7 @@ void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie, + (unsigned long)__builtin_frame_address(0), + current_stack_pointer, + (unsigned long)__builtin_return_address(0), +- (unsigned long)&&here); ++ _THIS_IP_); + /* skip this function */ + if (unwind_frame(&frame)) + return; +diff --git a/arch/arm/kernel/unwind.c b/arch/arm/kernel/unwind.c +index 9d2192156..4e4741742 100644 +--- a/arch/arm/kernel/unwind.c ++++ b/arch/arm/kernel/unwind.c +@@ -542,8 +542,7 @@ void unwind_backtrace(struct pt_regs *regs, struct task_struct *tsk, + * point, so we should ensure that frame.pc is within + * this block of code. + */ +-here: +- frame.pc = (unsigned long)&&here; ++ frame.pc = _THIS_IP_; + } else { + /* task blocked in __switch_to */ + frame.fp = thread_saved_fp(tsk); +diff --git a/drivers/dma/idxd/bus.c b/drivers/dma/idxd/bus.c +index 6f8462105..3e5b2d9b8 100644 +--- a/drivers/dma/idxd/bus.c ++++ b/drivers/dma/idxd/bus.c +@@ -13,7 +13,7 @@ int __idxd_driver_register(struct idxd_device_driver *idxd_drv, struct module *o + struct device_driver *drv = &idxd_drv->drv; + + if (!idxd_drv->type) { +- pr_debug("driver type not set (%ps)\n", __builtin_return_address(0)); ++ pr_debug("driver type not set (%ps)\n", _RET_IP_); + return -EINVAL; + } + +diff --git a/drivers/gpu/drm/drm_print.c b/drivers/gpu/drm/drm_print.c +index 5b93c1189..5e6187bbf 100644 +--- a/drivers/gpu/drm/drm_print.c ++++ b/drivers/gpu/drm/drm_print.c +@@ -270,10 +270,10 @@ void drm_dev_printk(const struct device *dev, const char *level, + + if (dev) + dev_printk(level, dev, "[" DRM_NAME ":%ps] %pV", +- __builtin_return_address(0), &vaf); ++ _RET_IP_, &vaf); + else + printk("%s" "[" DRM_NAME ":%ps] %pV", +- level, __builtin_return_address(0), &vaf); ++ level, _RET_IP_, &vaf); + + va_end(args); + } +@@ -295,10 +295,10 @@ void __drm_dev_dbg(struct _ddebug *desc, const struct device *dev, + + if (dev) + dev_printk(KERN_DEBUG, dev, "[" DRM_NAME ":%ps] %pV", +- __builtin_return_address(0), &vaf); ++ _RET_IP_, &vaf); + else + printk(KERN_DEBUG "[" DRM_NAME ":%ps] %pV", +- __builtin_return_address(0), &vaf); ++ _RET_IP_, &vaf); + + va_end(args); + } +@@ -317,7 +317,7 @@ void ___drm_dbg(struct _ddebug *desc, enum drm_debug_category category, const ch + vaf.va = &args; + + printk(KERN_DEBUG "[" DRM_NAME ":%ps] %pV", +- __builtin_return_address(0), &vaf); ++ _RET_IP_, &vaf); + + va_end(args); + } +@@ -333,7 +333,7 @@ void __drm_err(const char *format, ...) + vaf.va = &args; + + printk(KERN_ERR "[" DRM_NAME ":%ps] *ERROR* %pV", +- __builtin_return_address(0), &vaf); ++ _RET_IP_, &vaf); + + va_end(args); + } +diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c +index 7b516b1a4..35f1647b3 100644 +--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c ++++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c +@@ -525,7 +525,7 @@ static noinline int cpu_check(struct drm_i915_gem_object *obj, + for (m = 0; m < max; m++) { + if (map[m] != m) { + pr_err("%pS: Invalid value at object %d page %ld/%ld, offset %d/%d: found %x expected %x\n", +- __builtin_return_address(0), idx, ++ _RET_IP_, idx, + n, real_page_count(obj), m, max, + map[m], m); + err = -EINVAL; +@@ -536,7 +536,7 @@ static noinline int cpu_check(struct drm_i915_gem_object *obj, + for (; m < DW_PER_PAGE; m++) { + if (map[m] != STACK_MAGIC) { + pr_err("%pS: Invalid value at object %d page %ld, offset %d: found %x expected %x (uninitialised)\n", +- __builtin_return_address(0), idx, n, m, ++ _RET_IP_, idx, n, m, + map[m], STACK_MAGIC); + err = -EINVAL; + goto out_unmap; +diff --git a/drivers/gpu/drm/i915/i915_utils.c b/drivers/gpu/drm/i915/i915_utils.c +index 29fd02bf5..e93475acb 100644 +--- a/drivers/gpu/drm/i915/i915_utils.c ++++ b/drivers/gpu/drm/i915/i915_utils.c +@@ -35,7 +35,7 @@ __i915_printk(struct drm_i915_private *dev_priv, const char *level, + dev_printk(level, kdev, "%pV", &vaf); + else + dev_printk(level, kdev, "[" DRM_NAME ":%ps] %pV", +- __builtin_return_address(0), &vaf); ++ _RET_IP_, &vaf); + + va_end(args); + +diff --git a/drivers/gpu/drm/i915/intel_pcode.c b/drivers/gpu/drm/i915/intel_pcode.c +index 3db2ba439..78850c3a3 100644 +--- a/drivers/gpu/drm/i915/intel_pcode.c ++++ b/drivers/gpu/drm/i915/intel_pcode.c +@@ -103,7 +103,7 @@ int snb_pcode_read(struct intel_uncore *uncore, u32 mbox, u32 *val, u32 *val1) + if (err) { + drm_dbg(&uncore->i915->drm, + "warning: pcode (read from mbox %x) mailbox access failed for %ps: %d\n", +- mbox, __builtin_return_address(0), err); ++ mbox, _RET_IP_, err); + } + + return err; +@@ -122,7 +122,7 @@ int snb_pcode_write_timeout(struct intel_uncore *uncore, u32 mbox, u32 val, + if (err) { + drm_dbg(&uncore->i915->drm, + "warning: pcode (write of 0x%08x to mbox %x) mailbox access failed for %ps: %d\n", +- val, mbox, __builtin_return_address(0), err); ++ val, mbox, _RET_IP_, err); + } + + return err; +diff --git a/drivers/gpu/drm/i915/selftests/igt_flush_test.c b/drivers/gpu/drm/i915/selftests/igt_flush_test.c +index 29110abb4..80fe1a252 100644 +--- a/drivers/gpu/drm/i915/selftests/igt_flush_test.c ++++ b/drivers/gpu/drm/i915/selftests/igt_flush_test.c +@@ -26,10 +26,10 @@ int igt_flush_test(struct drm_i915_private *i915) + + if (intel_gt_wait_for_idle(gt, HZ * 3) == -ETIME) { + pr_err("%pS timed out, cancelling all further testing.\n", +- __builtin_return_address(0)); ++ _RET_IP_); + + GEM_TRACE("%pS timed out.\n", +- __builtin_return_address(0)); ++ _RET_IP_); + GEM_TRACE_DUMP(); + + intel_gt_set_wedged(gt); +diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +index bbb1bf33f..84eade7ec 100644 +--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c ++++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +@@ -1082,7 +1082,7 @@ bool a5xx_idle(struct msm_gpu *gpu, struct msm_ringbuffer *ring) + + if (spin_until(_a5xx_check_idle(gpu))) { + DRM_ERROR("%s: %ps: timeout waiting for GPU to idle: status %8.8X irq %8.8X rptr/wptr %d/%d\n", +- gpu->name, __builtin_return_address(0), ++ gpu->name, _RET_IP_, + gpu_read(gpu, REG_A5XX_RBBM_STATUS), + gpu_read(gpu, REG_A5XX_RBBM_INT_0_STATUS), + gpu_read(gpu, REG_A5XX_CP_RB_RPTR), +diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +index bdda1a633..f8d66ecb7 100644 +--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c ++++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +@@ -41,7 +41,7 @@ static bool a6xx_idle(struct msm_gpu *gpu, struct msm_ringbuffer *ring) + + if (spin_until(_a6xx_check_idle(gpu))) { + DRM_ERROR("%s: %ps: timeout waiting for GPU to idle: status %8.8X irq %8.8X rptr/wptr %d/%d\n", +- gpu->name, __builtin_return_address(0), ++ gpu->name, _RET_IP_, + gpu_read(gpu, REG_A6XX_RBBM_STATUS), + gpu_read(gpu, REG_A6XX_RBBM_INT_0_STATUS), + gpu_read(gpu, REG_A6XX_CP_RB_RPTR), +diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.c +index 17f3e7e4f..86ead4fea 100644 +--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.c ++++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_interrupts.c +@@ -392,7 +392,7 @@ u32 dpu_core_irq_read(struct dpu_kms *dpu_kms, int irq_idx) + + if (irq_idx < 0) { + DPU_ERROR("[%pS] invalid irq_idx=%d\n", +- __builtin_return_address(0), irq_idx); ++ _RET_IP_, irq_idx); + return 0; + } + +@@ -471,7 +471,7 @@ int dpu_core_irq_register_callback(struct dpu_kms *dpu_kms, int irq_idx, + return -EINVAL; + } + +- VERB("[%pS] irq_idx=%d\n", __builtin_return_address(0), irq_idx); ++ VERB("[%pS] irq_idx=%d\n", _RET_IP_, irq_idx); + + spin_lock_irqsave(&dpu_kms->hw_intr->irq_lock, irq_flags); + +@@ -508,7 +508,7 @@ int dpu_core_irq_unregister_callback(struct dpu_kms *dpu_kms, int irq_idx) + return -EINVAL; + } + +- VERB("[%pS] irq_idx=%d\n", __builtin_return_address(0), irq_idx); ++ VERB("[%pS] irq_idx=%d\n", _RET_IP_, irq_idx); + + spin_lock_irqsave(&dpu_kms->hw_intr->irq_lock, irq_flags); + trace_dpu_core_irq_unregister_callback(irq_idx); +diff --git a/drivers/infiniband/hw/irdma/cm.c b/drivers/infiniband/hw/irdma/cm.c +index 70009b970..2852ba1d3 100644 +--- a/drivers/infiniband/hw/irdma/cm.c ++++ b/drivers/infiniband/hw/irdma/cm.c +@@ -165,7 +165,7 @@ static int irdma_send_cm_event(struct irdma_cm_node *cm_node, + event.event = type; + event.status = status; + trace_irdma_send_cm_event(cm_node, cm_id, type, status, +- __builtin_return_address(0)); ++ _RET_IP_); + + ibdev_dbg(&cm_node->iwdev->ibdev, + "CM: cm_node %p cm_id=%p state=%d accel=%d event_type=%d status=%d\n", +@@ -252,7 +252,7 @@ static struct irdma_cm_event *irdma_create_event(struct irdma_cm_node *cm_node, + "CM: node=%p event=%p type=%u dst=%pI4 src=%pI4\n", cm_node, + event, type, event->cm_info.loc_addr, + event->cm_info.rem_addr); +- trace_irdma_create_event(cm_node, type, __builtin_return_address(0)); ++ trace_irdma_create_event(cm_node, type, _RET_IP_); + irdma_cm_post_event(event); + + return event; +@@ -595,7 +595,7 @@ int irdma_send_reset(struct irdma_cm_node *cm_node) + struct irdma_puda_buf *sqbuf; + int flags = SET_RST | SET_ACK; + +- trace_irdma_send_reset(cm_node, 0, __builtin_return_address(0)); ++ trace_irdma_send_reset(cm_node, 0, _RET_IP_); + sqbuf = cm_node->cm_core->form_cm_frame(cm_node, NULL, NULL, NULL, + flags); + if (!sqbuf) +@@ -603,7 +603,7 @@ int irdma_send_reset(struct irdma_cm_node *cm_node) + + ibdev_dbg(&cm_node->iwdev->ibdev, + "CM: caller: %pS cm_node %p cm_id=%p accel=%d state=%d rem_port=0x%04x, loc_port=0x%04x rem_addr=%pI4 loc_addr=%pI4\n", +- __builtin_return_address(0), cm_node, cm_node->cm_id, ++ _RET_IP_, cm_node, cm_node->cm_id, + cm_node->accelerated, cm_node->state, cm_node->rem_port, + cm_node->loc_port, cm_node->rem_addr, cm_node->loc_addr); + +@@ -619,7 +619,7 @@ int irdma_send_reset(struct irdma_cm_node *cm_node) + static void irdma_active_open_err(struct irdma_cm_node *cm_node, bool reset) + { + trace_irdma_active_open_err(cm_node, reset, +- __builtin_return_address(0)); ++ _RET_IP_); + irdma_cleanup_retrans_entry(cm_node); + cm_node->cm_core->stats_connect_errs++; + if (reset) { +@@ -647,7 +647,7 @@ static void irdma_passive_open_err(struct irdma_cm_node *cm_node, bool reset) + ibdev_dbg(&cm_node->iwdev->ibdev, "CM: cm_node=%p state =%d\n", + cm_node, cm_node->state); + trace_irdma_passive_open_err(cm_node, reset, +- __builtin_return_address(0)); ++ _RET_IP_); + if (reset) + irdma_send_reset(cm_node); + else +@@ -1837,7 +1837,7 @@ static int irdma_dec_refcnt_listen(struct irdma_cm_core *cm_core, + enum irdma_cm_node_state old_state; + unsigned long flags; + +- trace_irdma_dec_refcnt_listen(listener, __builtin_return_address(0)); ++ trace_irdma_dec_refcnt_listen(listener, _RET_IP_); + /* free non-accelerated child nodes for this listener */ + INIT_LIST_HEAD(&reset_list); + if (free_hanging_nodes) { +@@ -2374,7 +2374,7 @@ void irdma_rem_ref_cm_node(struct irdma_cm_node *cm_node) + struct irdma_cm_core *cm_core = cm_node->cm_core; + unsigned long flags; + +- trace_irdma_rem_ref_cm_node(cm_node, 0, __builtin_return_address(0)); ++ trace_irdma_rem_ref_cm_node(cm_node, 0, _RET_IP_); + spin_lock_irqsave(&cm_core->ht_lock, flags); + + if (!refcount_dec_and_test(&cm_node->refcnt)) { +@@ -2462,7 +2462,7 @@ static void irdma_handle_rst_pkt(struct irdma_cm_node *cm_node, + { + ibdev_dbg(&cm_node->iwdev->ibdev, + "CM: caller: %pS cm_node=%p state=%d rem_port=0x%04x loc_port=0x%04x rem_addr=%pI4 loc_addr=%pI4\n", +- __builtin_return_address(0), cm_node, cm_node->state, ++ _RET_IP_, cm_node, cm_node->state, + cm_node->rem_port, cm_node->loc_port, cm_node->rem_addr, + cm_node->loc_addr); + +diff --git a/drivers/infiniband/hw/irdma/hw.c b/drivers/infiniband/hw/irdma/hw.c +index 457368e32..6734d4aae 100644 +--- a/drivers/infiniband/hw/irdma/hw.c ++++ b/drivers/infiniband/hw/irdma/hw.c +@@ -2485,7 +2485,7 @@ int irdma_manage_qhash(struct irdma_device *iwdev, struct irdma_cm_info *cminfo, + ibdev_dbg(&iwdev->ibdev, + "CM: %s caller: %pS loc_port=0x%04x rem_port=0x%04x loc_addr=%pI4 rem_addr=%pI4 mac=%pM, vlan_id=%d cm_node=%p\n", + (!mtype) ? "DELETE" : "ADD", +- __builtin_return_address(0), info->dest_port, ++ _RET_IP_, info->dest_port, + info->src_port, info->dest_ip, info->src_ip, + info->mac_addr, cminfo->vlan_id, + cmnode ? cmnode : NULL); +@@ -2493,7 +2493,7 @@ int irdma_manage_qhash(struct irdma_device *iwdev, struct irdma_cm_info *cminfo, + ibdev_dbg(&iwdev->ibdev, + "CM: %s caller: %pS loc_port=0x%04x rem_port=0x%04x loc_addr=%pI6 rem_addr=%pI6 mac=%pM, vlan_id=%d cm_node=%p\n", + (!mtype) ? "DELETE" : "ADD", +- __builtin_return_address(0), info->dest_port, ++ _RET_IP_, info->dest_port, + info->src_port, info->dest_ip, info->src_ip, + info->mac_addr, cminfo->vlan_id, + cmnode ? cmnode : NULL); +diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c +index 20d70f0d2..2b21f0a7a 100644 +--- a/drivers/infiniband/hw/irdma/verbs.c ++++ b/drivers/infiniband/hw/irdma/verbs.c +@@ -1287,7 +1287,7 @@ int irdma_modify_qp_roce(struct ib_qp *ibqp, struct ib_qp_attr *attr, + + ibdev_dbg(&iwdev->ibdev, + "VERBS: caller: %pS qp_id=%d to_ibqpstate=%d ibqpstate=%d irdma_qpstate=%d attr_mask=0x%x\n", +- __builtin_return_address(0), ibqp->qp_num, attr->qp_state, ++ _RET_IP_, ibqp->qp_num, attr->qp_state, + iwqp->ibqp_state, iwqp->iwarp_state, attr_mask); + + spin_lock_irqsave(&iwqp->lock, flags); +@@ -1492,7 +1492,7 @@ int irdma_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask, + wait_event(iwqp->mod_qp_waitq, !atomic_read(&iwqp->hw_mod_qp_pend)); + ibdev_dbg(&iwdev->ibdev, + "VERBS: caller: %pS qp_id=%d to_ibqpstate=%d ibqpstate=%d irdma_qpstate=%d last_aeq=%d hw_tcp_state=%d hw_iwarp_state=%d attr_mask=0x%x\n", +- __builtin_return_address(0), ibqp->qp_num, attr->qp_state, ++ _RET_IP_, ibqp->qp_num, attr->qp_state, + iwqp->ibqp_state, iwqp->iwarp_state, iwqp->last_aeq, + iwqp->hw_tcp_state, iwqp->hw_iwarp_state, attr_mask); + +diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c +index 7a9f0b0bd..17ec3dead 100644 +--- a/drivers/iommu/dma-iommu.c ++++ b/drivers/iommu/dma-iommu.c +@@ -876,7 +876,7 @@ static void *iommu_dma_alloc_remap(struct device *dev, size_t size, + *dma_handle = sgt.sgl->dma_address; + sg_free_table(&sgt); + vaddr = dma_common_pages_remap(pages, size, prot, +- __builtin_return_address(0)); ++ _RET_IP_); + if (!vaddr) + goto out_unmap; + return vaddr; +@@ -1429,7 +1429,7 @@ static void *iommu_dma_alloc_pages(struct device *dev, size_t size, + pgprot_t prot = dma_pgprot(dev, PAGE_KERNEL, attrs); + + cpu_addr = dma_common_contiguous_remap(page, alloc_size, +- prot, __builtin_return_address(0)); ++ prot, _RET_IP_); + if (!cpu_addr) + goto out_free_pages; + +diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c +index f68569bfe..9fa4ef477 100644 +--- a/drivers/isdn/hardware/mISDN/avmfritz.c ++++ b/drivers/isdn/hardware/mISDN/avmfritz.c +@@ -935,7 +935,7 @@ avm_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) + break; + case CLOSE_CHANNEL: + pr_debug("%s: dev(%d) close from %p\n", fc->name, dch->dev.id, +- __builtin_return_address(0)); ++ _RET_IP_); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: +diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c +index 2e5cb9dde..1692ab7c7 100644 +--- a/drivers/isdn/hardware/mISDN/hfcmulti.c ++++ b/drivers/isdn/hardware/mISDN/hfcmulti.c +@@ -4020,7 +4020,7 @@ open_dchannel(struct hfc_multi *hc, struct dchannel *dch, + + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__, +- dch->dev.id, __builtin_return_address(0)); ++ dch->dev.id, _RET_IP_); + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + if ((dch->dev.D.protocol != ISDN_P_NONE) && +@@ -4180,7 +4180,7 @@ hfcm_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) close from %p\n", + __func__, dch->dev.id, +- __builtin_return_address(0)); ++ _RET_IP_); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: +diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c +index fe391de1a..59c141f87 100644 +--- a/drivers/isdn/hardware/mISDN/hfcpci.c ++++ b/drivers/isdn/hardware/mISDN/hfcpci.c +@@ -1882,7 +1882,7 @@ open_dchannel(struct hfc_pci *hc, struct mISDNchannel *ch, + + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__, +- hc->dch.dev.id, __builtin_return_address(0)); ++ hc->dch.dev.id, _RET_IP_); + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + if (rq->adr.channel == 1) { +@@ -1973,7 +1973,7 @@ hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) close from %p\n", + __func__, hc->dch.dev.id, +- __builtin_return_address(0)); ++ _RET_IP_); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: +diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.c b/drivers/isdn/hardware/mISDN/hfcsusb.c +index 1efd17979..a3f539268 100644 +--- a/drivers/isdn/hardware/mISDN/hfcsusb.c ++++ b/drivers/isdn/hardware/mISDN/hfcsusb.c +@@ -420,7 +420,7 @@ open_dchannel(struct hfcsusb *hw, struct mISDNchannel *ch, + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: %s: dev(%d) open addr(%i) from %p\n", + hw->name, __func__, hw->dch.dev.id, rq->adr.channel, +- __builtin_return_address(0)); ++ _RET_IP_); + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + +@@ -546,7 +546,7 @@ hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) + printk(KERN_DEBUG + "%s: %s: dev(%d) close from %p (open %d)\n", + hw->name, __func__, hw->dch.dev.id, +- __builtin_return_address(0), hw->open); ++ _RET_IP_, hw->open); + if (!hw->open) { + hfcsusb_stop_endpoint(hw, HFC_CHAN_D); + if (hw->fifos[HFCUSB_PCM_RX].pipe) +diff --git a/drivers/isdn/hardware/mISDN/mISDNipac.c b/drivers/isdn/hardware/mISDN/mISDNipac.c +index 4f8d85bb3..e86063ece 100644 +--- a/drivers/isdn/hardware/mISDN/mISDNipac.c ++++ b/drivers/isdn/hardware/mISDN/mISDNipac.c +@@ -760,7 +760,7 @@ open_dchannel_caller(struct isac_hw *isac, struct channel_req *rq, void *caller) + static int + open_dchannel(struct isac_hw *isac, struct channel_req *rq) + { +- return open_dchannel_caller(isac, rq, __builtin_return_address(0)); ++ return open_dchannel_caller(isac, rq, _RET_IP_); + } + + static const char *ISACVer[] = +@@ -1536,7 +1536,7 @@ ipac_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) + case OPEN_CHANNEL: + rq = arg; + if (rq->protocol == ISDN_P_TE_S0) +- err = open_dchannel_caller(isac, rq, __builtin_return_address(0)); ++ err = open_dchannel_caller(isac, rq, _RET_IP_); + else + err = open_bchannel(ipac, rq); + if (err) +@@ -1546,7 +1546,7 @@ ipac_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) + break; + case CLOSE_CHANNEL: + pr_debug("%s: dev(%d) close from %p\n", ipac->name, +- dch->dev.id, __builtin_return_address(0)); ++ dch->dev.id, _RET_IP_); + module_put(ipac->owner); + break; + case CONTROL_CHANNEL: +diff --git a/drivers/isdn/hardware/mISDN/netjet.c b/drivers/isdn/hardware/mISDN/netjet.c +index 566c790a9..4dc6e9899 100644 +--- a/drivers/isdn/hardware/mISDN/netjet.c ++++ b/drivers/isdn/hardware/mISDN/netjet.c +@@ -890,7 +890,7 @@ nj_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) + break; + case CLOSE_CHANNEL: + pr_debug("%s: dev(%d) close from %p\n", card->name, dch->dev.id, +- __builtin_return_address(0)); ++ _RET_IP_); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: +diff --git a/drivers/isdn/hardware/mISDN/speedfax.c b/drivers/isdn/hardware/mISDN/speedfax.c +index b530c78ec..d4064cbf2 100644 +--- a/drivers/isdn/hardware/mISDN/speedfax.c ++++ b/drivers/isdn/hardware/mISDN/speedfax.c +@@ -256,7 +256,7 @@ sfax_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) + break; + case CLOSE_CHANNEL: + pr_debug("%s: dev(%d) close from %p\n", sf->name, +- dch->dev.id, __builtin_return_address(0)); ++ dch->dev.id, _RET_IP_); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: +diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c +index f3b8db7b4..8c95e1fbf 100644 +--- a/drivers/isdn/hardware/mISDN/w6692.c ++++ b/drivers/isdn/hardware/mISDN/w6692.c +@@ -1186,7 +1186,7 @@ w6692_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) + case OPEN_CHANNEL: + rq = arg; + if (rq->protocol == ISDN_P_TE_S0) +- err = open_dchannel(card, rq, __builtin_return_address(0)); ++ err = open_dchannel(card, rq, _RET_IP_); + else + err = open_bchannel(card, rq); + if (err) +@@ -1196,7 +1196,7 @@ w6692_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) + break; + case CLOSE_CHANNEL: + pr_debug("%s: dev(%d) close from %p\n", card->name, +- dch->dev.id, __builtin_return_address(0)); ++ dch->dev.id, _RET_IP_); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: +diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c +index f010b35a0..450007d17 100644 +--- a/drivers/isdn/mISDN/l1oip_core.c ++++ b/drivers/isdn/mISDN/l1oip_core.c +@@ -979,7 +979,7 @@ open_dchannel(struct l1oip *hc, struct dchannel *dch, struct channel_req *rq) + { + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__, +- dch->dev.id, __builtin_return_address(0)); ++ dch->dev.id, _RET_IP_); + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + if ((dch->dev.D.protocol != ISDN_P_NONE) && +@@ -1067,7 +1067,7 @@ l1oip_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) close from %p\n", + __func__, dch->dev.id, +- __builtin_return_address(0)); ++ _RET_IP_); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: +diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c +index 100057276..5879adca6 100644 +--- a/drivers/mfd/mc13xxx-core.c ++++ b/drivers/mfd/mc13xxx-core.c +@@ -49,19 +49,19 @@ void mc13xxx_lock(struct mc13xxx *mc13xxx) + { + if (!mutex_trylock(&mc13xxx->lock)) { + dev_dbg(mc13xxx->dev, "wait for %s from %ps\n", +- __func__, __builtin_return_address(0)); ++ __func__, _RET_IP_); + + mutex_lock(&mc13xxx->lock); + } + dev_dbg(mc13xxx->dev, "%s from %ps\n", +- __func__, __builtin_return_address(0)); ++ __func__, _RET_IP_); + } + EXPORT_SYMBOL(mc13xxx_lock); + + void mc13xxx_unlock(struct mc13xxx *mc13xxx) + { + dev_dbg(mc13xxx->dev, "%s from %ps\n", +- __func__, __builtin_return_address(0)); ++ __func__, _RET_IP_); + mutex_unlock(&mc13xxx->lock); + } + EXPORT_SYMBOL(mc13xxx_unlock); +diff --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c +index 1794d66b6..72ffe4168 100644 +--- a/drivers/mtd/ubi/misc.c ++++ b/drivers/mtd/ubi/misc.c +@@ -169,7 +169,7 @@ void ubi_warn(const struct ubi_device *ubi, const char *fmt, ...) + vaf.va = &args; + + pr_warn(UBI_NAME_STR "%d warning: %ps: %pV\n", +- ubi->ubi_num, __builtin_return_address(0), &vaf); ++ ubi->ubi_num, _RET_IP_, &vaf); + + va_end(args); + } +@@ -186,6 +186,6 @@ void ubi_err(const struct ubi_device *ubi, const char *fmt, ...) + vaf.va = &args; + + pr_err(UBI_NAME_STR "%d error: %ps: %pV\n", +- ubi->ubi_num, __builtin_return_address(0), &vaf); ++ ubi->ubi_num, _RET_IP_, &vaf); + va_end(args); + } +diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c +index 954dbc105..1b8c859a5 100644 +--- a/drivers/nvdimm/bus.c ++++ b/drivers/nvdimm/bus.c +@@ -603,7 +603,7 @@ int __nd_driver_register(struct nd_device_driver *nd_drv, struct module *owner, + + if (!nd_drv->type) { + pr_debug("driver type bitmask not set (%ps)\n", +- __builtin_return_address(0)); ++ _RET_IP_); + return -EINVAL; + } + +diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c +index 957f7c3d1..982359e79 100644 +--- a/drivers/nvdimm/dimm_devs.c ++++ b/drivers/nvdimm/dimm_devs.c +@@ -47,7 +47,7 @@ static int validate_dimm(struct nvdimm_drvdata *ndd) + rc = nvdimm_check_config_data(ndd->dev); + if (rc) + dev_dbg(ndd->dev, "%ps: %s error: %d\n", +- __builtin_return_address(0), __func__, rc); ++ _RET_IP_, __func__, rc); + return rc; + } + +diff --git a/drivers/parisc/superio.c b/drivers/parisc/superio.c +index e973c6893..b68e99a74 100644 +--- a/drivers/parisc/superio.c ++++ b/drivers/parisc/superio.c +@@ -347,7 +347,7 @@ int superio_fixup_irq(struct pci_dev *pcidev) + printk(KERN_DEBUG "superio_fixup_irq(%s) ven 0x%x dev 0x%x from %ps\n", + pci_name(pcidev), + pcidev->vendor, pcidev->device, +- __builtin_return_address(0)); ++ _RET_IP_); + #endif + + for (i = 0; i < 16; i++) { +diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c +index 4ce0b2d73..3415707c8 100644 +--- a/drivers/scsi/arm/fas216.c ++++ b/drivers/scsi/arm/fas216.c +@@ -368,7 +368,7 @@ static int cmd_ptr; + static void fas216_cmd(FAS216_Info *info, unsigned int command) + { + cmd_list[cmd_ptr].command = command; +- cmd_list[cmd_ptr].from = __builtin_return_address(0); ++ cmd_list[cmd_ptr].from = _RET_IP_; + + cmd_ptr = (cmd_ptr + 1) & 7; + +diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c +index 97816a0e6..2cead0278 100644 +--- a/drivers/scsi/esp_scsi.c ++++ b/drivers/scsi/esp_scsi.c +@@ -1034,7 +1034,7 @@ static int esp_check_spur_intr(struct esp *esp) + static void esp_schedule_reset(struct esp *esp) + { + esp_log_reset("esp_schedule_reset() from %ps\n", +- __builtin_return_address(0)); ++ _RET_IP_); + esp->flags |= ESP_FLAG_RESETTING; + esp_event(esp, ESP_EVENT_RESET); + } +diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c +index 97f07757d..105bd4e1a 100644 +--- a/drivers/usb/gadget/function/u_serial.c ++++ b/drivers/usb/gadget/function/u_serial.c +@@ -756,7 +756,7 @@ static int gs_put_char(struct tty_struct *tty, unsigned char ch) + int status; + + pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %ps\n", +- port->port_num, tty, ch, __builtin_return_address(0)); ++ port->port_num, tty, ch, _RET_IP_); + + spin_lock_irqsave(&port->port_lock, flags); + status = kfifo_put(&port->port_write_buf, ch); +diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c +index ecbd3784b..385d044f4 100644 +--- a/drivers/usb/musb/musb_core.c ++++ b/drivers/usb/musb/musb_core.c +@@ -251,13 +251,13 @@ static u8 musb_default_readb(void __iomem *addr, u32 offset) + { + u8 data = __raw_readb(addr + offset); + +- trace_musb_readb(__builtin_return_address(0), addr, offset, data); ++ trace_musb_readb(_RET_IP_, addr, offset, data); + return data; + } + + static void musb_default_writeb(void __iomem *addr, u32 offset, u8 data) + { +- trace_musb_writeb(__builtin_return_address(0), addr, offset, data); ++ trace_musb_writeb(_RET_IP_, addr, offset, data); + __raw_writeb(data, addr + offset); + } + +@@ -265,13 +265,13 @@ static u16 musb_default_readw(void __iomem *addr, u32 offset) + { + u16 data = __raw_readw(addr + offset); + +- trace_musb_readw(__builtin_return_address(0), addr, offset, data); ++ trace_musb_readw(_RET_IP_, addr, offset, data); + return data; + } + + static void musb_default_writew(void __iomem *addr, u32 offset, u16 data) + { +- trace_musb_writew(__builtin_return_address(0), addr, offset, data); ++ trace_musb_writew(_RET_IP_, addr, offset, data); + __raw_writew(data, addr + offset); + } + +@@ -419,14 +419,14 @@ u32 musb_readl(void __iomem *addr, u32 offset) + { + u32 data = __raw_readl(addr + offset); + +- trace_musb_readl(__builtin_return_address(0), addr, offset, data); ++ trace_musb_readl(_RET_IP_, addr, offset, data); + return data; + } + EXPORT_SYMBOL_GPL(musb_readl); + + void musb_writel(void __iomem *addr, u32 offset, u32 data) + { +- trace_musb_writel(__builtin_return_address(0), addr, offset, data); ++ trace_musb_writel(_RET_IP_, addr, offset, data); + __raw_writel(data, addr + offset); + } + EXPORT_SYMBOL_GPL(musb_writel); +diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c +index ed1644e76..2fb9ac566 100644 +--- a/fs/afs/rxrpc.c ++++ b/fs/afs/rxrpc.c +@@ -155,7 +155,7 @@ static struct afs_call *afs_alloc_call(struct afs_net *net, + + o = atomic_inc_return(&net->nr_outstanding_calls); + trace_afs_call(call->debug_id, afs_call_trace_alloc, 1, o, +- __builtin_return_address(0)); ++ _RET_IP_); + return call; + } + +@@ -172,7 +172,7 @@ void afs_put_call(struct afs_call *call) + zero = __refcount_dec_and_test(&call->ref, &r); + o = atomic_read(&net->nr_outstanding_calls); + trace_afs_call(debug_id, afs_call_trace_put, r - 1, o, +- __builtin_return_address(0)); ++ _RET_IP_); + + if (zero) { + ASSERT(!work_pending(&call->async_work)); +@@ -191,7 +191,7 @@ void afs_put_call(struct afs_call *call) + kfree(call->request); + + trace_afs_call(call->debug_id, afs_call_trace_free, 0, o, +- __builtin_return_address(0)); ++ _RET_IP_); + kfree(call); + + o = atomic_dec_return(&net->nr_outstanding_calls); +@@ -209,7 +209,7 @@ static struct afs_call *afs_get_call(struct afs_call *call, + + trace_afs_call(call->debug_id, why, r + 1, + atomic_read(&call->net->nr_outstanding_calls), +- __builtin_return_address(0)); ++ _RET_IP_); + return call; + } + +@@ -679,7 +679,7 @@ static void afs_wake_up_async_call(struct sock *sk, struct rxrpc_call *rxcall, + if (__refcount_inc_not_zero(&call->ref, &r)) { + trace_afs_call(call->debug_id, afs_call_trace_wake, r + 1, + atomic_read(&call->net->nr_outstanding_calls), +- __builtin_return_address(0)); ++ _RET_IP_); + + if (!queue_work(afs_async_calls, &call->async_work)) + afs_put_call(call); +diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h +index cfb8e274c..2744ee793 100644 +--- a/fs/f2fs/f2fs.h ++++ b/fs/f2fs/f2fs.h +@@ -1798,7 +1798,7 @@ struct f2fs_sb_info { + + #ifdef CONFIG_F2FS_FAULT_INJECTION + #define time_to_inject(sbi, type) __time_to_inject(sbi, type, __func__, \ +- __builtin_return_address(0)) ++ _RET_IP_) + static inline bool __time_to_inject(struct f2fs_sb_info *sbi, int type, + const char *func, const char *parent_func) + { +diff --git a/fs/hfs/bfind.c b/fs/hfs/bfind.c +index ef9498a6e..597a12c0e 100644 +--- a/fs/hfs/bfind.c ++++ b/fs/hfs/bfind.c +@@ -24,7 +24,7 @@ int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd) + fd->search_key = ptr; + fd->key = ptr + tree->max_key_len + 2; + hfs_dbg(BNODE_REFS, "find_init: %d (%p)\n", +- tree->cnid, __builtin_return_address(0)); ++ tree->cnid, _RET_IP_); + switch (tree->cnid) { + case HFS_CAT_CNID: + mutex_lock_nested(&tree->tree_lock, CATALOG_BTREE_MUTEX); +@@ -46,7 +46,7 @@ void hfs_find_exit(struct hfs_find_data *fd) + hfs_bnode_put(fd->bnode); + kfree(fd->search_key); + hfs_dbg(BNODE_REFS, "find_exit: %d (%p)\n", +- fd->tree->cnid, __builtin_return_address(0)); ++ fd->tree->cnid, _RET_IP_); + mutex_unlock(&fd->tree->tree_lock); + fd->tree = NULL; + } +diff --git a/fs/hfsplus/bfind.c b/fs/hfsplus/bfind.c +index ca2ba8c9f..6e29c75db 100644 +--- a/fs/hfsplus/bfind.c ++++ b/fs/hfsplus/bfind.c +@@ -24,7 +24,7 @@ int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd) + fd->search_key = ptr; + fd->key = ptr + tree->max_key_len + 2; + hfs_dbg(BNODE_REFS, "find_init: %d (%p)\n", +- tree->cnid, __builtin_return_address(0)); ++ tree->cnid, _RET_IP_); + switch (tree->cnid) { + case HFSPLUS_CAT_CNID: + mutex_lock_nested(&tree->tree_lock, CATALOG_BTREE_MUTEX); +@@ -46,7 +46,7 @@ void hfs_find_exit(struct hfs_find_data *fd) + hfs_bnode_put(fd->bnode); + kfree(fd->search_key); + hfs_dbg(BNODE_REFS, "find_exit: %d (%p)\n", +- fd->tree->cnid, __builtin_return_address(0)); ++ fd->tree->cnid, _RET_IP_); + mutex_unlock(&fd->tree->tree_lock); + fd->tree = NULL; + } +diff --git a/fs/jfs/super.c b/fs/jfs/super.c +index d2f82cb7d..760bb807b 100644 +--- a/fs/jfs/super.c ++++ b/fs/jfs/super.c +@@ -91,7 +91,7 @@ void jfs_error(struct super_block *sb, const char *fmt, ...) + vaf.va = &args; + + pr_err("ERROR: (device %s): %ps: %pV\n", +- sb->s_id, __builtin_return_address(0), &vaf); ++ sb->s_id, _RET_IP_, &vaf); + + va_end(args); + +diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c +index 60b97c92e..9a7ca4b1f 100644 +--- a/fs/ocfs2/cluster/heartbeat.c ++++ b/fs/ocfs2/cluster/heartbeat.c +@@ -2425,7 +2425,7 @@ int o2hb_register_callback(const char *region_uuid, + ret = 0; + out: + mlog(ML_CLUSTER, "returning %d on behalf of %p for funcs %p\n", +- ret, __builtin_return_address(0), hc); ++ ret, _RET_IP_, hc); + return ret; + } + EXPORT_SYMBOL_GPL(o2hb_register_callback); +@@ -2436,7 +2436,7 @@ void o2hb_unregister_callback(const char *region_uuid, + BUG_ON(hc->hc_magic != O2HB_CB_MAGIC); + + mlog(ML_CLUSTER, "on behalf of %p for funcs %p\n", +- __builtin_return_address(0), hc); ++ _RET_IP_, hc); + + /* XXX Can this happen _with_ a region reference? */ + if (list_empty(&hc->hc_item)) +diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c +index d610da8e2..7ac155df9 100644 +--- a/fs/ocfs2/dlm/dlmmaster.c ++++ b/fs/ocfs2/dlm/dlmmaster.c +@@ -604,7 +604,7 @@ void dlm_lockres_set_refmap_bit(struct dlm_ctxt *dlm, + assert_spin_locked(&res->spinlock); + + mlog(0, "res %.*s, set node %u, %ps()\n", res->lockname.len, +- res->lockname.name, bit, __builtin_return_address(0)); ++ res->lockname.name, bit, _RET_IP_); + + set_bit(bit, res->refmap); + } +@@ -615,7 +615,7 @@ void dlm_lockres_clear_refmap_bit(struct dlm_ctxt *dlm, + assert_spin_locked(&res->spinlock); + + mlog(0, "res %.*s, clr node %u, %ps()\n", res->lockname.len, +- res->lockname.name, bit, __builtin_return_address(0)); ++ res->lockname.name, bit, _RET_IP_); + + clear_bit(bit, res->refmap); + } +@@ -627,7 +627,7 @@ static void __dlm_lockres_grab_inflight_ref(struct dlm_ctxt *dlm, + + mlog(0, "%s: res %.*s, inflight++: now %u, %ps()\n", dlm->name, + res->lockname.len, res->lockname.name, res->inflight_locks, +- __builtin_return_address(0)); ++ _RET_IP_); + } + + void dlm_lockres_grab_inflight_ref(struct dlm_ctxt *dlm, +@@ -648,7 +648,7 @@ void dlm_lockres_drop_inflight_ref(struct dlm_ctxt *dlm, + + mlog(0, "%s: res %.*s, inflight--: now %u, %ps()\n", dlm->name, + res->lockname.len, res->lockname.name, res->inflight_locks, +- __builtin_return_address(0)); ++ _RET_IP_); + + wake_up(&res->wq); + } +diff --git a/fs/ubifs/misc.c b/fs/ubifs/misc.c +index cd23de0f2..4372aa344 100644 +--- a/fs/ubifs/misc.c ++++ b/fs/ubifs/misc.c +@@ -32,7 +32,7 @@ void ubifs_err(const struct ubifs_info *c, const char *fmt, ...) + + pr_err("UBIFS error (ubi%d:%d pid %d): %ps: %pV\n", + c->vi.ubi_num, c->vi.vol_id, current->pid, +- __builtin_return_address(0), ++ _RET_IP_, + &vaf); + + va_end(args); +@@ -51,7 +51,7 @@ void ubifs_warn(const struct ubifs_info *c, const char *fmt, ...) + + pr_warn("UBIFS warning (ubi%d:%d pid %d): %ps: %pV\n", + c->vi.ubi_num, c->vi.vol_id, current->pid, +- __builtin_return_address(0), ++ _RET_IP_, + &vaf); + + va_end(args); +diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h +index 549c60942..4c4ea4054 100644 +--- a/fs/xfs/xfs_buf.h ++++ b/fs/xfs/xfs_buf.h +@@ -248,7 +248,7 @@ xfs_buf_read( + DEFINE_SINGLE_BUF_MAP(map, blkno, numblks); + + return xfs_buf_read_map(target, &map, 1, flags, bpp, ops, +- __builtin_return_address(0)); ++ _RET_IP_); + } + + static inline void +diff --git a/fs/xfs/xfs_linux.h b/fs/xfs/xfs_linux.h +index 74dcb0506..3895d8652 100644 +--- a/fs/xfs/xfs_linux.h ++++ b/fs/xfs/xfs_linux.h +@@ -125,14 +125,18 @@ typedef __u32 xfs_nlink_t; + #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ + #define EFSBADCRC EBADMSG /* Bad CRC detected */ + +-#define __return_address __builtin_return_address(0) ++#define __return_address _RET_IP_ + + /* + * Return the address of a label. Use barrier() so that the optimizer + * won't reorder code to refactor the error jumpouts into a single + * return, which throws off the reported address. + */ ++#ifndef CONFIG_NO_IP + #define __this_address ({ __label__ __here; __here: barrier(); &&__here; }) ++#else ++#define __this_address (0) ++#endif + + #define XFS_PROJID_DEFAULT 0 + +diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h +index b23bdd414..e839360cf 100644 +--- a/include/linux/ftrace.h ++++ b/include/linux/ftrace.h +@@ -946,7 +946,7 @@ static inline void __ftrace_enabled_restore(int enabled) + + /* All archs should have this, but we define it for consistency */ + #ifndef ftrace_return_address0 +-# define ftrace_return_address0 __builtin_return_address(0) ++# define ftrace_return_address0 _RET_IP_ + #endif + + /* Archs may use other ways for ADDR1 and beyond */ +diff --git a/include/linux/instruction_pointer.h b/include/linux/instruction_pointer.h +index cda1f706e..fbb04af59 100644 +--- a/include/linux/instruction_pointer.h ++++ b/include/linux/instruction_pointer.h +@@ -2,7 +2,12 @@ + #ifndef _LINUX_INSTRUCTION_POINTER_H + #define _LINUX_INSTRUCTION_POINTER_H + ++#ifndef CONFIG_NO_IP + #define _RET_IP_ (unsigned long)__builtin_return_address(0) + #define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; }) ++#else ++#define _RET_IP_ (0) ++#define _THIS_IP_ (0) ++#endif + + #endif /* _LINUX_INSTRUCTION_POINTER_H */ +diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c +index f715ec5d5..a9caa2c1b 100644 +--- a/kernel/bpf/syscall.c ++++ b/kernel/bpf/syscall.c +@@ -304,7 +304,7 @@ static void *__bpf_map_area_alloc(u64 size, int numa_node, bool mmapable) + + return __vmalloc_node_range(size, align, VMALLOC_START, VMALLOC_END, + gfp | GFP_KERNEL | __GFP_RETRY_MAYFAIL, PAGE_KERNEL, +- flags, numa_node, __builtin_return_address(0)); ++ flags, numa_node, _RET_IP_); + } + + void *bpf_map_area_alloc(u64 size, int numa_node) +diff --git a/kernel/cred.c b/kernel/cred.c +index 811ad654a..71a2939c3 100644 +--- a/kernel/cred.c ++++ b/kernel/cred.c +@@ -142,7 +142,7 @@ void __put_cred(struct cred *cred) + #ifdef CONFIG_DEBUG_CREDENTIALS + BUG_ON(read_cred_subscribers(cred) != 0); + cred->magic = CRED_MAGIC_DEAD; +- cred->put_addr = __builtin_return_address(0); ++ cred->put_addr = _RET_IP_; + #endif + BUG_ON(cred == current->cred); + BUG_ON(cred == current->real_cred); +diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c +index 5595d1d5c..e71b337f1 100644 +--- a/kernel/dma/direct.c ++++ b/kernel/dma/direct.c +@@ -289,7 +289,7 @@ void *dma_direct_alloc(struct device *dev, size_t size, + + /* create a coherent mapping */ + ret = dma_common_contiguous_remap(page, size, prot, +- __builtin_return_address(0)); ++ _RET_IP_); + if (!ret) + goto out_free_pages; + } else { +diff --git a/kernel/dma/pool.c b/kernel/dma/pool.c +index 1acec2e22..a2d742390 100644 +--- a/kernel/dma/pool.c ++++ b/kernel/dma/pool.c +@@ -103,7 +103,7 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size, + #ifdef CONFIG_DMA_DIRECT_REMAP + addr = dma_common_contiguous_remap(page, pool_size, + pgprot_dmacoherent(PAGE_KERNEL), +- __builtin_return_address(0)); ++ _RET_IP_); + if (!addr) + goto free_page; + #else +diff --git a/kernel/fork.c b/kernel/fork.c +index 8103ffd21..038753f0e 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -314,7 +314,7 @@ static int alloc_thread_stack_node(struct task_struct *tsk, int node) + VMALLOC_START, VMALLOC_END, + THREADINFO_GFP & ~__GFP_ACCOUNT, + PAGE_KERNEL, +- 0, node, __builtin_return_address(0)); ++ 0, node, _RET_IP_); + if (!stack) + return -ENOMEM; + +diff --git a/kernel/module/main.c b/kernel/module/main.c +index a04e94c9f..b7379f09e 100644 +--- a/kernel/module/main.c ++++ b/kernel/module/main.c +@@ -1621,7 +1621,7 @@ void * __weak module_alloc(unsigned long size) + { + return __vmalloc_node_range(size, 1, VMALLOC_START, VMALLOC_END, + GFP_KERNEL, PAGE_KERNEL_EXEC, VM_FLUSH_RESET_PERMS, +- NUMA_NO_NODE, __builtin_return_address(0)); ++ NUMA_NO_NODE, _RET_IP_); + } + + bool __weak module_init_section(const char *name) +diff --git a/kernel/panic.c b/kernel/panic.c +index 886d2ebd0..c990ada85 100644 +--- a/kernel/panic.c ++++ b/kernel/panic.c +@@ -694,14 +694,13 @@ void warn_slowpath_fmt(const char *file, int line, unsigned taint, + pr_warn(CUT_HERE); + + if (!fmt) { +- __warn(file, line, __builtin_return_address(0), taint, +- NULL, NULL); ++ __warn(file, line, _RET_IP_, taint, NULL, NULL); + return; + } + + args.fmt = fmt; + va_start(args.args, fmt); +- __warn(file, line, __builtin_return_address(0), taint, NULL, &args); ++ __warn(file, line, _RET_IP_, taint, NULL, &args); + va_end(args.args); + warn_rcu_exit(rcu); + } +@@ -757,7 +756,7 @@ __visible noinstr void __stack_chk_fail(void) + { + instrumentation_begin(); + panic("stack-protector: Kernel stack is corrupted in: %pB", +- __builtin_return_address(0)); ++ _RET_IP_); + instrumentation_end(); + } + EXPORT_SYMBOL(__stack_chk_fail); +diff --git a/kernel/sched/core.c b/kernel/sched/core.c +index 900057600..ae5b19cb5 100644 +--- a/kernel/sched/core.c ++++ b/kernel/sched/core.c +@@ -5924,7 +5924,7 @@ static inline void schedule_debug(struct task_struct *prev, bool preempt) + rcu_sleep_check(); + SCHED_WARN_ON(ct_state() == CONTEXT_USER); + +- profile_hit(SCHED_PROFILING, __builtin_return_address(0)); ++ profile_hit(SCHED_PROFILING, _RET_IP_); + + schedstat_inc(this_rq()->sched_count); + } +diff --git a/kernel/scs.c b/kernel/scs.c +index d7809affe..57292f5da 100644 +--- a/kernel/scs.c ++++ b/kernel/scs.c +@@ -45,7 +45,7 @@ static void *__scs_alloc(int node) + + s = __vmalloc_node_range(SCS_SIZE, 1, VMALLOC_START, VMALLOC_END, + GFP_SCS, PAGE_KERNEL, 0, node, +- __builtin_return_address(0)); ++ _RET_IP_); + + out: + return kasan_reset_tag(s); +diff --git a/lib/smp_processor_id.c b/lib/smp_processor_id.c +index a2bb7738c..cd68af76a 100644 +--- a/lib/smp_processor_id.c ++++ b/lib/smp_processor_id.c +@@ -45,7 +45,7 @@ unsigned int check_preemption_disabled(const char *what1, const char *what2) + printk(KERN_ERR "BUG: using %s%s() in preemptible [%08x] code: %s/%d\n", + what1, what2, preempt_count() - 1, current->comm, current->pid); + +- printk("caller is %pS\n", __builtin_return_address(0)); ++ printk("caller is %pS\n", _RET_IP_); + dump_stack(); + + out_enable: +diff --git a/lib/test_kprobes.c b/lib/test_kprobes.c +index 0648f7154..bb74ad6af 100644 +--- a/lib/test_kprobes.c ++++ b/lib/test_kprobes.c +@@ -76,14 +76,14 @@ static noinline u32 kprobe_target2(u32 value) + static noinline unsigned long kprobe_stacktrace_internal_target(void) + { + if (!target_return_address[0]) +- target_return_address[0] = (unsigned long)__builtin_return_address(0); ++ target_return_address[0] = (unsigned long)_RET_IP_; + return target_return_address[0]; + } + + static noinline unsigned long kprobe_stacktrace_target(void) + { + if (!target_return_address[1]) +- target_return_address[1] = (unsigned long)__builtin_return_address(0); ++ target_return_address[1] = (unsigned long)_RET_IP_; + + if (internal_target) + internal_target(); +@@ -97,7 +97,7 @@ static noinline unsigned long kprobe_stacktrace_driver(void) + stacktrace_target(); + + /* This is for preventing inlining the function */ +- return (unsigned long)__builtin_return_address(0); ++ return (unsigned long)_RET_IP_; + } + + static int kp_pre_handler2(struct kprobe *p, struct pt_regs *regs) +@@ -287,7 +287,7 @@ static struct kretprobe rp3 = { + + static void test_stacktrace_on_kretprobe(struct kunit *test) + { +- unsigned long myretaddr = (unsigned long)__builtin_return_address(0); ++ unsigned long myretaddr = (unsigned long)_RET_IP_; + + current_test = test; + rp3.kp.addr = NULL; +@@ -348,7 +348,7 @@ static struct kretprobe rp4 = { + + static void test_stacktrace_on_nested_kretprobe(struct kunit *test) + { +- unsigned long myretaddr = (unsigned long)__builtin_return_address(0); ++ unsigned long myretaddr = (unsigned long)_RET_IP_; + struct kretprobe *rps[2] = {&rp3, &rp4}; + + current_test = test; +diff --git a/lib/test_vmalloc.c b/lib/test_vmalloc.c +index 3718d9886..0ec275c49 100644 +--- a/lib/test_vmalloc.c ++++ b/lib/test_vmalloc.c +@@ -97,7 +97,7 @@ static int random_size_align_alloc_test(void) + size = ((rnd % 10) + 1) * PAGE_SIZE; + + ptr = __vmalloc_node(size, align, GFP_KERNEL | __GFP_ZERO, 0, +- __builtin_return_address(0)); ++ _RET_IP_); + if (!ptr) + return -1; + +@@ -120,7 +120,7 @@ static int align_shift_alloc_test(void) + align = ((unsigned long) 1) << i; + + ptr = __vmalloc_node(PAGE_SIZE, align, GFP_KERNEL|__GFP_ZERO, 0, +- __builtin_return_address(0)); ++ _RET_IP_); + if (!ptr) + return -1; + +@@ -138,7 +138,7 @@ static int fix_align_alloc_test(void) + for (i = 0; i < test_loop_count; i++) { + ptr = __vmalloc_node(5 * PAGE_SIZE, THREAD_ALIGN << 1, + GFP_KERNEL | __GFP_ZERO, 0, +- __builtin_return_address(0)); ++ _RET_IP_); + if (!ptr) + return -1; + +diff --git a/mm/ioremap.c b/mm/ioremap.c +index 865242628..ebe8b07fd 100644 +--- a/mm/ioremap.c ++++ b/mm/ioremap.c +@@ -32,7 +32,7 @@ void __iomem *ioremap_prot(phys_addr_t phys_addr, size_t size, + return NULL; + + area = get_vm_area_caller(size, VM_IOREMAP, +- __builtin_return_address(0)); ++ _RET_IP_); + if (!area) + return NULL; + vaddr = (unsigned long)area->addr; +diff --git a/mm/kasan/shadow.c b/mm/kasan/shadow.c +index 3e62728ae..4cda3582e 100644 +--- a/mm/kasan/shadow.c ++++ b/mm/kasan/shadow.c +@@ -262,7 +262,7 @@ static int __meminit kasan_mem_notifier(struct notifier_block *nb, + shadow_end, GFP_KERNEL, + PAGE_KERNEL, VM_NO_GUARD, + pfn_to_nid(mem_data->start_pfn), +- __builtin_return_address(0)); ++ _RET_IP_); + if (!ret) + return NOTIFY_BAD; + +@@ -621,7 +621,7 @@ int kasan_alloc_module_shadow(void *addr, size_t size, gfp_t gfp_mask) + shadow_start + shadow_size, + GFP_KERNEL, + PAGE_KERNEL, VM_NO_GUARD, NUMA_NO_NODE, +- __builtin_return_address(0)); ++ _RET_IP_); + + if (ret) { + struct vm_struct *vm = find_vm_area(addr); +diff --git a/mm/kmsan/instrumentation.c b/mm/kmsan/instrumentation.c +index cc3907a9c..b2be7bcab 100644 +--- a/mm/kmsan/instrumentation.c ++++ b/mm/kmsan/instrumentation.c +@@ -269,7 +269,7 @@ void __msan_poison_alloca(void *address, uintptr_t size, char *descr) + ua_flags = user_access_save(); + entries[0] = KMSAN_ALLOCA_MAGIC_ORIGIN; + entries[1] = (u64)descr; +- entries[2] = (u64)__builtin_return_address(0); ++ entries[2] = (u64)_RET_IP_; + /* + * With frame pointers enabled, it is possible to quickly fetch the + * second frame of the caller stack without calling the unwinder. +diff --git a/mm/util.c b/mm/util.c +index 406634f26..908885577 100644 +--- a/mm/util.c ++++ b/mm/util.c +@@ -628,7 +628,7 @@ void *kvmalloc_node(size_t size, gfp_t flags, int node) + */ + return __vmalloc_node_range(size, 1, VMALLOC_START, VMALLOC_END, + flags, PAGE_KERNEL, VM_ALLOW_HUGE_VMAP, +- node, __builtin_return_address(0)); ++ node, _RET_IP_); + } + EXPORT_SYMBOL(kvmalloc_node); + +diff --git a/mm/vmalloc.c b/mm/vmalloc.c +index d78dfb071..37c38af09 100644 +--- a/mm/vmalloc.c ++++ b/mm/vmalloc.c +@@ -2595,7 +2595,7 @@ struct vm_struct *get_vm_area(unsigned long size, unsigned long flags) + return __get_vm_area_node(size, 1, PAGE_SHIFT, flags, + VMALLOC_START, VMALLOC_END, + NUMA_NO_NODE, GFP_KERNEL, +- __builtin_return_address(0)); ++ _RET_IP_); + } + + struct vm_struct *get_vm_area_caller(unsigned long size, unsigned long flags, +@@ -2870,7 +2870,7 @@ void *vmap(struct page **pages, unsigned int count, + return NULL; + + size = (unsigned long)count << PAGE_SHIFT; +- area = get_vm_area_caller(size, flags, __builtin_return_address(0)); ++ area = get_vm_area_caller(size, flags, _RET_IP_); + if (!area) + return NULL; + +@@ -2921,7 +2921,7 @@ void *vmap_pfn(unsigned long *pfns, unsigned int count, pgprot_t prot) + struct vm_struct *area; + + area = get_vm_area_caller(count * PAGE_SIZE, VM_IOREMAP, +- __builtin_return_address(0)); ++ _RET_IP_); + if (!area) + return NULL; + if (apply_to_page_range(&init_mm, (unsigned long)area->addr, +@@ -3347,7 +3347,7 @@ EXPORT_SYMBOL_GPL(__vmalloc_node); + void *__vmalloc(unsigned long size, gfp_t gfp_mask) + { + return __vmalloc_node(size, 1, gfp_mask, NUMA_NO_NODE, +- __builtin_return_address(0)); ++ _RET_IP_); + } + EXPORT_SYMBOL(__vmalloc); + +@@ -3366,7 +3366,7 @@ EXPORT_SYMBOL(__vmalloc); + void *vmalloc(unsigned long size) + { + return __vmalloc_node(size, 1, GFP_KERNEL, NUMA_NO_NODE, +- __builtin_return_address(0)); ++ _RET_IP_); + } + EXPORT_SYMBOL(vmalloc); + +@@ -3386,7 +3386,7 @@ void *vmalloc_huge(unsigned long size, gfp_t gfp_mask) + { + return __vmalloc_node_range(size, 1, VMALLOC_START, VMALLOC_END, + gfp_mask, PAGE_KERNEL, VM_ALLOW_HUGE_VMAP, +- NUMA_NO_NODE, __builtin_return_address(0)); ++ NUMA_NO_NODE, _RET_IP_); + } + EXPORT_SYMBOL_GPL(vmalloc_huge); + +@@ -3406,7 +3406,7 @@ EXPORT_SYMBOL_GPL(vmalloc_huge); + void *vzalloc(unsigned long size) + { + return __vmalloc_node(size, 1, GFP_KERNEL | __GFP_ZERO, NUMA_NO_NODE, +- __builtin_return_address(0)); ++ _RET_IP_); + } + EXPORT_SYMBOL(vzalloc); + +@@ -3424,7 +3424,7 @@ void *vmalloc_user(unsigned long size) + return __vmalloc_node_range(size, SHMLBA, VMALLOC_START, VMALLOC_END, + GFP_KERNEL | __GFP_ZERO, PAGE_KERNEL, + VM_USERMAP, NUMA_NO_NODE, +- __builtin_return_address(0)); ++ _RET_IP_); + } + EXPORT_SYMBOL(vmalloc_user); + +@@ -3444,7 +3444,7 @@ EXPORT_SYMBOL(vmalloc_user); + void *vmalloc_node(unsigned long size, int node) + { + return __vmalloc_node(size, 1, GFP_KERNEL, node, +- __builtin_return_address(0)); ++ _RET_IP_); + } + EXPORT_SYMBOL(vmalloc_node); + +@@ -3462,7 +3462,7 @@ EXPORT_SYMBOL(vmalloc_node); + void *vzalloc_node(unsigned long size, int node) + { + return __vmalloc_node(size, 1, GFP_KERNEL | __GFP_ZERO, node, +- __builtin_return_address(0)); ++ _RET_IP_); + } + EXPORT_SYMBOL(vzalloc_node); + +@@ -3490,7 +3490,7 @@ EXPORT_SYMBOL(vzalloc_node); + void *vmalloc_32(unsigned long size) + { + return __vmalloc_node(size, 1, GFP_VMALLOC32, NUMA_NO_NODE, +- __builtin_return_address(0)); ++ _RET_IP_); + } + EXPORT_SYMBOL(vmalloc_32); + +@@ -3508,7 +3508,7 @@ void *vmalloc_32_user(unsigned long size) + return __vmalloc_node_range(size, SHMLBA, VMALLOC_START, VMALLOC_END, + GFP_VMALLOC32 | __GFP_ZERO, PAGE_KERNEL, + VM_USERMAP, NUMA_NO_NODE, +- __builtin_return_address(0)); ++ _RET_IP_); + } + EXPORT_SYMBOL(vmalloc_32_user); + +diff --git a/net/core/skbuff.c b/net/core/skbuff.c +index 0c0fef73b..ba51e1620 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -1049,9 +1049,9 @@ bool __kfree_skb_reason(struct sk_buff *skb, enum skb_drop_reason reason) + SKB_DROP_REASON_SUBSYS_NUM); + + if (reason == SKB_CONSUMED) +- trace_consume_skb(skb, __builtin_return_address(0)); ++ trace_consume_skb(skb, _RET_IP_); + else +- trace_kfree_skb(skb, __builtin_return_address(0), reason); ++ trace_kfree_skb(skb, _RET_IP_, reason); + return true; + } + +@@ -1247,7 +1247,7 @@ void consume_skb(struct sk_buff *skb) + if (!skb_unref(skb)) + return; + +- trace_consume_skb(skb, __builtin_return_address(0)); ++ trace_consume_skb(skb, _RET_IP_); + __kfree_skb(skb); + } + EXPORT_SYMBOL(consume_skb); +@@ -1262,7 +1262,7 @@ EXPORT_SYMBOL(consume_skb); + */ + void __consume_stateless_skb(struct sk_buff *skb) + { +- trace_consume_skb(skb, __builtin_return_address(0)); ++ trace_consume_skb(skb, _RET_IP_); + skb_release_data(skb, SKB_CONSUMED, false); + kfree_skbmem(skb); + } +@@ -1318,7 +1318,7 @@ void napi_consume_skb(struct sk_buff *skb, int budget) + return; + + /* if reaching here SKB is ready to free */ +- trace_consume_skb(skb, __builtin_return_address(0)); ++ trace_consume_skb(skb, _RET_IP_); + + /* if SKB is a clone, don't handle this case */ + if (skb->fclone != SKB_FCLONE_UNAVAILABLE) { +@@ -2394,7 +2394,7 @@ void *skb_put(struct sk_buff *skb, unsigned int len) + skb->tail += len; + skb->len += len; + if (unlikely(skb->tail > skb->end)) +- skb_over_panic(skb, len, __builtin_return_address(0)); ++ skb_over_panic(skb, len, _RET_IP_); + return tmp; + } + EXPORT_SYMBOL(skb_put); +@@ -2413,7 +2413,7 @@ void *skb_push(struct sk_buff *skb, unsigned int len) + skb->data -= len; + skb->len += len; + if (unlikely(skb->data < skb->head)) +- skb_under_panic(skb, len, __builtin_return_address(0)); ++ skb_under_panic(skb, len, _RET_IP_); + return skb->data; + } + EXPORT_SYMBOL(skb_push); +diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c +index f4c55e65a..a6d4a3264 100644 +--- a/net/netfilter/ipvs/ip_vs_conn.c ++++ b/net/netfilter/ipvs/ip_vs_conn.c +@@ -181,7 +181,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp) + ret = 1; + } else { + pr_err("%s(): request for already hashed, called from %pS\n", +- __func__, __builtin_return_address(0)); ++ __func__, _RET_IP_); + ret = 0; + } + +diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c +index 4bb0d90ec..3c0d0b0cb 100644 +--- a/net/netfilter/ipvs/ip_vs_ctl.c ++++ b/net/netfilter/ipvs/ip_vs_ctl.c +@@ -349,7 +349,7 @@ static int ip_vs_svc_hash(struct ip_vs_service *svc) + + if (svc->flags & IP_VS_SVC_F_HASHED) { + pr_err("%s(): request for already hashed, called from %pS\n", +- __func__, __builtin_return_address(0)); ++ __func__, _RET_IP_); + return 0; + } + +@@ -383,7 +383,7 @@ static int ip_vs_svc_unhash(struct ip_vs_service *svc) + { + if (!(svc->flags & IP_VS_SVC_F_HASHED)) { + pr_err("%s(): request for unhash flagged, called from %pS\n", +- __func__, __builtin_return_address(0)); ++ __func__, _RET_IP_); + return 0; + } + +diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c +index 6b7807540..8b07ca8fe 100644 +--- a/net/smc/smc_core.c ++++ b/net/smc/smc_core.c +@@ -1718,7 +1718,7 @@ static void smcr_link_down(struct smc_link *lnk) + void smcr_link_down_cond(struct smc_link *lnk) + { + if (smc_link_downing(&lnk->state)) { +- trace_smcr_link_down(lnk, __builtin_return_address(0)); ++ trace_smcr_link_down(lnk, _RET_IP_); + smcr_link_down(lnk); + } + } +@@ -1727,7 +1727,7 @@ void smcr_link_down_cond(struct smc_link *lnk) + void smcr_link_down_cond_sched(struct smc_link *lnk) + { + if (smc_link_downing(&lnk->state)) { +- trace_smcr_link_down(lnk, __builtin_return_address(0)); ++ trace_smcr_link_down(lnk, _RET_IP_); + schedule_work(&lnk->link_down_wrk); + } + } +diff --git a/net/tipc/crypto.c b/net/tipc/crypto.c +index 302fd749c..fc5ff3030 100644 +--- a/net/tipc/crypto.c ++++ b/net/tipc/crypto.c +@@ -1101,7 +1101,7 @@ static inline void tipc_crypto_key_set_state(struct tipc_crypto *c, + + pr_debug("%s: key changing %s ::%pS\n", c->name, + tipc_key_change_dump(old, c->key, buf), +- __builtin_return_address(0)); ++ _RET_IP_); + } + + /** +diff --git a/samples/bpf/tracex2.bpf.c b/samples/bpf/tracex2.bpf.c +index 0a5c75b36..df3639ebc 100644 +--- a/samples/bpf/tracex2.bpf.c ++++ b/samples/bpf/tracex2.bpf.c +@@ -28,7 +28,7 @@ int bpf_prog2(struct pt_regs *ctx) + long *value; + + /* read ip of kfree_skb_reason caller. +- * non-portable version of __builtin_return_address(0) ++ * non-portable version of _RET_IP_ + */ + BPF_KPROBE_READ_RET_IP(loc, ctx); + +diff --git a/sound/core/device.c b/sound/core/device.c +index b57d80a17..3c6e0b6c3 100644 +--- a/sound/core/device.c ++++ b/sound/core/device.c +@@ -114,7 +114,7 @@ void snd_device_disconnect(struct snd_card *card, void *device_data) + __snd_device_disconnect(dev); + else + dev_dbg(card->dev, "device disconnect %p (from %pS), not found\n", +- device_data, __builtin_return_address(0)); ++ device_data, _RET_IP_); + } + EXPORT_SYMBOL_GPL(snd_device_disconnect); + +@@ -138,7 +138,7 @@ void snd_device_free(struct snd_card *card, void *device_data) + __snd_device_free(dev); + else + dev_dbg(card->dev, "device free %p (from %pS), not found\n", +- device_data, __builtin_return_address(0)); ++ device_data, _RET_IP_); + } + EXPORT_SYMBOL(snd_device_free); + +-- +2.25.1 + diff --git a/patches/kernel/0002-Allow-architecture-specific-panic-handling.patch b/patches/kernel/0002-Allow-architecture-specific-panic-handling.patch new file mode 100644 index 0000000..cc2dfb8 --- /dev/null +++ b/patches/kernel/0002-Allow-architecture-specific-panic-handling.patch @@ -0,0 +1,68 @@ +From 99fe993477b73fab9aeffb8ed96c79319f2abfff Mon Sep 17 00:00:00 2001 +From: Joel Severin +Date: Sun, 14 Sep 2025 17:03:25 +0200 +Subject: [PATCH] Allow architecture-specific panic handling + +Debugging panics can be made much easier in hosted architectures (like +Wasm) if they can intercept panic() calls right when they happen. +--- + include/asm-generic/Kbuild | 1 + + include/asm-generic/panic.h | 12 ++++++++++++ + kernel/panic.c | 3 +++ + 3 files changed, 16 insertions(+) + create mode 100644 include/asm-generic/panic.h + +diff --git a/include/asm-generic/Kbuild b/include/asm-generic/Kbuild +index 941be574b..f1feb32a4 100644 +--- a/include/asm-generic/Kbuild ++++ b/include/asm-generic/Kbuild +@@ -42,6 +42,7 @@ mandatory-y += mmu_context.h + mandatory-y += module.h + mandatory-y += module.lds.h + mandatory-y += msi.h ++mandatory-y += panic.h + mandatory-y += pci.h + mandatory-y += percpu.h + mandatory-y += pgalloc.h +diff --git a/include/asm-generic/panic.h b/include/asm-generic/panic.h +new file mode 100644 +index 000000000..5f1a0ac69 +--- /dev/null ++++ b/include/asm-generic/panic.h +@@ -0,0 +1,12 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++#ifndef _ASM_GENERIC_PANIC_H ++#define _ASM_GENERIC_PANIC_H ++ ++#ifndef CONFIG_ARCH_HAVE_PANIC_NOTIFY ++static inline void arch_panic_notify(const char *msg) ++{ ++} ++#endif ++ ++#endif /* _ASM_GENERIC_PANIC_H */ +diff --git a/kernel/panic.c b/kernel/panic.c +index c990ada85..c2343cfc9 100644 +--- a/kernel/panic.c ++++ b/kernel/panic.c +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + #include + + #define PANIC_TIMER_STEP 100 +@@ -397,6 +398,8 @@ void panic(const char *fmt, ...) + + panic_print_sys_info(true); + ++ arch_panic_notify(buf); ++ + if (!panic_blink) + panic_blink = no_blink; + +-- +2.25.1 + diff --git a/patches/kernel/0003-Add-missing-processor.h-include-for-asm-generic-barr.patch b/patches/kernel/0003-Add-missing-processor.h-include-for-asm-generic-barr.patch new file mode 100644 index 0000000..7596bf8 --- /dev/null +++ b/patches/kernel/0003-Add-missing-processor.h-include-for-asm-generic-barr.patch @@ -0,0 +1,26 @@ +From 8d9649b899cd680ad4f5f57069eeceed72acc162 Mon Sep 17 00:00:00 2001 +From: Joel Severin +Date: Sun, 14 Sep 2025 17:29:37 +0200 +Subject: [PATCH] Add missing processor.h include for asm-generic/barrier.h + +The default implementation of smp_cond_load_relaxed() uses cpu_relax(), +which is defined in the arch-specific processor.h header. +--- + include/asm-generic/barrier.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/asm-generic/barrier.h b/include/asm-generic/barrier.h +index 961f4d88f..2fd9233f0 100644 +--- a/include/asm-generic/barrier.h ++++ b/include/asm-generic/barrier.h +@@ -15,6 +15,7 @@ + + #include + #include ++#include + #include + + #ifndef nop +-- +2.25.1 + diff --git a/patches/kernel/0004-Align-dot-instead-of-section-in-vmlinux.lds.h.patch b/patches/kernel/0004-Align-dot-instead-of-section-in-vmlinux.lds.h.patch new file mode 100644 index 0000000..b57ea18 --- /dev/null +++ b/patches/kernel/0004-Align-dot-instead-of-section-in-vmlinux.lds.h.patch @@ -0,0 +1,29 @@ +From 29413574b48c2f12120b65573d28324d74129ee5 Mon Sep 17 00:00:00 2001 +From: Joel Severin +Date: Sun, 14 Sep 2025 18:21:14 +0200 +Subject: [PATCH] Align dot instead of section in vmlinux.lds.h + +The rudimentary linker script support in wasm-ld does not permit a section +to be aligned directly. This workaround overcomes that limitation by +immediately aligning the dot at the start of each section instead. +--- + include/asm-generic/vmlinux.lds.h | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h +index da9e5629e..b331a3947 100644 +--- a/include/asm-generic/vmlinux.lds.h ++++ b/include/asm-generic/vmlinux.lds.h +@@ -865,7 +865,8 @@ + /* Built-in firmware blobs */ + #ifdef CONFIG_FW_LOADER + #define FW_LOADER_BUILT_IN_DATA \ +- .builtin_fw : AT(ADDR(.builtin_fw) - LOAD_OFFSET) ALIGN(8) { \ ++ . = ALIGN(8); \ ++ .builtin_fw : AT(ADDR(.builtin_fw) - LOAD_OFFSET) { \ + BOUNDED_SECTION_PRE_LABEL(.builtin_fw, _builtin_fw, __start, __end) \ + } + #else +-- +2.25.1 + diff --git a/patches/kernel/0005-Add-Wasm-architecture.patch b/patches/kernel/0005-Add-Wasm-architecture.patch new file mode 100644 index 0000000..5f8bab1 --- /dev/null +++ b/patches/kernel/0005-Add-Wasm-architecture.patch @@ -0,0 +1,3758 @@ +From efdef05f887b3ea571b329f0b2a52d062635fe13 Mon Sep 17 00:00:00 2001 +From: Joel Severin +Date: Sun, 14 Sep 2025 17:09:39 +0200 +Subject: [PATCH] Add Wasm architecture + +This is the bare minimum arch-specific code needed to get Linux to boot on +Wasm (WebAssembly). +--- + Makefile | 9 +- + arch/wasm/Kbuild | 1 + + arch/wasm/Kconfig | 78 ++++++ + arch/wasm/Kconfig.debug | 10 + + arch/wasm/Makefile | 24 ++ + arch/wasm/include/asm/Kbuild | 58 ++++ + arch/wasm/include/asm/barrier.h | 16 ++ + arch/wasm/include/asm/cache.h | 12 + + arch/wasm/include/asm/cmpxchg.h | 111 ++++++++ + arch/wasm/include/asm/cpuflags.h | 22 ++ + arch/wasm/include/asm/current.h | 36 +++ + arch/wasm/include/asm/delay.h | 25 ++ + arch/wasm/include/asm/elf.h | 66 +++++ + arch/wasm/include/asm/entry-common.h | 16 ++ + arch/wasm/include/asm/futex.h | 68 +++++ + arch/wasm/include/asm/irq.h | 11 + + arch/wasm/include/asm/irq_work.h | 13 + + arch/wasm/include/asm/irqflags.h | 14 + + arch/wasm/include/asm/linkage.h | 22 ++ + arch/wasm/include/asm/mmu_context.h | 8 + + arch/wasm/include/asm/panic.h | 15 ++ + arch/wasm/include/asm/pgtable.h | 45 ++++ + arch/wasm/include/asm/processor.h | 50 ++++ + arch/wasm/include/asm/ptrace.h | 35 +++ + arch/wasm/include/asm/smp.h | 28 ++ + arch/wasm/include/asm/stacktrace.h | 20 ++ + arch/wasm/include/asm/syscall.h | 62 +++++ + arch/wasm/include/asm/thread_info.h | 105 ++++++++ + arch/wasm/include/asm/time.h | 9 + + arch/wasm/include/asm/vmalloc.h | 6 + + arch/wasm/include/asm/wasm.h | 29 ++ + arch/wasm/include/uapi/asm/Kbuild | 2 + + arch/wasm/include/uapi/asm/byteorder.h | 8 + + arch/wasm/include/uapi/asm/ptrace.h | 39 +++ + arch/wasm/include/uapi/asm/sigcontext.h | 13 + + arch/wasm/include/uapi/asm/unistd.h | 6 + + arch/wasm/kernel/Makefile | 21 ++ + arch/wasm/kernel/asm-offsets.c | 36 +++ + arch/wasm/kernel/cpu.c | 46 ++++ + arch/wasm/kernel/cpuflags.c | 5 + + arch/wasm/kernel/entry.S | 299 ++++++++++++++++++++ + arch/wasm/kernel/head.S | 110 ++++++++ + arch/wasm/kernel/irq.c | 55 ++++ + arch/wasm/kernel/irqflags.c | 21 ++ + arch/wasm/kernel/process.c | 282 +++++++++++++++++++ + arch/wasm/kernel/ptrace.c | 13 + + arch/wasm/kernel/reboot.c | 31 +++ + arch/wasm/kernel/setup.c | 84 ++++++ + arch/wasm/kernel/signal.c | 189 +++++++++++++ + arch/wasm/kernel/smp.c | 344 ++++++++++++++++++++++++ + arch/wasm/kernel/stack.c | 26 ++ + arch/wasm/kernel/sys_wasm.c | 19 ++ + arch/wasm/kernel/syscall_table.c | 37 +++ + arch/wasm/kernel/time.c | 88 ++++++ + arch/wasm/kernel/traps.c | 207 ++++++++++++++ + arch/wasm/kernel/vmlinux.lds.S | 65 +++++ + arch/wasm/lib/Makefile | 3 + + arch/wasm/lib/delay.c | 19 ++ + arch/wasm/mm/Makefile | 3 + + arch/wasm/mm/init.c | 21 ++ + include/asm-generic/vmlinux.lds.h | 4 + + include/uapi/linux/audit.h | 1 + + include/uapi/linux/elf-em.h | 1 + + scripts/Makefile.clang | 1 + + scripts/Makefile.vmlinux_o | 10 +- + scripts/link-vmlinux.sh | 23 +- + 66 files changed, 3151 insertions(+), 5 deletions(-) + create mode 100644 arch/wasm/Kbuild + create mode 100644 arch/wasm/Kconfig + create mode 100644 arch/wasm/Kconfig.debug + create mode 100644 arch/wasm/Makefile + create mode 100644 arch/wasm/include/asm/Kbuild + create mode 100644 arch/wasm/include/asm/barrier.h + create mode 100644 arch/wasm/include/asm/cache.h + create mode 100644 arch/wasm/include/asm/cmpxchg.h + create mode 100644 arch/wasm/include/asm/cpuflags.h + create mode 100644 arch/wasm/include/asm/current.h + create mode 100644 arch/wasm/include/asm/delay.h + create mode 100644 arch/wasm/include/asm/elf.h + create mode 100644 arch/wasm/include/asm/entry-common.h + create mode 100644 arch/wasm/include/asm/futex.h + create mode 100644 arch/wasm/include/asm/irq.h + create mode 100644 arch/wasm/include/asm/irq_work.h + create mode 100644 arch/wasm/include/asm/irqflags.h + create mode 100644 arch/wasm/include/asm/linkage.h + create mode 100644 arch/wasm/include/asm/mmu_context.h + create mode 100644 arch/wasm/include/asm/panic.h + create mode 100644 arch/wasm/include/asm/pgtable.h + create mode 100644 arch/wasm/include/asm/processor.h + create mode 100644 arch/wasm/include/asm/ptrace.h + create mode 100644 arch/wasm/include/asm/smp.h + create mode 100644 arch/wasm/include/asm/stacktrace.h + create mode 100644 arch/wasm/include/asm/syscall.h + create mode 100644 arch/wasm/include/asm/thread_info.h + create mode 100644 arch/wasm/include/asm/time.h + create mode 100644 arch/wasm/include/asm/vmalloc.h + create mode 100644 arch/wasm/include/asm/wasm.h + create mode 100644 arch/wasm/include/uapi/asm/Kbuild + create mode 100644 arch/wasm/include/uapi/asm/byteorder.h + create mode 100644 arch/wasm/include/uapi/asm/ptrace.h + create mode 100644 arch/wasm/include/uapi/asm/sigcontext.h + create mode 100644 arch/wasm/include/uapi/asm/unistd.h + create mode 100644 arch/wasm/kernel/Makefile + create mode 100644 arch/wasm/kernel/asm-offsets.c + create mode 100644 arch/wasm/kernel/cpu.c + create mode 100644 arch/wasm/kernel/cpuflags.c + create mode 100644 arch/wasm/kernel/entry.S + create mode 100644 arch/wasm/kernel/head.S + create mode 100644 arch/wasm/kernel/irq.c + create mode 100644 arch/wasm/kernel/irqflags.c + create mode 100644 arch/wasm/kernel/process.c + create mode 100644 arch/wasm/kernel/ptrace.c + create mode 100644 arch/wasm/kernel/reboot.c + create mode 100644 arch/wasm/kernel/setup.c + create mode 100644 arch/wasm/kernel/signal.c + create mode 100644 arch/wasm/kernel/smp.c + create mode 100644 arch/wasm/kernel/stack.c + create mode 100644 arch/wasm/kernel/sys_wasm.c + create mode 100644 arch/wasm/kernel/syscall_table.c + create mode 100644 arch/wasm/kernel/time.c + create mode 100644 arch/wasm/kernel/traps.c + create mode 100644 arch/wasm/kernel/vmlinux.lds.S + create mode 100644 arch/wasm/lib/Makefile + create mode 100644 arch/wasm/lib/delay.c + create mode 100644 arch/wasm/mm/Makefile + create mode 100644 arch/wasm/mm/init.c + +diff --git a/Makefile b/Makefile +index 34ea74d74..c69000c85 100644 +--- a/Makefile ++++ b/Makefile +@@ -479,7 +479,11 @@ KBUILD_HOSTLDLIBS := $(HOST_LFS_LIBS) $(HOSTLDLIBS) + CPP = $(CC) -E + ifneq ($(LLVM),) + CC = $(LLVM_PREFIX)clang$(LLVM_SUFFIX) +-LD = $(LLVM_PREFIX)ld.lld$(LLVM_SUFFIX) ++ifneq ($(ARCH),wasm) ++ LD = $(LLVM_PREFIX)ld.lld$(LLVM_SUFFIX) ++else ++ LD = $(LLVM_PREFIX)wasm-ld$(LLVM_SUFFIX) ++endif + AR = $(LLVM_PREFIX)llvm-ar$(LLVM_SUFFIX) + NM = $(LLVM_PREFIX)llvm-nm$(LLVM_SUFFIX) + OBJCOPY = $(LLVM_PREFIX)llvm-objcopy$(LLVM_SUFFIX) +@@ -1100,8 +1104,11 @@ KBUILD_AFLAGS += $(KAFLAGS) + KBUILD_CFLAGS += $(KCFLAGS) + KBUILD_RUSTFLAGS += $(KRUSTFLAGS) + ++# Not supported in Wasm binaries yet, PR seems to be in the works (LLVM D107662). ++ifneq ($(ARCH),wasm) + KBUILD_LDFLAGS_MODULE += --build-id=sha1 + LDFLAGS_vmlinux += --build-id=sha1 ++endif + + KBUILD_LDFLAGS += -z noexecstack + ifeq ($(CONFIG_LD_IS_BFD),y) +diff --git a/arch/wasm/Kbuild b/arch/wasm/Kbuild +new file mode 100644 +index 000000000..a4e40e534 +--- /dev/null ++++ b/arch/wasm/Kbuild +@@ -0,0 +1 @@ ++# SPDX-License-Identifier: GPL-2.0-only +diff --git a/arch/wasm/Kconfig b/arch/wasm/Kconfig +new file mode 100644 +index 000000000..f6e566f50 +--- /dev/null ++++ b/arch/wasm/Kconfig +@@ -0,0 +1,78 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++ ++menu "Wasm-specific options" ++ ++# Wasm must run on many CPUs, as a task cannot be preempted, unless terminated. ++# Each CPU becomes a thread in the host OS, and is handled by its scheduler. ++# There is no MMU support in the current version of WebAssembly. ++ ++config WASM ++ bool ++ default y ++ # The execution model of one task per cpu mandates the below options. ++ # One CPU is kept clear of tasks to act as a tick broadcast device. ++ select SMP ++ # PREEMPTION and PREEMPT_COUNT is not set, disallowing kernel preemption ++ select ARCH_NO_PREEMPT ++ select GENERIC_CLOCKEVENTS_BROADCAST ++ select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST ++ # Needed by NO_HZ_FULL: ++ select HAVE_VIRT_CPU_ACCOUNTING_GEN ++ # TODO: Check that we comply with the user tracking requirements! ++ select HAVE_CONTEXT_TRACKING_USER ++ ++ select NO_IP ++ select THREAD_INFO_IN_TASK ++ select ARCH_TASK_STRUCT_ON_STACK ++ select ARCH_TASK_STRUCT_ALLOCATOR ++ select ARCH_THREAD_STACK_ALLOCATOR ++ select GENERIC_SMP_IDLE_THREAD ++ select UACCESS_MEMCPY ++ select ARCH_USE_QUEUED_RWLOCKS ++ select GENERIC_CPU_DEVICES ++ select GENERIC_CSUM ++ select GENERIC_ENTRY ++ select GENERIC_HWEIGHT ++ select GENERIC_IRQ_SHOW ++ select HAVE_SYSCALL_TRACEPOINTS ++ select ARCH_HAVE_PANIC_NOTIFY ++ select ARCH_USE_BUILTIN_BSWAP ++ select ARCH_SUPPORTS_LTO_CLANG ++ select ARCH_SUPPORTS_LTO_CLANG_THIN ++ ++ # TODO: Very inefficient, replace with native stuff. Our atomic impl. ++ # of xchg and cmpxchg already supports 64-bit integers, we could use it. ++ select GENERIC_ATOMIC64 ++ ++config SMP ++ bool "Symmetric Multi-Processing" ++ help ++ This enables support for systems with more than one CPU. In the ++ context of Wasm, every task needs one CPU, since there is no ++ preemption and no interrupts. If you say N here, you will only ever ++ be able to run one task. Only do this if you really know what ++ you're doing - there is a big risk you will lock up your system. ++ ++ If you don't know what to do here, say Y. ++ ++config HZ ++ int ++ default 100 ++ ++config NR_CPUS ++ int ++ range 1 8192 ++ default 64 ++ ++config GENERIC_CSUM ++ def_bool y ++ ++config GENERIC_HWEIGHT ++ def_bool y ++ ++config ARCH_HAVE_PANIC_NOTIFY ++ bool ++ ++endmenu ++ ++source "drivers/Kconfig" +diff --git a/arch/wasm/Kconfig.debug b/arch/wasm/Kconfig.debug +new file mode 100644 +index 000000000..8fc81eafa +--- /dev/null ++++ b/arch/wasm/Kconfig.debug +@@ -0,0 +1,10 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++ ++config EARLY_PRINTK ++ bool "Early printk" ++ default y ++ help ++ Write kernel log output directly to console.log. ++ ++ This is useful for kernel debugging when your machine crashes very ++ early before the console code is initialized. +diff --git a/arch/wasm/Makefile b/arch/wasm/Makefile +new file mode 100644 +index 000000000..b86103e0b +--- /dev/null ++++ b/arch/wasm/Makefile +@@ -0,0 +1,24 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++ ++KBUILD_DEFCONFIG := wasm_defconfig ++ ++KCFLAGS += -EL -m32 ++KCFLAGS += -nostdlib -fno-builtin ++ ++# These flags are needed so that wasm-ld can be run with --shared-memory. ++KCFLAGS += -Xclang -target-feature -Xclang +atomics ++KCFLAGS += -Xclang -target-feature -Xclang +bulk-memory ++ ++core-y += arch/wasm/kernel/ ++core-y += arch/wasm/mm/ ++libs-y += arch/wasm/lib/ ++ ++PHONY += bzImage ++ ++all: bzImage ++ ++bzImage: vmlinux ++ ++define archhelp ++ echo '* bzImage - Compressed kernel image (arch/wasm/boot/bzImage)' ++endef +diff --git a/arch/wasm/include/asm/Kbuild b/arch/wasm/include/asm/Kbuild +new file mode 100644 +index 000000000..876a533cd +--- /dev/null ++++ b/arch/wasm/include/asm/Kbuild +@@ -0,0 +1,58 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++ ++# TODO: Clean up headers that are not used by this arch. ++generic-y += access_ok.h ++generic-y += agp.h ++generic-y += asm-offsets.h ++generic-y += asm-prototypes.h ++generic-y += atomic64.h ++generic-y += audit_change_attr.h ++generic-y += audit_dir_write.h ++generic-y += audit_read.h ++generic-y += audit_signal.h ++generic-y += audit_write.h ++generic-y += bitsperlong.h ++generic-y += cmpxchg-local.h ++generic-y += early_ioremap.h ++generic-y += error-injection.h ++generic-y += export.h ++generic-y += extable.h ++generic-y += fixmap.h ++generic-y += flat.h ++generic-y += getorder.h ++generic-y += hugetlb.h ++generic-y += hyperv-tlfs.h ++generic-y += ide_iops.h ++generic-y += int-ll64.h ++generic-y += ioctl.h ++generic-y += iomap.h ++generic-y += kvm_para.h ++generic-y += kvm_types.h ++generic-y += logic_io.h ++generic-y += mcs_spinlock.h ++generic-y += memory_model.h ++generic-y += mm_hooks.h ++generic-y += mmiowb_types.h ++generic-y += mshyperv.h ++generic-y += numa.h ++generic-y += page.h ++generic-y += param.h ++generic-y += parport.h ++generic-y += pci_iomap.h ++generic-y += qrwlock.h ++generic-y += qrwlock_types.h ++generic-y += qspinlock.h ++generic-y += qspinlock_types.h ++generic-y += resource.h ++generic-y += seccomp.h ++generic-y += set_memory.h ++generic-y += signal.h ++generic-y += spinlock.h ++generic-y += spinlock_types.h ++generic-y += statfs.h ++generic-y += string.h ++generic-y += syscalls.h ++generic-y += tlb.h ++generic-y += user.h ++generic-y += vmlinux.lds.h ++generic-y += vtime.h +diff --git a/arch/wasm/include/asm/barrier.h b/arch/wasm/include/asm/barrier.h +new file mode 100644 +index 000000000..86d3fc9b2 +--- /dev/null ++++ b/arch/wasm/include/asm/barrier.h +@@ -0,0 +1,16 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_BARRIER_H ++#define _ASM_WASM_BARRIER_H ++ ++/* ++ * Inspired by: ++ * https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0124r7.html ++ */ ++#define mb() __atomic_thread_fence(__ATOMIC_SEQ_CST) ++#define rmb() __atomic_thread_fence(__ATOMIC_ACQ_REL) ++#define wmb() __atomic_thread_fence(__ATOMIC_ACQ_REL) ++ ++#include ++ ++#endif /* _ASM_WASM_BARRIER_H */ +diff --git a/arch/wasm/include/asm/cache.h b/arch/wasm/include/asm/cache.h +new file mode 100644 +index 000000000..1abcb0191 +--- /dev/null ++++ b/arch/wasm/include/asm/cache.h +@@ -0,0 +1,12 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_CACHE_H ++#define _ASM_WASM_CACHE_H ++ ++/* ++ * Most architectures executing Wasm code has a cacheline size of 64 bytes. ++ */ ++#define L1_CACHE_SHIFT 6 ++#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT) ++ ++#endif /* _ASM_WASM_CACHE_H */ +diff --git a/arch/wasm/include/asm/cmpxchg.h b/arch/wasm/include/asm/cmpxchg.h +new file mode 100644 +index 000000000..a870f2682 +--- /dev/null ++++ b/arch/wasm/include/asm/cmpxchg.h +@@ -0,0 +1,111 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_CMPXCHG_H ++#define _ASM_WASM_CMPXCHG_H ++ ++#include ++#include ++ ++/* ++ * Inspired by: ++ * https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0124r7.html ++ * https://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs.git/tree/include/asm-generic/iso-cmpxchg.h?h=iso-atomic ++ * ++ * TODO: McKenney et. al. above mention that atomic operations that return a ++ * value should be marked with __ATOMIC_RELAXED and wrapped with ++ * smp_mb__before_atomic()/smp_mb__after_atomic() calls. Howells above, ++ * however, just applies __ATOMIC_SEQ_CST. What is the best approach? ++ */ ++ ++/* ++ * This function doesn't exist, so you'll get a linker error if ++ * something tries to do an invalidly-sized xchg(). ++ */ ++extern unsigned long long __generic_xchg_called_with_bad_pointer(void); ++ ++static __always_inline unsigned long long __generic_xchg( ++ unsigned long long val, volatile void *ptr, int size) ++{ ++ switch (size) { ++ case 1: ++ return __atomic_exchange_n( ++ (volatile u8 *)ptr, (u8)val, __ATOMIC_SEQ_CST); ++ ++ case 2: ++ return __atomic_exchange_n( ++ (volatile u16 *)ptr, (u16)val, __ATOMIC_SEQ_CST); ++ ++ case 4: ++ return __atomic_exchange_n( ++ (volatile u32 *)ptr, (u32)val, __ATOMIC_SEQ_CST); ++ ++ case 8: ++ return __atomic_exchange_n( ++ (volatile u64 *)ptr, (u64)val, __ATOMIC_SEQ_CST); ++ ++ default: ++ return __generic_xchg_called_with_bad_pointer(); ++ } ++} ++ ++#define arch_xchg(ptr, x) ({ \ ++ ((__typeof__(*(ptr))) __generic_xchg((unsigned long long)(x), (ptr), \ ++ sizeof(*(ptr)))); \ ++}) ++ ++static __always_inline unsigned long long __generic_cmpxchg(volatile void *ptr, ++ unsigned long long oldVal, unsigned long long newVal, int size) ++{ ++ /* ++ * Unlike this functions' signature, __atomic_compare_exchange_n will ++ * modify oldVal with the actual value if the compare fails. ++ */ ++ u8 expected8; ++ u16 expected16; ++ u32 expected32; ++ u64 expected64; ++ ++ switch (size) { ++ case 1: ++ expected8 = (u8)oldVal; ++ __atomic_compare_exchange_n( ++ (volatile u8 *)ptr, &expected8, (u8)newVal, ++ false, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED); ++ return expected8; ++ ++ case 2: ++ expected16 = (u16)oldVal; ++ __atomic_compare_exchange_n( ++ (volatile u16 *)ptr, &expected16, (u16)newVal, ++ false, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED); ++ return expected16; ++ ++ case 4: ++ expected32 = (u32)oldVal; ++ __atomic_compare_exchange_n( ++ (volatile u32 *)ptr, &expected32, (u32)newVal, ++ false, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED); ++ return expected32; ++ ++ case 8: ++ expected64 = (u64)oldVal; ++ __atomic_compare_exchange_n( ++ (volatile u64 *)ptr, &expected64, (u64)newVal, ++ false, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED); ++ return expected64; ++ ++ default: ++ return __generic_xchg_called_with_bad_pointer(); ++ } ++} ++ ++#define arch_cmpxchg(ptr, o, n) ({ \ ++ ((__typeof__(*(ptr)))__generic_cmpxchg((ptr), (unsigned long long)(o), \ ++ (unsigned long long)(n), sizeof(*(ptr)))); \ ++}) ++ ++#define arch_cmpxchg64 arch_cmpxchg ++#define arch_cmpxchg_local arch_cmpxchg ++#define arch_cmpxchg64_local arch_cmpxchg ++ ++#endif /* _ASM_WASM_CMPXCHG_H */ +diff --git a/arch/wasm/include/asm/cpuflags.h b/arch/wasm/include/asm/cpuflags.h +new file mode 100644 +index 000000000..365502f4f +--- /dev/null ++++ b/arch/wasm/include/asm/cpuflags.h +@@ -0,0 +1,22 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_CPUFLAGS_H ++#define _ASM_WASM_CPUFLAGS_H ++ ++#include ++ ++/* ++ * CPU flags handled by Wasm. These are used for accounting in many places. ++ * Interrupt: 1 if local interrupts are enabled. ++ * User mode: 1 if we're not in privileged mode. ++ * ++ * The reset value is 0: we boot into privileged mode with interrupts disabled. ++ */ ++#define CPUFLAGS_INTERRUPT 0 ++#define CPUFLAGS_USER_MODE 1 ++ ++#define CPUFLAGS_RESET_VALUE 0U ++ ++DECLARE_PER_CPU(unsigned long, wasm_cpuflags); ++ ++#endif /* _ASM_WASM_CPUFLAGS_H */ +diff --git a/arch/wasm/include/asm/current.h b/arch/wasm/include/asm/current.h +new file mode 100644 +index 000000000..5f104a966 +--- /dev/null ++++ b/arch/wasm/include/asm/current.h +@@ -0,0 +1,36 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_CURRENT_H ++#define _ASM_WASM_CURRENT_H ++ ++/* ++ * Questionable but necessary to keep get_current() inline, due to the cyclic ++ * dependency between task_struct and thread_info. ++ */ ++#ifndef ASM_OFFSETS_C ++#include ++#endif ++ ++#ifndef __ASSEMBLY__ ++ ++#include ++#include ++ ++struct task_struct; ++ ++static inline struct task_struct *get_current(void) ++{ ++#ifndef ASM_OFFSETS_C ++ char dummy; /* Something stored in the current kernel stack. */ ++ unsigned long thread_page = (unsigned long)&dummy & THREAD_MASK; ++ return (struct task_struct *)(thread_page + THREAD_TASK_STRUCT_OFFSET); ++#else ++ return NULL; ++#endif ++} ++ ++#define current (get_current()) ++ ++#endif /* !__ASSEMBLY__ */ ++ ++#endif /* _ASM_WASM_CURRENT_H */ +diff --git a/arch/wasm/include/asm/delay.h b/arch/wasm/include/asm/delay.h +new file mode 100644 +index 000000000..0e3bd9346 +--- /dev/null ++++ b/arch/wasm/include/asm/delay.h +@@ -0,0 +1,25 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_DELAY_H ++#define _ASM_WASM_DELAY_H ++ ++extern void __delay(unsigned long loops); ++extern void __bad_udelay(void); ++extern void __bad_ndelay(void); ++ ++/* ++ * Wasm uses 1 loop = 1 nanosecond. This makes the conversion easy. ++ * ++ * Just like the rest of the kernel, these macros polices you if you try to ++ * delay for too long. You should use a sleep function that calls schedule() ++ * internally if you need longer sleeps than this. In Wasm in particular, usage ++ * of these macros is really discouraged (what are you busy-waiting for?). ++ */ ++ ++#define udelay(n) (__builtin_constant_p(n) && (n) > 20000 ? \ ++ __bad_udelay() : __delay((n) * 1000)) ++ ++#define ndelay(n) (__builtin_constant_p(n) && (n) > 20000000 ? \ ++ __bad_ndelay() : __delay(n)) ++ ++#endif /* _ASM_WASM_DELAY_H */ +diff --git a/arch/wasm/include/asm/elf.h b/arch/wasm/include/asm/elf.h +new file mode 100644 +index 000000000..3a02588f9 +--- /dev/null ++++ b/arch/wasm/include/asm/elf.h +@@ -0,0 +1,66 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_ELF_H ++#define _ASM_WASM_ELF_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define ELF_ARCH EM_WASM ++#define ELF_CLASS ELFCLASS32 ++#define ELF_DATA ELFDATA2LSB ++ ++#define elf_check_arch(x) (((x)->e_machine == ELF_ARCH) && \ ++ ((x)->e_ident[EI_CLASS] == ELF_CLASS)) ++ ++extern bool compat_elf_check_arch(Elf32_Ehdr *hdr); ++#define compat_elf_check_arch compat_elf_check_arch ++ ++#define CORE_DUMP_USE_REGSET ++#define ELF_EXEC_PAGESIZE (PAGE_SIZE) ++ ++/* ++ * This is the location that an ET_DYN program is loaded if exec'ed. Typical ++ * use of this is to invoke "./ld.so someprog" to test out a new version of ++ * the loader. We need to make sure that it is out of the way of the program ++ * that it will "exec", and that there is sufficient room for the brk. ++ */ ++#define ELF_ET_DYN_BASE ((TASK_SIZE / 3) * 2) ++ ++#ifdef CONFIG_64BIT ++#ifdef CONFIG_COMPAT ++#define STACK_RND_MASK (test_thread_flag(TIF_32BIT) ? \ ++ 0x7ff >> (PAGE_SHIFT - 12) : \ ++ 0x3ffff >> (PAGE_SHIFT - 12)) ++#else ++#define STACK_RND_MASK (0x3ffff >> (PAGE_SHIFT - 12)) ++#endif ++#endif ++ ++/* ++ * Provides information on the availiable set of ISA extensions to userspace, ++ * via a bitmap that coorespends to each single-letter ISA extension. This is ++ * essentially defunct, but will remain for compatibility with userspace. ++ */ ++#define ELF_HWCAP (elf_hwcap & ((1UL << RISCV_ISA_EXT_BASE) - 1)) ++extern unsigned long elf_hwcap; ++ ++/* ++ * This yields a string that ld.so will use to load implementation ++ * specific libraries for optimization. This is more specific in ++ * intent than poking at uname or /proc/cpuinfo. ++ */ ++#define ELF_PLATFORM (NULL) ++ ++#define COMPAT_ELF_PLATFORM (NULL) ++ ++#define ELF_CORE_COPY_REGS(dest, regs) \ ++do { \ ++ *(struct user_regs_struct *)&(dest) = \ ++ *(struct user_regs_struct *)regs; \ ++} while (0); ++ ++#endif /* _ASM_WASM_ELF_H */ +diff --git a/arch/wasm/include/asm/entry-common.h b/arch/wasm/include/asm/entry-common.h +new file mode 100644 +index 000000000..20155e98f +--- /dev/null ++++ b/arch/wasm/include/asm/entry-common.h +@@ -0,0 +1,16 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_ENTRY_COMMON_H ++#define _ASM_WASM_ENTRY_COMMON_H ++ ++#include ++ ++/* ++ * Needed by common/entry.c. Returning -1 signals failure, should it ever run... ++ * ++ * Wasm could in theory support seccomp, but the transformation from non-seccomp ++ * to seccomp mode would require quite a bit of thought to get everything right. ++ */ ++#define __secure_computing(...) (-1) ++ ++#endif /* _ASM_WASM_ENTRY_COMMON_H */ +diff --git a/arch/wasm/include/asm/futex.h b/arch/wasm/include/asm/futex.h +new file mode 100644 +index 000000000..05f901e4e +--- /dev/null ++++ b/arch/wasm/include/asm/futex.h +@@ -0,0 +1,68 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_FUTEX_H ++#define _ASM_WASM_FUTEX_H ++ ++#include ++#include ++#include ++ ++#define FUTEX_MAX_LOOPS 128 ++ ++static inline int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, ++ u32 oldval, u32 newval) ++{ ++ int loops; ++ u32 expected; ++ ++ if (!access_ok(uaddr, sizeof(u32))) ++ return -EFAULT; ++ ++ for (loops = 0; loops < FUTEX_MAX_LOOPS; ++loops) { ++ expected = oldval; ++ if (__atomic_compare_exchange_n((volatile u32 *)uaddr, ++ &expected, newval, false, __ATOMIC_SEQ_CST, ++ __ATOMIC_RELAXED)) { ++ *uval = oldval; ++ return 0; ++ } ++ } ++ ++ return -EAGAIN; ++} ++ ++static inline int arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, ++ u32 __user *uaddr) ++{ ++ if (!access_ok(uaddr, sizeof(u32))) ++ return -EFAULT; ++ ++ switch (op) { ++ case FUTEX_OP_SET: ++ *oval = __atomic_exchange_n( ++ (volatile u32 *)uaddr, oparg, __ATOMIC_SEQ_CST); ++ break; ++ case FUTEX_OP_ADD: ++ *oval = __atomic_fetch_add( ++ (volatile u32 *)uaddr, oparg, __ATOMIC_SEQ_CST); ++ break; ++ case FUTEX_OP_OR: ++ *oval = __atomic_fetch_or( ++ (volatile u32 *)uaddr, oparg, __ATOMIC_SEQ_CST); ++ break; ++ case FUTEX_OP_ANDN: ++ *oval = __atomic_fetch_and( ++ (volatile u32 *)uaddr, ~oparg, __ATOMIC_SEQ_CST); ++ break; ++ case FUTEX_OP_XOR: ++ *oval = __atomic_fetch_xor( ++ (volatile u32 *)uaddr, oparg, __ATOMIC_SEQ_CST); ++ break; ++ default: ++ return -ENOSYS; ++ } ++ ++ return 0; ++} ++ ++#endif /* _ASM_WASM_FUTEX_H */ +diff --git a/arch/wasm/include/asm/irq.h b/arch/wasm/include/asm/irq.h +new file mode 100644 +index 000000000..5069bef1f +--- /dev/null ++++ b/arch/wasm/include/asm/irq.h +@@ -0,0 +1,11 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_IRQ_H ++#define _ASM_WASM_IRQ_H ++ ++#define NR_IRQS 32 ++ ++#define WASM_IRQ_IPI 0 ++#define WASM_IRQ_TIMER 1 ++ ++#endif /* _ASM_WASM_IRQ_H */ +diff --git a/arch/wasm/include/asm/irq_work.h b/arch/wasm/include/asm/irq_work.h +new file mode 100644 +index 000000000..fa9c40b0d +--- /dev/null ++++ b/arch/wasm/include/asm/irq_work.h +@@ -0,0 +1,13 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_IRQ_WORK_H ++#define _ASM_WASM_IRQ_WORK_H ++ ++extern void arch_irq_work_raise(void); ++ ++static inline bool arch_irq_work_has_interrupt(void) ++{ ++ return true; ++} ++ ++#endif /* _ASM_WASM_IRQ_WORK_H */ +diff --git a/arch/wasm/include/asm/irqflags.h b/arch/wasm/include/asm/irqflags.h +new file mode 100644 +index 000000000..337a882f9 +--- /dev/null ++++ b/arch/wasm/include/asm/irqflags.h +@@ -0,0 +1,14 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_IRQFLAGS_H ++#define _ASM_WASM_IRQFLAGS_H ++ ++/* ++ * arch_local_save_flags and arch_local_irq_restore are defined as non-static ++ * functions as this header is included from places where percpu-variables and ++ * even definitions for raw_smp_processor_id() cannot be included... ++ */ ++ ++#include ++ ++#endif /* _ASM_WASM_IRQFLAGS_H */ +diff --git a/arch/wasm/include/asm/linkage.h b/arch/wasm/include/asm/linkage.h +new file mode 100644 +index 000000000..49f6776c5 +--- /dev/null ++++ b/arch/wasm/include/asm/linkage.h +@@ -0,0 +1,22 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_LINKAGE_H ++#define _ASM_WASM_LINKAGE_H ++ ++/* ++ * llvm-wasm crashes when generating the output file using the default ++ * definition in linux/linking.h. In addition to this, wasm-ld does not like it ++ * if two function signatures don't match, ruling out using a function with ++ * __attribute__ ((weak, alias("sys_ni_syscall"))) here, even if it is lacking a ++ * prototype (it assumes "one" (void) param). ++ * ++ * This has to be fixed by the host (or possibly some post-process build script) ++ * because there is no way to tell which prototype to use for which symbol. ++ * Getting rid of these stray declarations to begin with (e.g. setting ++ * ARCH_HAS_SYSCALL_WRAPPER) unfortunately causes problems for the ++ * sys_call_table generation. sys_call_table could be generated in some other ++ * way (or shape) but that would require other hacks to find available syscalls. ++ */ ++#define cond_syscall(x) ++ ++#endif /* _ASM_WASM_LINKAGE_H */ +diff --git a/arch/wasm/include/asm/mmu_context.h b/arch/wasm/include/asm/mmu_context.h +new file mode 100644 +index 000000000..e9414c5c0 +--- /dev/null ++++ b/arch/wasm/include/asm/mmu_context.h +@@ -0,0 +1,8 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_MMU_CONTEXT_H ++#define _ASM_WASM_MMU_CONTEXT_H ++ ++#include ++ ++#endif /* _ASM_WASM_MMU_CONTEXT_H */ +diff --git a/arch/wasm/include/asm/panic.h b/arch/wasm/include/asm/panic.h +new file mode 100644 +index 000000000..52ad0fa32 +--- /dev/null ++++ b/arch/wasm/include/asm/panic.h +@@ -0,0 +1,15 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_PANIC_H ++#define _ASM_WASM_PANIC_H ++ ++#include ++ ++static inline void arch_panic_notify(const char *msg) ++{ ++ wasm_panic(msg); ++} ++ ++#include ++ ++#endif /* _ASM_WASM_PANIC_H */ +diff --git a/arch/wasm/include/asm/pgtable.h b/arch/wasm/include/asm/pgtable.h +new file mode 100644 +index 000000000..eeafad742 +--- /dev/null ++++ b/arch/wasm/include/asm/pgtable.h +@@ -0,0 +1,45 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_PGTABLE_H ++#define _ASM_WASM_PGTABLE_H ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++/* ++ * No MMU support so do nothing... ++ * Inspired by the various other NOMMU implementations in the kernel. ++ */ ++ ++#define pgd_present(pgd) (1) ++#define pgd_none(pgd) (0) ++#define pgd_bad(pgd) (0) ++#define pgd_clear(pgdp) ++#define pmd_offset(a, b) ((void *)0) ++ ++#define PAGE_NONE __pgprot(0) ++#define PAGE_SHARED __pgprot(0) ++#define PAGE_COPY __pgprot(0) ++#define PAGE_READONLY __pgprot(0) ++#define PAGE_KERNEL __pgprot(0) ++ ++#define VMALLOC_START 0 ++#define VMALLOC_END 0xFFFFFFFF ++#define KMAP_START 0 ++#define KMAP_END 0xFFFFFFFF ++ ++extern void paging_init(void); ++#define swapper_pg_dir ((pgd_t *) 0) ++ ++/* ++ * ZERO_PAGE is a global shared page that is always zero: used ++ * for zero-mapped memory areas etc.. ++ */ ++extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)]; ++#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) ++ ++#endif /* _ASM_WASM_PGTABLE_H */ +diff --git a/arch/wasm/include/asm/processor.h b/arch/wasm/include/asm/processor.h +new file mode 100644 +index 000000000..93243e16d +--- /dev/null ++++ b/arch/wasm/include/asm/processor.h +@@ -0,0 +1,50 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_PROCESSOR_H ++#define _ASM_WASM_PROCESSOR_H ++ ++#ifndef __ASSEMBLY__ ++ ++struct pt_regs; ++ ++/* 3 GB RAM for userspace, 1 GB for the kernel. */ ++#define TASK_SIZE (0xC0000000) ++ ++/* ++ * We run interrupts on CPU 1, keep it clear. Why not CPU 0? Because init needs ++ * to run on CPU 0 for a while. We don't need interrupts until SMP has started, ++ * but we need init before. ++ */ ++#define IRQ_CPU 1 ++ ++#define cpu_relax() barrier() ++ ++struct thread_struct { ++}; ++ ++#define INIT_THREAD { \ ++} ++ ++void start_thread(struct pt_regs *regs, unsigned long stack_pointer); ++ ++void do_irq_stacked(int irq_nr); ++ ++int user_mode_tail(void); ++ ++struct task_struct; ++static inline unsigned long __get_wchan(struct task_struct *p) ++{ ++ /* Should return the function before schedule() was called. */ ++ /* Will be shown under the "Waiting Channel" of the ps command. */ ++ return 0; ++} ++ ++/* We don't have an instruction pointer. See instruction_pointer.h */ ++#define KSTK_EIP(task) (0) ++ ++/* We could possibly expose the stack pointer (has some data)...? */ ++#define KSTK_ESP(task) (0) ++ ++#endif /* __ASSEMBLY__ */ ++ ++#endif /* _ASM_WASM_PROCESSOR_H */ +diff --git a/arch/wasm/include/asm/ptrace.h b/arch/wasm/include/asm/ptrace.h +new file mode 100644 +index 000000000..40b4ff72d +--- /dev/null ++++ b/arch/wasm/include/asm/ptrace.h +@@ -0,0 +1,35 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_PTRACE_H ++#define _ASM_WASM_PTRACE_H ++ ++#include ++#include ++#include ++ ++#ifndef __ASSEMBLY__ ++ ++#define user_mode(regs) ((regs)->cpuflags & BIT(CPUFLAGS_USER_MODE)) ++ ++/* Not available in Wasm. */ ++#define instruction_pointer(regs) (0) ++ ++#define current_user_stack_pointer() (0) ++ ++/* Not available (maybe we could extract this from a stacktrace?) */ ++#define profile_pc(regs) instruction_pointer(regs) ++ ++#define task_pt_regs(task) ((struct pt_regs *)(task) - 1U) ++#define current_pt_regs() task_pt_regs(current) ++ ++#define task_switch_stack(task) ((struct switch_stack *)task_pt_regs(task) - 1U) ++#define current_switch_stack() task_switch_stack(current) ++ ++static inline int regs_irqs_disabled(struct pt_regs *regs) ++{ ++ return arch_irqs_disabled_flags(arch_local_save_flags()); ++} ++ ++#endif /* !__ASSEMBLY__ */ ++ ++#endif /* _ASM_WASM_PTRACE_H */ +diff --git a/arch/wasm/include/asm/smp.h b/arch/wasm/include/asm/smp.h +new file mode 100644 +index 000000000..d47beeccb +--- /dev/null ++++ b/arch/wasm/include/asm/smp.h +@@ -0,0 +1,28 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_SMP_H ++#define _ASM_WASM_SMP_H ++ ++#include ++ ++#ifdef CONFIG_SMP ++ ++#define raw_smp_processor_id() (current_thread_info()->cpu) ++ ++void __init setup_smp_ipi(void); ++ ++void arch_send_call_function_single_ipi(int cpu); ++ ++static inline void arch_send_call_function_ipi_mask(const struct cpumask *mask) ++{ ++ int cpu; ++ ++ for_each_cpu(cpu, mask) ++ arch_send_call_function_single_ipi(cpu); ++} ++ ++__visible void raise_interrupt(int cpu, int irq_nr); ++ ++#endif /* !CONFIG_SMP */ ++ ++#endif /* _ASM_WASM_SMP_H */ +diff --git a/arch/wasm/include/asm/stacktrace.h b/arch/wasm/include/asm/stacktrace.h +new file mode 100644 +index 000000000..2f702245e +--- /dev/null ++++ b/arch/wasm/include/asm/stacktrace.h +@@ -0,0 +1,20 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_STACKTRACE_H ++#define _ASM_WASM_STACKTRACE_H ++ ++#include ++#include ++ ++#define WASM_STACKTRACE_MAX_SIZE 1000U ++ ++static inline bool on_thread_stack(void) ++{ ++ /* ++ * Since current is directly derived from the stack pointer on Wasm, we ++ * can do this sneaky trick of comparing stack ends. ++ */ ++ return current->stack == (void*)((unsigned long)current & THREAD_MASK); ++} ++ ++#endif /* _ASM_WASM_STACKTRACE_H */ +diff --git a/arch/wasm/include/asm/syscall.h b/arch/wasm/include/asm/syscall.h +new file mode 100644 +index 000000000..a31199740 +--- /dev/null ++++ b/arch/wasm/include/asm/syscall.h +@@ -0,0 +1,62 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_SYSCALL_H ++#define _ASM_WASM_SYSCALL_H ++ ++#include ++#include ++ ++extern void (* const sys_call_table[])(void); ++ ++struct task_struct; ++ ++static inline long syscall_get_nr(struct task_struct *task, ++ struct pt_regs *regs) ++{ ++ return regs->syscall_nr; ++} ++ ++static inline void syscall_rollback(struct task_struct *task, ++ struct pt_regs *regs) ++{ ++ /* We don't need to rollback anything on Wasm. */ ++} ++ ++static inline long syscall_get_error(struct task_struct *task, ++ struct pt_regs *regs) ++{ ++ return IS_ERR_VALUE(regs->syscall_ret) ? regs->syscall_ret : 0; ++} ++ ++static inline long syscall_get_return_value(struct task_struct *task, ++ struct pt_regs *regs) ++{ ++ return regs->syscall_ret; ++} ++ ++static inline void syscall_set_return_value(struct task_struct *task, ++ struct pt_regs *regs, ++ int error, long val) ++{ ++ regs->syscall_ret = error ? (long)error : val; ++} ++ ++static inline void syscall_get_arguments(struct task_struct *task, ++ struct pt_regs *regs, ++ unsigned long *args) ++{ ++ args[0] = regs->syscall_nr; ++ memcpy(&args[1], regs->syscall_args, sizeof(regs->syscall_args)); ++} ++ ++static inline int syscall_get_arch(struct task_struct *task) ++{ ++ return AUDIT_ARCH_WASM32; ++} ++ ++static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs) ++{ ++ return false; ++} ++ ++#endif /* _ASM_WASM_SYSCALL_H */ +diff --git a/arch/wasm/include/asm/thread_info.h b/arch/wasm/include/asm/thread_info.h +new file mode 100644 +index 000000000..2f9c43907 +--- /dev/null ++++ b/arch/wasm/include/asm/thread_info.h +@@ -0,0 +1,105 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_THREAD_INFO_H ++#define _ASM_WASM_THREAD_INFO_H ++ ++#include ++ ++/* ++ * In the Wasm arch, thread_info sits at the top of task_struct and both reside ++ * at the very end of the stack area (which grows downwards). ++ * ++ * HIGHER ADDRESSES ++ * ++ * [ [ [...] ] ] ^ <- (__stack_pointer & THREAD_MASK) + THREAD_SIZE ++ * [ [ [thread_info]] ] | ++ * [ [task_struct ] ] | <- current, current_thread_info() ++ * [ [stack ] ] | <- (stack starts with pt_regs + possibly switch_stack) ++ * [ [ [...] ] ] | <- __stack_pointer (growing towards lower addresses) ++ * [ ] | ++ * [ free space ] | THREAD_SIZE ++ * [ ] v <- (__stack_pointer & THREAD_MASK) ++ * ++ * LOWER ADDRESSES ++ * ++ * As can be seen, current == current_thread_info() in this arch. In order to ++ * access any of these, __stack_pointer can be masked by THREAD_MASK, since ++ * the kernel stack for every task will be aligned on a THREAD_SIZE boundary. ++ * ++ * Example of memory-growing instructions Resides in ++ * -------------------------------------------- -------------------------------- ++ * iX.const, iX.load, local.get, global.get Wasm internal stack ++ * lobal.set __stack_pointer __stack_pointer managed stack ++ * ++ * Stack usage in Wasm is pretty sparse. Most data resides in "locals" or on the ++ * internal Wasm stack. Both of these are not accessible from within Wasm, ++ * except outside the local usage of them of course. The stack we manage is used ++ * for things that Wasm can't put on any of those, for exmaple when a pointer is ++ * constructed when taking the address of an auto variable (i.e. the ++ * function/block scope in C). That stack is referred to by the Wasm global ++ * __stack_pointer and is known by the compiler. It is not part of the Wasm ++ * standard, but makes certain parts of the C standard possible to compile. Two ++ * pages should for this reason be enough as kernel stack. struct task_struct ++ * (including struct thread_info at its base) is about 2K, leaving 6K for the ++ * kernel stack. ++ */ ++#define THREAD_SIZE_ORDER (1) ++#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER) ++#define THREAD_MASK (~(THREAD_SIZE - 1)) ++ ++#ifndef __ASSEMBLY__ ++ ++struct thread_info { ++ unsigned int cpu; ++ unsigned int flags; ++ int preempt_count; /* Needed but not really used */ ++ int instance_depth; /* 0 = idle task, 1 = running */ ++ unsigned long syscall_work; /* SYSCALL_WORK_ flags */ ++}; ++ ++#define INIT_THREAD_INFO(tsk) \ ++{ \ ++ .cpu = 0, \ ++ .flags = 0, \ ++ .preempt_count = INIT_PREEMPT_COUNT, \ ++ .instance_depth = 0, \ ++} ++ ++struct task_struct; ++ ++static inline void *arch_alloc_thread_stack_node( ++ struct task_struct *tsk, int node) ++{ ++ return (void *)((unsigned long)tsk & THREAD_MASK); ++} ++ ++static inline void arch_free_thread_stack(struct task_struct *tsk) { } ++ ++struct task_struct *alloc_task_struct_node(int node); ++void free_task_struct(struct task_struct *tsk); ++ ++#endif /* !__ASSEMBLY__ */ ++ ++#define TIF_SYSCALL_TRACE 0 /* syscall trace active */ ++#define TIF_SIGPENDING 1 /* signal pending */ ++#define TIF_NEED_RESCHED 2 /* rescheduling necessary */ ++#define TIF_NOTIFY_SIGNAL 3 /* signal notifications exist */ ++#define TIF_MEMDIE 4 /* is terminating due to OOM killer */ ++#define TIF_NOTIFY_RESUME 5 /* callback before returning to user */ ++#define TIF_NEVER_RUN 6 /* was never run by the scheduler */ ++#define TIF_RELOAD_PROGRAM 7 /* should reload code at syscall end */ ++#define TIF_DELIVER_SIGNAL 8 /* run sighandler at syscall end */ ++#define TIF_RETURN_SIGNAL 9 /* return sighandler at syscall end */ ++ ++#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) ++#define _TIF_SIGPENDING (1 << TIF_SIGPENDING) ++#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) ++#define _TIF_NOTIFY_SIGNAL (1 << TIF_NOTIFY_SIGNAL) ++#define _TIF_MEMDIE (1 << TIF_MEMDIE) ++#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) ++#define _TIF_NEVER_RUN (1 << TIF_NEVER_RUN) ++#define _TIF_RELOAD_PROGRAM (1 << TIF_RELOAD_PROGRAM) ++#define _TIF_DELIVER_SIGNAL (1 << TIF_DELIVER_SIGNAL) ++#define _TIF_RETURN_SIGNAL (1 << TIF_RETURN_SIGNAL) ++ ++#endif /* _ASM_WASM_THREAD_INFO_H */ +diff --git a/arch/wasm/include/asm/time.h b/arch/wasm/include/asm/time.h +new file mode 100644 +index 000000000..2577a1151 +--- /dev/null ++++ b/arch/wasm/include/asm/time.h +@@ -0,0 +1,9 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_TIME_H ++#define _ASM_WASM_TIME_H ++ ++void wasm_clockevent_enable(void); ++void wasm_program_timer(unsigned long delta); ++ ++#endif /* _ASM_WASM_TIME_H */ +diff --git a/arch/wasm/include/asm/vmalloc.h b/arch/wasm/include/asm/vmalloc.h +new file mode 100644 +index 000000000..f1c2216f2 +--- /dev/null ++++ b/arch/wasm/include/asm/vmalloc.h +@@ -0,0 +1,6 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_VMALLOC_H ++#define _ASM_WASM_VMALLOC_H ++ ++#endif /* _ASM_WASM_VMALLOC_H */ +diff --git a/arch/wasm/include/asm/wasm.h b/arch/wasm/include/asm/wasm.h +new file mode 100644 +index 000000000..20decb1d5 +--- /dev/null ++++ b/arch/wasm/include/asm/wasm.h +@@ -0,0 +1,29 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#ifndef _ASM_WASM_WASM_H ++#define _ASM_WASM_WASM_H ++ ++/* These are symbols imported from the Wasm host. */ ++ ++extern void wasm_panic(const char *msg); ++extern void wasm_dump_stacktrace(char* buffer, unsigned long max_size); ++ ++extern void wasm_start_cpu(unsigned int cpu, struct task_struct *idle_task, ++ unsigned long start_stack); ++extern void wasm_stop_cpu(unsigned int cpu); ++ ++extern struct task_struct *wasm_create_and_run_task( ++ struct task_struct *prev_task, struct task_struct *new_task, ++ const char *name, unsigned long bin_start, unsigned long bin_end, ++ unsigned long data_start, unsigned long table_start); ++extern void wasm_release_task(struct task_struct *dead_task); ++extern struct task_struct *wasm_serialize_tasks(struct task_struct *prev_task, ++ struct task_struct *next_task); ++ ++extern void wasm_load_executable(unsigned long bin_start, unsigned long bin_end, ++ unsigned long data_start, unsigned long table_start); ++extern void wasm_reload_program(void); ++ ++extern void wasm_clone_callback(void); ++ ++#endif /* _ASM_WASM_WASM_H */ +diff --git a/arch/wasm/include/uapi/asm/Kbuild b/arch/wasm/include/uapi/asm/Kbuild +new file mode 100644 +index 000000000..b4bb51a5c +--- /dev/null ++++ b/arch/wasm/include/uapi/asm/Kbuild +@@ -0,0 +1,2 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++generic-y += ucontext.h +diff --git a/arch/wasm/include/uapi/asm/byteorder.h b/arch/wasm/include/uapi/asm/byteorder.h +new file mode 100644 +index 000000000..3f8945ac4 +--- /dev/null ++++ b/arch/wasm/include/uapi/asm/byteorder.h +@@ -0,0 +1,8 @@ ++/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ ++ ++#ifndef _UAPI_ASM_WASM_BYTEORDER_H ++#define _UAPI_ASM_WASM_BYTEORDER_H ++ ++#include ++ ++#endif /* _UAPI_ASM_WASM_BYTEORDER_H */ +diff --git a/arch/wasm/include/uapi/asm/ptrace.h b/arch/wasm/include/uapi/asm/ptrace.h +new file mode 100644 +index 000000000..0761ce261 +--- /dev/null ++++ b/arch/wasm/include/uapi/asm/ptrace.h +@@ -0,0 +1,39 @@ ++/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ ++ ++#ifndef _UAPI_ASM_WASM_PTRACE_H ++#define _UAPI_ASM_WASM_PTRACE_H ++ ++#define PTRACE_SYSEMU 31 ++#define PTRACE_SYSEMU_SINGLESTEP 32 ++ ++#ifndef __ASSEMBLY__ ++ ++/* Registers stored during kernel entry (syscalls, IRQs and exceptions). */ ++struct pt_regs { ++ unsigned long stack_pointer; /* The __stack_pointer global. */ ++ unsigned long cpuflags; /* CPU Flags (interrupt, user mode). */ ++ int syscall_nr; /* Needed by syscall_get_nr() etc. */ ++ long syscall_args[6]; /* Needed by syscall_get_args() etc. */ ++ long syscall_ret; /* Needed by syscall_*_return() etc. */ ++}; ++ ++#define PT_REGS_INIT ((struct pt_regs){.syscall_nr = -1}) ++ ++/* Registers stored when switching between user processes (and signals). */ ++struct switch_stack { ++ /* When kthread, kernel thread callback with arg. */ ++ int (*fn)(void *); ++ void *fn_arg; ++ ++ /* When user task, the __tls_base global. Unused by the kernel. */ ++ unsigned long tls; ++}; ++ ++/* Registers for user processes (gdb etc.), stable ABI compared to pt_regs. */ ++struct user_regs_struct { ++ unsigned long stack_pointer; ++ unsigned long tls; ++}; ++ ++#endif /* __ASSEMBLY__ */ ++#endif /* _UAPI_ASM_WASM_PTRACE_H */ +diff --git a/arch/wasm/include/uapi/asm/sigcontext.h b/arch/wasm/include/uapi/asm/sigcontext.h +new file mode 100644 +index 000000000..7fa987d86 +--- /dev/null ++++ b/arch/wasm/include/uapi/asm/sigcontext.h +@@ -0,0 +1,13 @@ ++/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ ++ ++#ifndef _UAPI_ASM_WASM_SIGCONTEXT_H ++#define _UAPI_ASM_WASM_SIGCONTEXT_H ++ ++#include ++ ++/* State saved before a signal is handled, given to signal handlers. */ ++struct sigcontext { ++ struct user_regs_struct regs; ++}; ++ ++#endif /* _UAPI_ASM_WASM_SIGCONTEXT_H */ +diff --git a/arch/wasm/include/uapi/asm/unistd.h b/arch/wasm/include/uapi/asm/unistd.h +new file mode 100644 +index 000000000..9729b100b +--- /dev/null ++++ b/arch/wasm/include/uapi/asm/unistd.h +@@ -0,0 +1,6 @@ ++/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ ++ ++#define __ARCH_WANT_SYS_CLONE ++#define __ARCH_WANT_SYS_CLONE3 ++ ++#include +diff --git a/arch/wasm/kernel/Makefile b/arch/wasm/kernel/Makefile +new file mode 100644 +index 000000000..a630af519 +--- /dev/null ++++ b/arch/wasm/kernel/Makefile +@@ -0,0 +1,21 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++ ++extra-y += vmlinux.lds ++ ++obj-y += cpu.o ++obj-y += cpuflags.o ++obj-y += entry.o ++obj-y += head.o ++obj-y += irqflags.o ++obj-y += irq.o ++obj-y += process.o ++obj-y += ptrace.o ++obj-y += reboot.o ++obj-y += setup.o ++obj-y += signal.o ++obj-y += smp.o ++obj-y += stack.o ++obj-y += sys_wasm.o ++obj-y += syscall_table.o ++obj-y += time.o ++obj-y += traps.o +diff --git a/arch/wasm/kernel/asm-offsets.c b/arch/wasm/kernel/asm-offsets.c +new file mode 100644 +index 000000000..272f0f461 +--- /dev/null ++++ b/arch/wasm/kernel/asm-offsets.c +@@ -0,0 +1,36 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#define ASM_OFFSETS_C ++ ++#include ++#include ++#include ++ ++void foo(void) ++{ ++/* ++ * struct task_struct is stored just above the thread stack. It is aligned by ++ * L1_CACHE_BYTES, which is enforced by init_task and the task memory allocator. ++ * ++ * sizeof(pt_regs) and sizeof(task_struct) is naturally aligned by their size. ++ * The start of the actual stack has to be 16-byte aligned when calling C code. ++ */ ++#define _THREAD_TASK_STRUCT_OFFSET ALIGN_DOWN(THREAD_SIZE - sizeof(struct task_struct), L1_CACHE_BYTES) ++#define _THREAD_PT_REGS_OFFSET (_THREAD_TASK_STRUCT_OFFSET - sizeof(struct pt_regs)) ++#define _THREAD_SWITCH_STACK_OFFSET (_THREAD_PT_REGS_OFFSET - sizeof(struct switch_stack)) ++#define _THREAD_STACK_START ALIGN_DOWN(_THREAD_SWITCH_STACK_OFFSET, 16) ++ ++ DEFINE(THREAD_TASK_STRUCT_OFFSET, _THREAD_TASK_STRUCT_OFFSET); ++ BLANK(); ++ ++ DEFINE(THREAD_PT_REGS_OFFSET, _THREAD_PT_REGS_OFFSET); ++ DEFINE(PT_REGS_STACK_POINTER, offsetof(struct pt_regs, stack_pointer)); ++ BLANK(); ++ ++ DEFINE(THREAD_SWITCH_STACK_OFFSET, _THREAD_SWITCH_STACK_OFFSET); ++ DEFINE(SWITCH_STACK_TLS, offsetof(struct switch_stack, tls)); ++ BLANK(); ++ ++ DEFINE(THREAD_STACK_START, _THREAD_STACK_START); ++ BLANK(); ++} +diff --git a/arch/wasm/kernel/cpu.c b/arch/wasm/kernel/cpu.c +new file mode 100644 +index 000000000..5fb9aa8ba +--- /dev/null ++++ b/arch/wasm/kernel/cpu.c +@@ -0,0 +1,46 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++#include ++ ++#ifdef CONFIG_PROC_FS ++ ++static void *c_start(struct seq_file *m, loff_t *pos) ++{ ++ if (*pos == nr_cpu_ids) ++ return NULL; ++ ++ *pos = cpumask_next(*pos - 1, cpu_online_mask); ++ if ((*pos) < nr_cpu_ids) ++ return (void *)(uintptr_t)(1 + *pos); ++ return NULL; ++} ++ ++static void *c_next(struct seq_file *m, void *v, loff_t *pos) ++{ ++ (*pos)++; ++ return c_start(m, pos); ++} ++ ++static void c_stop(struct seq_file *m, void *v) ++{ ++} ++ ++static int c_show(struct seq_file *m, void *v) ++{ ++ unsigned long cpu_id = (unsigned long)v - 1; ++ ++ seq_printf(m, "processor\t: %lu\n", cpu_id); ++ seq_printf(m, "vendor_id\t: Wasm\n"); ++ ++ return 0; ++} ++ ++const struct seq_operations cpuinfo_op = { ++ .start = c_start, ++ .next = c_next, ++ .stop = c_stop, ++ .show = c_show, ++}; ++ ++#endif /* CONFIG_PROC_FS */ +diff --git a/arch/wasm/kernel/cpuflags.c b/arch/wasm/kernel/cpuflags.c +new file mode 100644 +index 000000000..a97e9b58a +--- /dev/null ++++ b/arch/wasm/kernel/cpuflags.c +@@ -0,0 +1,5 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++ ++DEFINE_PER_CPU(unsigned long, wasm_cpuflags) = CPUFLAGS_RESET_VALUE; +diff --git a/arch/wasm/kernel/entry.S b/arch/wasm/kernel/entry.S +new file mode 100644 +index 000000000..04087b23f +--- /dev/null ++++ b/arch/wasm/kernel/entry.S +@@ -0,0 +1,299 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++ ++#include ++ ++ ++.globaltype __stack_pointer, i32 ++ ++.section .globals,"",@ ++ ++.globaltype __user_stack_pointer, i32 ++__user_stack_pointer: ++.globaltype __user_tls_base, i32 ++__user_tls_base: ++ ++.section .text,"",@ ++ ++.globl get_user_stack_pointer ++get_user_stack_pointer: ++ .functype get_user_stack_pointer() -> (i32) ++ global.get __user_stack_pointer ++ end_function ++ ++.globl get_user_tls_base ++get_user_tls_base: ++ .functype get_user_tls_base() -> (i32) ++ global.get __user_tls_base ++ end_function ++ ++.functype user_mode_tail() -> (i32) ++.functype wasm_user_mode_tail(i32) -> () ++ ++.globl _user_mode_tail ++_user_mode_tail: ++ .functype _user_mode_tail() -> () ++ .local i32 /* 0: flow */ ++ ++ block ++ call user_mode_tail ++ local.tee 0 ++ i32.eqz ++ br_if 0 ++ ++ local.get 0 ++ call wasm_user_mode_tail ++ end_block ++ ++ end_function ++ ++/* ++ * HIGH ADDRESSES ++ * -------------- ++ * (thread end) <- (current & THREAD_MASK) + THREAD_SIZE ++ * [task_struct] <- current ++ * [pt_regs] ++ * [switch_stack] <- initial __stack_pointer ++ * (alignment) ++ * <- Ready to call C code (16-byte aligned). ++ * (...free space...) ++ * ++ * (thread start) <- current & THREAD_MASK ++ * ------------- ++ * LOW ADDRESSES ++ */ ++ ++.functype __ret_from_fork(i32, i32) -> (i32) ++ ++/* New process. Called by Wasm host when it runs a task for the first time. */ ++.globl ret_from_fork ++ret_from_fork: ++ /* struct task_struct *prev_task, struct task_struct *next_task */ ++ .functype ret_from_fork(i32, i32) -> (i32) ++ ++ /* We can't switch back to a task so no need to save into prev_task. */ ++ ++ /* Load __stack_pointer from the new task's kernel stack area. */ ++ local.get 1 ++ i32.const zeroptr-THREAD_TASK_STRUCT_OFFSET+THREAD_STACK_START ++ i32.add ++ global.set __stack_pointer ++ ++ /* ++ * Finish up in C. Returns true if we have a clone callback to call. ++ * (Upon return, the correct cpuflags for userland have been loaded.) ++ */ ++ local.get 0 ++ local.get 1 ++ call __ret_from_fork ++ ++ /* Load __user_stack_pointer. */ ++ local.get 1 ++ i32.const zeroptr-THREAD_TASK_STRUCT_OFFSET+THREAD_PT_REGS_OFFSET+PT_REGS_STACK_POINTER ++ i32.add ++ i32.load 0 ++ global.set __user_stack_pointer ++ ++ /* Load __user_tls_base. */ ++ local.get 1 ++ i32.const zeroptr-THREAD_TASK_STRUCT_OFFSET+THREAD_SWITCH_STACK_OFFSET+SWITCH_STACK_TLS ++ i32.add ++ i32.load 0 ++ global.set __user_tls_base ++ ++ /* Clean up the stack. */ ++ global.get __stack_pointer ++ i32.const zeroptr-THREAD_STACK_START+THREAD_TASK_STRUCT_OFFSET ++ i32.add ++ global.set __stack_pointer ++ ++ /* In theory, the first thing we execute may be a signal handler. */ ++ call _user_mode_tail ++ ++ end_function ++ ++.macro WASM_SYSCALL_ASM_HEAD ++ /* ++ * The kernel expects pt_regs to be populated so save what we know. ++ * The following fields are saved in the C part of this handling: ++ * * cpuflags is set to appropriate values. ++ * * syscall_nr and syscall_args are set from our call parameters. ++ * * syscall_ret is set and returned to us. ++ */ ++ ++ local.get 0 ++ global.set __user_stack_pointer ++ local.get 1 ++ global.set __user_tls_base ++ ++ /* Allocate pt_regs + switch_stack + stack alignment. */ ++ global.get __stack_pointer ++ i32.const zeroptr-THREAD_TASK_STRUCT_OFFSET+THREAD_STACK_START ++ i32.add ++ global.set __stack_pointer ++ ++ /* Save __user_stack_pointer. */ ++ global.get __stack_pointer ++ i32.const zeroptr-THREAD_STACK_START+THREAD_PT_REGS_OFFSET+PT_REGS_STACK_POINTER ++ i32.add ++ global.get __user_stack_pointer ++ i32.store 0 ++ ++ /* Save __user_tls_base. */ ++ global.get __stack_pointer ++ i32.const zeroptr-THREAD_STACK_START+THREAD_SWITCH_STACK_OFFSET+SWITCH_STACK_TLS ++ i32.add ++ global.get __user_tls_base ++ i32.store 0 ++ ++ /* ++ * Note: we don't need to swap the stack pointer, it already happened ++ * automatically when calling into the vmlinux Wasm instance. ++ */ ++.endm ++ ++.macro WASM_SYSCALL_ASM_FOOT ++ /* (The cpuflags have already been restored in C.) */ ++ ++ /* Load __user_stack_pointer. */ ++ global.get __stack_pointer ++ i32.const zeroptr-THREAD_STACK_START+THREAD_PT_REGS_OFFSET+PT_REGS_STACK_POINTER ++ i32.add ++ i32.load 0 ++ global.set __user_stack_pointer ++ ++ /* Load __user_tls_base. */ ++ global.get __stack_pointer ++ i32.const zeroptr-THREAD_STACK_START+THREAD_SWITCH_STACK_OFFSET+SWITCH_STACK_TLS ++ i32.add ++ i32.load 0 ++ global.set __user_tls_base ++ ++ /* Deallocate stack alignment + switch_stack + pt_regs. */ ++ global.get __stack_pointer ++ i32.const zeroptr-THREAD_STACK_START+THREAD_TASK_STRUCT_OFFSET ++ i32.add ++ global.set __stack_pointer ++ ++ /* ++ * Note: we don't need to swap the __stack_pointer, it will ++ * happen automatically when returning back into the user code ++ * Wasm instance (as that instance has its own __stack_pointer). ++ * ++ * We can exploit this as we're basically in userland, but with ++ * the kernel stack pointer loaded. This allows us to play tricks ++ * with execution in userland without setting a program counter. ++ * ++ * This is where signal handlers are called, and returned, and exec() ++ * calls stop the execution of the user program. In the case of exec() ++ * and signal return, the call stack collapses (this call never returns). ++ */ ++ call _user_mode_tail ++ ++ end_function ++.endm ++ ++.functype __wasm_syscall_0(i32) -> (i32) ++.functype __wasm_syscall_1(i32, i32) -> (i32) ++.functype __wasm_syscall_2(i32, i32, i32) -> (i32) ++.functype __wasm_syscall_3(i32, i32, i32, i32) -> (i32) ++.functype __wasm_syscall_4(i32, i32, i32, i32, i32) -> (i32) ++.functype __wasm_syscall_5(i32, i32, i32, i32, i32, i32) -> (i32) ++.functype __wasm_syscall_6(i32, i32, i32, i32, i32, i32, i32) -> (i32) ++ ++/* ++ * These syscall functions should be called from userland code. In order to skip ++ * slow JavaScript glue code, they directly transfer all state needed into the ++ * kernel. This means that two initial parammeters are added for sp and tp. ++ * Parameter 0 [sp]: the userland stack pointer. ++ * Parameter 1 [tp]: the userland TLS pointer. ++ * Parameter 2 [nr]: the syscall nr. ++ * Parameter 3..8 [argN]: syscall argument(s) 0..5, where applicable. ++ * ++ * The kernel never modifies sp or tp for the calling task during syscalls and ++ * there is thus no need to restore them after the syscall returns. Apart from ++ * diagnosics, they only play a role in the clone and exec family of syscalls. ++ * Clone can be made to copy the supplied sp and tp to the new task. Exec should ++ * maintain the tp even for new process images (this use case is quite sketchy). ++ * Considering that the kernel does not care if userland even has a stack or TLS ++ * area, it would be OK to not transfer these pointers at all if desired. In ++ * both the clone and exec cases, the initial values of sp and tp would not be ++ * loaded at a syscall site, but instead during the ret_from_fork code flow. ++ */ ++.globl wasm_syscall_0 ++wasm_syscall_0: ++ .functype wasm_syscall_0(i32, i32, i32) -> (i32) ++ WASM_SYSCALL_ASM_HEAD ++ local.get 2 ++ call __wasm_syscall_0 ++ WASM_SYSCALL_ASM_FOOT ++ ++.globl wasm_syscall_1 ++wasm_syscall_1: ++ .functype wasm_syscall_1(i32, i32, i32, i32) -> (i32) ++ WASM_SYSCALL_ASM_HEAD ++ local.get 2 ++ local.get 3 ++ call __wasm_syscall_1 ++ WASM_SYSCALL_ASM_FOOT ++ ++.globl wasm_syscall_2 ++wasm_syscall_2: ++ .functype wasm_syscall_2(i32, i32, i32, i32, i32) -> (i32) ++ WASM_SYSCALL_ASM_HEAD ++ local.get 2 ++ local.get 3 ++ local.get 4 ++ call __wasm_syscall_2 ++ WASM_SYSCALL_ASM_FOOT ++ ++.globl wasm_syscall_3 ++wasm_syscall_3: ++ .functype wasm_syscall_3(i32, i32, i32, i32, i32, i32) -> (i32) ++ WASM_SYSCALL_ASM_HEAD ++ local.get 2 ++ local.get 3 ++ local.get 4 ++ local.get 5 ++ call __wasm_syscall_3 ++ WASM_SYSCALL_ASM_FOOT ++ ++.globl wasm_syscall_4 ++wasm_syscall_4: ++ .functype wasm_syscall_4(i32, i32, i32, i32, i32, i32, i32) -> (i32) ++ WASM_SYSCALL_ASM_HEAD ++ local.get 2 ++ local.get 3 ++ local.get 4 ++ local.get 5 ++ local.get 6 ++ call __wasm_syscall_4 ++ WASM_SYSCALL_ASM_FOOT ++ ++.globl wasm_syscall_5 ++wasm_syscall_5: ++ .functype wasm_syscall_5(i32, i32, i32, i32, i32, i32, i32, i32) -> (i32) ++ WASM_SYSCALL_ASM_HEAD ++ local.get 2 ++ local.get 3 ++ local.get 4 ++ local.get 5 ++ local.get 6 ++ local.get 7 ++ call __wasm_syscall_5 ++ WASM_SYSCALL_ASM_FOOT ++ ++.globl wasm_syscall_6 ++wasm_syscall_6: ++ .functype wasm_syscall_6(i32, i32, i32, i32, i32, i32, i32, i32, i32) -> (i32) ++ WASM_SYSCALL_ASM_HEAD ++ local.get 2 ++ local.get 3 ++ local.get 4 ++ local.get 5 ++ local.get 6 ++ local.get 7 ++ local.get 8 ++ call __wasm_syscall_6 ++ WASM_SYSCALL_ASM_FOOT +diff --git a/arch/wasm/kernel/head.S b/arch/wasm/kernel/head.S +new file mode 100644 +index 000000000..e7403fcf2 +--- /dev/null ++++ b/arch/wasm/kernel/head.S +@@ -0,0 +1,110 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++.globaltype __stack_pointer, i32 ++ ++.functype start_kernel() -> () ++.functype start_secondary() -> () ++ ++/* vmlinux entrypoint */ ++.globl _start ++_start: ++ .functype _start() -> () ++ .local i32 /* alloc_limit */ ++ ++ /* ++ * init_task resides just after the start of the stack. Higher addresses ++ * contain task_struct init_task data, while the stack grows downwards. ++ * ++ * The initial stack needs to be 16-byte aligned when calling C code. ++ * init_task already has a much higher alignment (by L1_CACHE_BYTES). ++ */ ++ i32.const init_task ++ global.set __stack_pointer ++ ++ /* Save static memory used by vmlinux. */ ++ i32.const memory_start ++ i32.const 0x10000 /* The first page is reserved for trapping nullptr. */ ++ i32.store 0 ++ i32.const memory_kernel_break ++ memory.size 0 ++ i32.const 0x10000 /* Multiply by Wasm page size (65k). */ ++ i32.mul ++ i32.store 0 ++ ++ /* ++ * By some trial-and-error in Firefox and (mostly) Chromium: ++ * * Allocating the full address space (4 GB) works most of the time. ++ * * Allocating 4 GB fails often enough to be unstable. Curiously, it ++ * does not seem to have anything to do with free memory, and just ++ * reloading the page fixes it. Waiting a bit might be beneficial. ++ * * Doing it from within Wasm seems to succeed more often(?). ++ * * Allocating it in one go works better than stepping, as below, and ++ * would allow 4 GB. But stepping is more reliable. ++ * * Stepping all the way from 4 GB makes Chromium accept the allocation ++ * soon enough, but then crash with SIGILL, probably because of OOM. ++ * * Stepping from 500 MB is a tradeoff with all things considered. It ++ * ought indeed to be enough for anybody! (Oh, old joke, sorry...) ++ * ++ * Considering the above heuristics, a fair approach seems to start high ++ * and aggressively step downwards, one page at a time. But not too high ++ * or there will be OOM troubles related to current default browser ++ * settings. Sadly, browsers don't seem to handle this in a very well ++ * defined way, and we have to be moderately aggressive. An even more ++ * aggressive approach that surprisingly works is to try again and again ++ * with the same allocation size, but stepping almost achieves that. ++ * ++ * Whatever happens, the memory is zero-initialized and hopefully ++ * overcommitted by the host OS. If it is not, that should be fixed! ++ * Even better would be MMU support in Wasm, and this problem would be ++ * solved altogether. And a whole slew of other problems too! ++ * ++ * Note that we cannot allocate the last page from within Wasm (even ++ * though it is possible from the JavaScript host to create a Memory ++ * with initial: 0x10000, memory.grow only allows us to get to 0xFFFF). ++ * This is not too bad, as this is almost like not placing anything in ++ * the first page to catch null pointers. This guards underflow instead. ++ */ ++ i32.const 0x2000 /* Immediately decremented by 1 in the loop below. */ ++ memory.size 0 /* Returns the current number of pages. */ ++ i32.sub /* Try grow by the difference, (max - curr). */ ++ local.set 0 ++ loop ++ local.get 0 ++ i32.const 1 ++ i32.sub ++ local.tee 0 ++ ++ memory.grow 0 ++ i32.const -1 /* Check if allocation failed (returned -1). */ ++ i32.eq ++ br_if 0 ++ end_loop ++ ++ block ++ local.get 0 ++ i32.const 16 ++ i32.lt_u ++ br_if 0 ++ ++ i32.const memory_end ++ local.get 0 ++ i32.const 0x10000 /* Multiply by Wasm page size (65k). */ ++ i32.mul ++ i32.store 0 ++ ++ call start_kernel /* Start the kernel! */ ++ end_block ++ ++ /* If we ever get here, the memory allocation failed. */ ++ end_function ++ ++.globl _start_secondary ++_start_secondary: ++ .functype _start_secondary(i32) -> () ++ local.get 0 ++ global.set __stack_pointer ++ call start_secondary ++ /* start_secondary should never return. */ ++ unreachable ++ ++ end_function +diff --git a/arch/wasm/kernel/irq.c b/arch/wasm/kernel/irq.c +new file mode 100644 +index 000000000..9092bf194 +--- /dev/null ++++ b/arch/wasm/kernel/irq.c +@@ -0,0 +1,55 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++#include ++#include ++ ++static unsigned int wasm_irq_startup(struct irq_data *data) ++{ ++ return 0; ++} ++ ++static void wasm_irq_noop(struct irq_data *data) ++{ ++} ++ ++static int ++wasm_irq_set_affinity(struct irq_data *data, const struct cpumask *mask, ++ bool force) ++{ ++#ifdef CONFIG_SMP ++ printk("wasm_irq_set_affinity: %d %d %d", data->irq, cpumask_weight(mask), cpumask_first(mask)); ++ return 0; ++#endif ++} ++ ++struct irq_chip wasm_irq_chip = { ++ .name = "wasm", ++ .irq_startup = wasm_irq_startup, ++ .irq_shutdown = wasm_irq_noop, ++ .irq_enable = wasm_irq_noop, ++ .irq_disable = wasm_irq_noop, ++ .irq_ack = wasm_irq_noop, ++ .irq_mask = wasm_irq_noop, ++ .irq_unmask = wasm_irq_noop, ++ .irq_set_affinity = wasm_irq_set_affinity, ++ .flags = IRQCHIP_SKIP_SET_WAKE, ++}; ++ ++void __init init_IRQ(void) ++{ ++ int irq; ++ ++ for (irq = 0; irq < NR_IRQS; ++irq) { ++ if (irq == WASM_IRQ_IPI || irq == WASM_IRQ_TIMER) { ++ irq_set_percpu_devid(irq); ++ irq_set_chip_and_handler( ++ irq, &wasm_irq_chip, handle_percpu_devid_irq); ++ } else { ++ irq_set_chip_and_handler( ++ irq, &wasm_irq_chip, handle_simple_irq); ++ } ++ } ++ ++ setup_smp_ipi(); ++} +diff --git a/arch/wasm/kernel/irqflags.c b/arch/wasm/kernel/irqflags.c +new file mode 100644 +index 000000000..cd8e86e90 +--- /dev/null ++++ b/arch/wasm/kernel/irqflags.c +@@ -0,0 +1,21 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++ ++#include ++#include ++ ++unsigned long arch_local_save_flags(void) ++{ ++ if (*this_cpu_ptr(&wasm_cpuflags) & BIT(CPUFLAGS_INTERRUPT)) ++ return ARCH_IRQ_ENABLED; ++ return ARCH_IRQ_DISABLED; ++} ++ ++void arch_local_irq_restore(unsigned long flags) ++{ ++ if (flags == ARCH_IRQ_DISABLED) ++ *this_cpu_ptr(&wasm_cpuflags) &= ~BIT(CPUFLAGS_INTERRUPT); ++ else ++ *this_cpu_ptr(&wasm_cpuflags) |= BIT(CPUFLAGS_INTERRUPT); ++} +diff --git a/arch/wasm/kernel/process.c b/arch/wasm/kernel/process.c +new file mode 100644 +index 000000000..1eaa35d8f +--- /dev/null ++++ b/arch/wasm/kernel/process.c +@@ -0,0 +1,282 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static cpumask_t user_cpus = CPU_MASK_NONE; ++ ++struct task_struct *__sched ++__switch_to(struct task_struct *prev_task, struct task_struct *next_task) ++{ ++ /* ++ * Here, a typical arch would normally: ++ * * Swap registers and stack. ++ * * Return to the instruction pointer of the new task. ++ * ++ * For new tasks (after fork), it would normally: ++ * * Swap registers and stack. ++ * * Call schedule_tail(), now in the context of the new process. ++ * * If there is a kernel_fn set, call it with correct callback arg. ++ * * Call syscall_exit_to_user_mode(current_pt_regs()) or equivalent. ++ * -> Return to the new instruciton pointer, ending up in userland. ++ * ++ * When prev_task was swapped in again (on another reschedule), we would ++ * continue here and return back to the scheduler. However, Wasm can't ++ * do this. Only the in-memory part of the call stack can be swapped and ++ * there is no way to jump. Instead, we have to resort to serializing ++ * processes (in the cooperative multitasking sense) by launching ++ * several threads of execution on the host and use locks to make sure ++ * that only one process at a time is running on the same cpu. ++ * ++ * As soon as more CPUs are enabled, we can start running concurrently, ++ * by putting each task (except idle) on separate CPUs. Before that, ++ * init and kthreadd will need to both run, until smp is started. ++ * Thankfully we have control over these threads and know they will not ++ * hog the CPU. They might call schedule() on longer waits, that's fine. ++ * ++ * There is still the issue with idle threads, we could optimize to get ++ * away with them, which would cut the number of tasks used in the ++ * system by half. However, doing so is pretty annoying, as the idle ++ * loop is actually doing something and will eventually need to call ++ * schedule_idle(). For now, we serialize them too. ++ */ ++ ++ struct task_struct *last_task; ++ char name[TASK_COMM_LEN]; ++ ++ /* For user code. */ ++ unsigned long bin_start = 0U; ++ unsigned long bin_end = 0U; ++ unsigned long data_start = 0U; ++ ++ if (task_thread_info(next_task)->flags & _TIF_NEVER_RUN) { ++ task_thread_info(next_task)->flags &= ~_TIF_NEVER_RUN; ++ ++ /* Get the name to aid debugging. */ ++ get_task_comm(name, next_task); ++ ++ /* For user executables, we need to clone the Wasm instance. */ ++ if (next_task->mm->start_code) { ++ bin_start = next_task->mm->start_code; ++ bin_end = next_task->mm->end_code; ++ data_start = next_task->mm->start_data; ++ } ++ ++ /* This is called instead of serialize the first time. */ ++ last_task = wasm_create_and_run_task(prev_task, next_task, name, ++ bin_start, bin_end, data_start, 0U); ++ } else { ++ last_task = wasm_serialize_tasks(prev_task, next_task); ++ } ++ ++ /* If/when we reach here, we got __switch_to():ed by another task. */ ++ ++ /* last_task is the previous task (never prev_task, maybe next_task). */ ++ return last_task; ++} ++ ++static int user_task_set_affinity(struct task_struct *p) ++{ ++ /* ++ * TODO: This function needs a review of proper approach and locking! ++ * It's probably best to take a step back and think about how this ++ * should be implemented properly in the first place, instead of adding ++ * band aid on top of about every line that violates this and that. That ++ * includes fixing release_thread() and garbage collecting unused CPUs. ++ * ++ * We may also have to move kthreads to IRQ_CPU (with an option of the ++ * boot cpu before IRQ_CPU is up) in case they risk getting blocked. ++ */ ++ int retval; ++ int cpu; ++ ++ /* Kthreads can be allowed to run on any online CPU. */ ++ if (p->flags & PF_KTHREAD) ++ return 0; ++ ++hack: ++ cpu = cpumask_first_zero(&user_cpus); ++ if (cpu >= nr_cpu_ids) ++ return -EBUSY; ++ ++ if(cpu == IRQ_CPU) { ++ /* TODO: We should mark IRQ_CPU as taken at boot instead. */ ++ cpumask_set_cpu(cpu, &user_cpus); ++ goto hack; ++ } ++ ++ if (!cpu_online(cpu)) { ++ BUG_ON(!cpu_possible(cpu)); ++ ++ /* We should add_cpu(cpu) if we properly supported hotplug... */ ++ retval = cpu_device_up(get_cpu_device(cpu)); ++ if (retval) ++ return retval; ++ } ++ ++ cpumask_set_cpu(cpu, &user_cpus); ++ ++ retval = set_cpus_allowed_ptr(p, cpumask_of(cpu)); ++ if (retval) { ++ cpumask_clear_cpu(cpu, &user_cpus); ++ return retval; ++ } ++ ++ p->flags |= PF_NO_SETAFFINITY; ++ ++ return 0; ++} ++ ++asmlinkage unsigned ++__ret_from_fork(struct task_struct *prev_task, struct task_struct *next_task) ++{ ++ struct switch_stack *next_switch_stack = task_switch_stack(next_task); ++ ++ schedule_tail(prev_task); ++ ++ /* Kernel thread callback. */ ++ if (next_switch_stack->fn) { ++ next_switch_stack->fn(next_switch_stack->fn_arg); ++ /* ++ * Kernel threads can return, and in doing so, return to user ++ * space. This happens for the first user process (init). ++ */ ++ ++ BUG_ON(current->flags & PF_KTHREAD); ++ ++ /* ++ * The binfmt loader would have set _TIF_RELOAD_PROGRAM ++ * but we clear it now so that future syscalls don't trap. ++ */ ++ current_thread_info()->flags &= ~_TIF_RELOAD_PROGRAM; ++ } ++ ++ /* ++ * syscall_exit_to_user_mode() turns off interrupts, as most ++ * architectures would IRET right after it, enabling them again. We ++ * emulate this behaviour by loading cpuflags, which should both enable ++ * interrupts again but also drop the privilege level down to USER_MODE. ++ */ ++ syscall_exit_to_user_mode(current_pt_regs()); ++ *this_cpu_ptr(&wasm_cpuflags) = current_pt_regs()->cpuflags; ++ ++ /* ++ * After returning, the Wasm module binary will be initialized and run. ++ * We run any signal handlers that should be run first, then: ++ * kthread case: the host will call _start(). ++ * clone callback case: the host will call __libc_clone_callback(). ++ */ ++ return !(next_switch_stack->fn); ++} ++ ++void flush_thread(void) ++{ ++ /* Wasm has no FP state to reset, so do nothing. */ ++} ++ ++int copy_thread(struct task_struct *p, const struct kernel_clone_args *args) ++{ ++ struct pt_regs *parent_pt_regs = current_pt_regs(); ++ struct switch_stack *parent_switch_stack = current_switch_stack(); ++ ++ struct pt_regs *child_pt_regs = task_pt_regs(p); ++ struct switch_stack *child_switch_stack = task_switch_stack(p); ++ ++ task_thread_info(p)->flags |= _TIF_NEVER_RUN; ++ ++ if (unlikely(args->fn)) { ++ /* Kernel thread */ ++ memset(child_pt_regs, 0, sizeof(*child_pt_regs)); ++ child_pt_regs->stack_pointer = (unsigned long)child_switch_stack; ++ child_pt_regs->cpuflags = BIT(CPUFLAGS_INTERRUPT); ++ ++ memset(child_switch_stack, 0, sizeof(*child_switch_stack)); ++ child_switch_stack->fn = args->fn; ++ child_switch_stack->fn_arg = args->fn_arg; ++ } else { ++ /* User thread */ ++ *child_pt_regs = *parent_pt_regs; ++ if (args->stack) ++ child_pt_regs->stack_pointer = args->stack; ++ ++ *child_switch_stack = *parent_switch_stack; ++ child_switch_stack->fn = NULL; ++ child_switch_stack->fn_arg = NULL; ++ if (args->flags & CLONE_SETTLS) ++ child_switch_stack->tls = args->tls; ++ } ++ ++ if (!p->mm->binfmt) { ++ /* These are normally not zeroed out in copy_process(). */ ++ current->mm->start_code = 0; ++ current->mm->end_code = 0; ++ current->mm->start_stack = 0; ++ current->mm->start_data = 0; ++ current->mm->end_data = 0; ++ } ++ ++ return user_task_set_affinity(p); ++} ++ ++/* ++ * Set up a thread for executing a new program. ++ */ ++void start_thread(struct pt_regs *regs, unsigned long stack_pointer) ++{ ++ memset(regs, 0, sizeof(*regs)); ++ regs->stack_pointer = stack_pointer; ++ regs->cpuflags = BIT(CPUFLAGS_USER_MODE) | BIT(CPUFLAGS_INTERRUPT); ++ ++ wasm_load_executable(current->mm->start_code, current->mm->end_code, ++ current->mm->start_data, 0U); ++ ++ /* Reload the program when the current syscall exits. */ ++ current_thread_info()->flags |= _TIF_RELOAD_PROGRAM; ++} ++ ++void release_thread(struct task_struct *dead_task) ++{ ++ /* TODO: This code also needs review, like user_task_set_affinity(). */ ++ if (!(dead_task->flags & PF_KTHREAD)) { ++ BUG_ON(dead_task->nr_cpus_allowed != 1); ++ BUG_ON(cpumask_first(&dead_task->cpus_mask) ++ != task_thread_info(dead_task)->cpu); ++ cpumask_clear_cpu(task_thread_info(dead_task)->cpu, &user_cpus); ++ } ++ ++ wasm_release_task(dead_task); ++} ++ ++void show_regs(struct pt_regs *regs) ++{ ++ show_regs_print_info(KERN_DEFAULT); ++ ++ pr_cont("cpuflags: %08x sp: %08x flags: %08x preempt_count: %08x\n", ++ (unsigned)regs->cpuflags, ++ (unsigned)regs->stack_pointer, ++ (unsigned)current_thread_info()->flags, ++ (unsigned)current_thread_info()->preempt_count); ++} ++ ++void show_stack(struct task_struct *task, unsigned long *stack, ++ const char *loglvl) ++{ ++ char *stack_trace; ++ ++ printk("%sStack from %08lx:", loglvl, (unsigned long)stack); ++ ++ stack_trace = kmalloc(WASM_STACKTRACE_MAX_SIZE, GFP_ATOMIC); ++ if (stack_trace) { ++ wasm_dump_stacktrace(stack_trace, WASM_STACKTRACE_MAX_SIZE); ++ printk("%s", stack_trace); ++ } else { ++ printk("Failed to allocate stack trace buffer."); ++ } ++ kfree(stack_trace); ++} +diff --git a/arch/wasm/kernel/ptrace.c b/arch/wasm/kernel/ptrace.c +new file mode 100644 +index 000000000..a52667068 +--- /dev/null ++++ b/arch/wasm/kernel/ptrace.c +@@ -0,0 +1,13 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++ ++void ptrace_disable(struct task_struct *child) ++{ ++} ++ ++long arch_ptrace(struct task_struct *child, long request, unsigned long addr, ++ unsigned long data) ++{ ++ return ptrace_request(child, request, addr, data); ++} +diff --git a/arch/wasm/kernel/reboot.c b/arch/wasm/kernel/reboot.c +new file mode 100644 +index 000000000..271e4ef64 +--- /dev/null ++++ b/arch/wasm/kernel/reboot.c +@@ -0,0 +1,31 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++#include ++#include ++#include ++ ++void machine_restart(char *cmd) ++{ ++ local_irq_disable(); ++ smp_send_stop(); ++ ++ do_kernel_restart(cmd); ++ ++ printk("Reboot failed -- System halted\n"); ++ for (;;); ++} ++ ++void machine_halt(void) ++{ ++ local_irq_disable(); ++ smp_send_stop(); ++ for (;;); ++} ++ ++void machine_power_off(void) ++{ ++ local_irq_disable(); ++ smp_send_stop(); ++ do_kernel_power_off(); ++} +diff --git a/arch/wasm/kernel/setup.c b/arch/wasm/kernel/setup.c +new file mode 100644 +index 000000000..2ea9cc364 +--- /dev/null ++++ b/arch/wasm/kernel/setup.c +@@ -0,0 +1,84 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * The format of "screen_info" is strange, and due to early ++ * i386-setup code. This is just enough to make the console ++ * code think we're on a VGA color display. ++ */ ++struct screen_info screen_info = { ++ .orig_x = 0, ++ .orig_y = 25, ++ .orig_video_cols = 80, ++ .orig_video_lines = 25, ++ .orig_video_isVGA = 1, ++ .orig_video_points = 16, ++}; ++ ++unsigned long memory_start; ++EXPORT_SYMBOL(memory_start); ++ ++unsigned long memory_end; ++EXPORT_SYMBOL(memory_end); ++ ++unsigned long memory_kernel_break; ++EXPORT_SYMBOL(memory_kernel_break); ++ ++void __init smp_prepare_cpus(unsigned int max_cpus) ++{ ++ unsigned i; ++ ++ for_each_possible_cpu(i) ++ set_cpu_present(i, true); ++} ++ ++void __init smp_init_cpus(void) ++{ ++ unsigned i; ++ unsigned int ncpus = NR_CPUS; /* TODO: make this configurable */ ++ ++ pr_info("%s: Core Count = %d\n", __func__, ncpus); ++ ++ if (ncpus > NR_CPUS) { ++ ncpus = NR_CPUS; ++ pr_info("%s: limiting core count by %d\n", __func__, ncpus); ++ } ++ ++ for (i = 0; i < ncpus; ++i) ++ set_cpu_possible(i, true); ++} ++ ++void __init smp_prepare_boot_cpu(void) ++{ ++} ++ ++void __init setup_arch(char **cmdline_p) ++{ ++ unsigned long max_zone_pfn[MAX_NR_ZONES] = {0}; ++ ++ /* Save unparsed command line copy for /proc/cmdline */ ++ *cmdline_p = boot_command_line; ++ ++ parse_early_param(); ++ ++ /* See head.S for the logic that sets up these values. */ ++ memblock_reserve(memory_start, memory_kernel_break - memory_start); ++ memblock_add(memory_start, memory_end - memory_start); ++ ++ /* pcpu_find_block_fit() returns signed 32-bit memory addresses, ugh. */ ++ memblock_set_current_limit(0x80000000); /* Only positive addresses. */ ++ ++ /* This is needed so that more than 128 allocations can be made. */ ++ memblock_allow_resize(); ++ ++ /* Initialize zones, so that memory can be allocated beyond bootmem. */ ++ max_zone_pfn[ZONE_NORMAL] = memory_end >> PAGE_SHIFT; ++ free_area_init(max_zone_pfn); ++ ++ smp_init_cpus(); ++} +diff --git a/arch/wasm/kernel/signal.c b/arch/wasm/kernel/signal.c +new file mode 100644 +index 000000000..ec1753ae2 +--- /dev/null ++++ b/arch/wasm/kernel/signal.c +@@ -0,0 +1,189 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++#include ++ ++struct rt_sigframe { ++ int sig_param; ++ ++ /* ++ * info_param and uc_param are convenience pointers that allow extension ++ * of rt_sigframe in the future and easy detection of whether SA_SIGINFO ++ * is set (they are NULL if it is not set). ++ */ ++ siginfo_t *info_param; ++ void *uc_param; ++ ++ union { ++ /* Signature the kernel uses internaly ("opaque type"). */ ++ __sighandler_t sa_handler; ++ ++ /* Signature libc should use when SA_SIGINFO is set. */ ++ void (*sigaction)(int sig, siginfo_t *info, void *uc); ++ ++ /* Signature libc should use when SA_SIGINFO is not set. */ ++ void (*handler)(int sig); ++ }; ++ ++ /* ++ * info and uc are used for sigaction (SA_SIGINFO) but ignored for ++ * handler (!SA_SIGINFO). However, uc is still used by the kernel when ++ * handler is used, as it stores the regs to restore upon sigreturn. ++ * This is what allows signal handlers to be stacked. ++ */ ++ struct siginfo info; ++ struct ucontext uc; ++}; ++ ++SYSCALL_DEFINE0(rt_sigreturn) ++{ ++ struct pt_regs *regs = current_pt_regs(); ++ struct switch_stack *switch_stack = current_switch_stack(); ++ struct rt_sigframe __user *frame = ++ (struct rt_sigframe __user *)regs->stack_pointer; ++ struct user_regs_struct __user *user_regs = &frame->uc.uc_mcontext.regs; ++ sigset_t set; ++ ++ /* Always make any pending restarted system calls return -EINTR */ ++ current->restart_block.fn = do_no_restart_syscall; ++ ++ if (!access_ok(frame, sizeof(*frame))) ++ goto badframe; ++ ++ if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) ++ goto badframe; ++ ++ set_current_blocked(&set); ++ ++ if (__get_user(regs->stack_pointer, &user_regs->stack_pointer)) ++ goto badframe; ++ if (__get_user(switch_stack->tls, &user_regs->tls)) ++ goto badframe; ++ ++ if (restore_altstack(&frame->uc.uc_stack)) ++ goto badframe; ++ ++ current_thread_info()->flags |= _TIF_RETURN_SIGNAL; ++ ++ return 0; ++ ++badframe: ++ force_sig(SIGSEGV); ++ ++ return 0; ++} ++ ++static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, ++ struct pt_regs *regs) ++{ ++ struct switch_stack *switch_stack = (struct switch_stack *)regs - 1U; ++ unsigned long stack_pointer = sigsp(regs->stack_pointer, ksig); ++ struct rt_sigframe __user *frame; ++ long err = 0; ++ ++ /* ++ * Allocate storage for frame, aligning it for itself and for further C ++ * function calling (which shoulld really be the largest alignment...). ++ */ ++ stack_pointer -= sizeof(*frame); ++ stack_pointer &= -16UL; ++ stack_pointer &= -(unsigned long)__alignof__(*frame); ++ frame = (struct rt_sigframe __user *)stack_pointer; ++ ++ if (!access_ok(frame, sizeof(*frame))) ++ return -EFAULT; ++ ++ /* struct siginfo info */ ++ err |= copy_siginfo_to_user(&frame->info, &ksig->info); ++ ++ /* struct ucontext uc */ ++ err |= __put_user(0, &frame->uc.uc_flags); ++ err |= __put_user(NULL, &frame->uc.uc_link); ++ err |= __save_altstack(&frame->uc.uc_stack, regs->stack_pointer); ++ err |= __put_user(regs->stack_pointer, ++ &frame->uc.uc_mcontext.regs.stack_pointer); ++ err |= __put_user(switch_stack->tls, &frame->uc.uc_mcontext.regs.tls); ++ err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); ++ ++ /* int sig_param */ ++ err |= __put_user(ksig->sig, &frame->sig_param); ++ ++ /* info_param and uc_param convenience pointers */ ++ if (ksig->ka.sa.sa_flags & SA_SIGINFO) { ++ err |= __put_user(&frame->info, &frame->info_param); ++ err |= __put_user(&frame->uc, &frame->uc_param); ++ } else { ++ err |= __put_user(NULL, &frame->info_param); ++ err |= __put_user(NULL, &frame->uc_param); ++ } ++ ++ /* __sighandler_t sa_handler */ ++ err |= __put_user(ksig->ka.sa.sa_handler, &frame->sa_handler); ++ ++ if (err) ++ return -EFAULT; ++ ++ regs->stack_pointer = stack_pointer; ++ current_thread_info()->flags |= _TIF_DELIVER_SIGNAL; ++ ++ return 0; ++} ++ ++static void handle_signal(struct ksignal *ksig, struct pt_regs *regs) ++{ ++ sigset_t *oldset = sigmask_to_save(); ++ int ret; ++ ++ /* If we're from a syscall, cancel syscall restarting if appropriate. */ ++ if (regs->syscall_nr != -1) { ++ switch (regs->syscall_ret) { ++ case -ERESTART_RESTARTBLOCK: ++ case -ERESTARTNOHAND: ++ regs->syscall_ret = -EINTR; ++ break; ++ ++ case -ERESTARTSYS: ++ if (!(ksig->ka.sa.sa_flags & SA_RESTART)) { ++ regs->syscall_ret = -EINTR; ++ break; ++ } ++ fallthrough; ++ case -ERESTARTNOINTR: ++ default: ++ /* We will actually restart in these cases. */ ++ break; ++ } ++ } ++ ++ rseq_signal_deliver(ksig, regs); ++ ++ /* ++ * Wasm always uses rt-frames - the libc will have to figure out which ++ * signature to call the handler with depending on if SA_SIGINFO is set. ++ */ ++ ret = setup_rt_frame(ksig, oldset, regs); ++ ++ signal_setup_done(ret, ksig, 0); ++} ++ ++void arch_do_signal_or_restart(struct pt_regs *regs) ++{ ++ struct ksignal ksig; ++ ++ if (get_signal(&ksig)) { ++ handle_signal(&ksig, regs); ++ return; ++ } ++ ++ /* ++ * Restart is handled in the syscall wrapper instead, as Wasm can't ++ * fiddle with the instruction pointer to re-run the syscall. (Restart ++ * may be canceled by handle_signal() above if we're handling a signal.) ++ */ ++ ++ /* ++ * If there's no signal to deliver, we just put the saved sigmask ++ * back. ++ */ ++ restore_saved_sigmask(); ++} +diff --git a/arch/wasm/kernel/smp.c b/arch/wasm/kernel/smp.c +new file mode 100644 +index 000000000..c105e5259 +--- /dev/null ++++ b/arch/wasm/kernel/smp.c +@@ -0,0 +1,344 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++extern unsigned long long wasm_cpu_clock_get_monotonic(void); ++ ++static DECLARE_COMPLETION(cpu_running); ++ ++#if NR_IRQS > 32 ++#error "NR_IRQS too high" ++#endif ++static DEFINE_PER_CPU(unsigned int, raised_irqs); ++ ++#define TIMER_NEVER_EXPIRE (-1) ++static DEFINE_PER_CPU(long long, local_timer_expiries) = TIMER_NEVER_EXPIRE; ++ ++enum ipi_type { ++ IPI_RESCHEDULE = 0, ++ IPI_CALL_FUNC = 1, ++ IPI_RECEIVE_BROADCAST = 2, ++ IPI_IRQ_WORK = 3, ++}; ++#define IPI_MASK(ipi_type) ((unsigned int)(1U << (int)(ipi_type))) ++static DEFINE_PER_CPU(unsigned int, raised_ipis); ++ ++void smp_send_stop(void) ++{ ++ unsigned int cpu; ++ unsigned int this_cpu = smp_processor_id(); ++ ++ for_each_online_cpu(cpu) { ++ if (likely(cpu != this_cpu)) ++ wasm_stop_cpu(cpu); ++ } ++} ++ ++/* Run for each cpu except the first one, to bring the others up. */ ++int __cpu_up(unsigned int cpu, struct task_struct *idle_task) ++{ ++ /* Use 16-byte aligned stack to be able to call C functions. */ ++ unsigned long stack_start = (unsigned long)idle_task & -16; ++ ++ task_thread_info(idle_task)->cpu = cpu; ++ ++ /* Needed so that __switch_to does not create a new Wasm task. */ ++ task_thread_info(idle_task)->flags &= ~_TIF_NEVER_RUN; ++ ++ reinit_completion(&cpu_running); ++ ++ /* Will create a new Wasm instance and call start_secondary(). */ ++ wasm_start_cpu(cpu, idle_task, (unsigned long)stack_start); ++ ++ /* Wait for CPU to finish startup & mark itself online before return. */ ++ wait_for_completion(&cpu_running); ++ return 0; ++} ++ ++/* ++ * First thing to run on the secondary CPUs. ++ * ++ * Launched by __cpu_up(), which calls out to the Wasm host. The Wasm host calls ++ * _start_secondary, which sets up the __stack_pointer and then calls us. ++ */ ++__visible void start_secondary(void) ++{ ++ unsigned int cpu = smp_processor_id(); ++ ++ notify_cpu_starting(cpu); ++ set_cpu_online(cpu, true); ++ ++ enable_percpu_irq(WASM_IRQ_IPI, IRQ_TYPE_NONE); ++ ++ /* ++ * Notify boot CPU that we're up & online and it can safely return ++ * from __cpu_up(). IPIs need to be enabled (enable_percpu_irq above). ++ */ ++ complete(&cpu_running); ++ ++ wasm_clockevent_enable(); ++ ++ local_irq_enable(); ++ cpu_startup_entry(CPUHP_AP_ONLINE_IDLE); /* Enter idle. */ ++} ++ ++void __init smp_cpus_done(unsigned int max_cpus) ++{ ++ pr_info("SMP: Total of %d processors activated.\n", num_online_cpus()); ++} ++ ++__visible void raise_interrupt(int cpu, int irq_nr) ++{ ++ /* ++ * Note: this function may be called independent of the kernel, outside ++ * any CPU or taks. Do not call kernel functions in here! ++ * ++ * per_cpu_ptr() is however safe to call (unlike e.g. this_cpu_ptr()). ++ */ ++ unsigned int *raised_irqs_ptr = per_cpu_ptr(&raised_irqs, cpu); ++ ++ if (irq_nr >= NR_IRQS) ++ return; ++ ++ __atomic_or_fetch(raised_irqs_ptr, 1U << irq_nr, __ATOMIC_SEQ_CST); ++ __builtin_wasm_memory_atomic_notify(raised_irqs_ptr, 1U); ++} ++ ++static void send_ipi_message(int cpu, enum ipi_type ipi) ++{ ++ unsigned int *raised_ipis_ptr = per_cpu_ptr(&raised_ipis, cpu); ++ __atomic_or_fetch(raised_ipis_ptr, IPI_MASK(ipi), __ATOMIC_SEQ_CST); ++ ++ raise_interrupt(cpu, WASM_IRQ_IPI); ++} ++ ++void arch_send_call_function_single_ipi(int cpu) ++{ ++ /* ++ * This is not ideal, as these can only be handled when the other CPU is ++ * idle, but it allows using the kernel completion API when there is ++ * really only one task running. This is the case when the primary CPU ++ * is booting up secondaries, waiting for them to wake up and finish ++ * their part of the boot process. Ideally, we'd get rid of IPI calls. ++ */ ++ ++ preempt_disable(); ++ send_ipi_message(cpu, IPI_CALL_FUNC); ++ preempt_enable(); ++} ++ ++void arch_smp_send_reschedule(int cpu) ++{ ++ preempt_disable(); ++ send_ipi_message(cpu, IPI_RESCHEDULE); ++ preempt_enable(); ++} ++ ++extern void arch_irq_work_raise(void) ++{ ++ /* This is a special IPI sent to ourselves, to break out of context. */ ++ ++ preempt_disable(); ++ send_ipi_message(smp_processor_id(), IPI_IRQ_WORK); ++ preempt_enable(); ++} ++ ++void tick_broadcast(const struct cpumask *mask) ++{ ++ int cpu; ++ ++ preempt_disable(); ++ ++ for_each_cpu(cpu, mask) { ++ send_ipi_message(cpu, IPI_RECEIVE_BROADCAST); ++ } ++ ++ preempt_enable(); ++} ++ ++void wasm_program_timer(unsigned long delta) ++{ ++ unsigned long long now; ++ unsigned long long expiry = 0ULL; ++ ++ unsigned int *raised_irqs_ptr = this_cpu_ptr(&raised_irqs); ++ long long *expiry_ptr = this_cpu_ptr(&local_timer_expiries); ++ ++ if (delta == 0UL) { ++ /* Optimization: set expiry to 0 to immediately expire. */ ++ } else { ++ now = wasm_cpu_clock_get_monotonic(); ++ expiry = now + (unsigned long long)delta; ++ ++ /* ++ * This overflow will realistically never happen. Calling panic ++ * instead of returning a non-zero value is warranted, as the ++ * calling code would otherwise enter an infinite loop... ++ */ ++ if (expiry < now || expiry > (unsigned long long)LLONG_MAX) ++ panic("clockevent expiry too large"); ++ } ++ ++ __atomic_store_n(expiry_ptr, (long long)expiry, __ATOMIC_SEQ_CST); ++ ++ /* ++ * We notify on raised_irqs since that's what we're waiting on in the ++ * idle loop. It does not matter if it's still 0 - it will wake anyway. ++ */ ++ __builtin_wasm_memory_atomic_notify(raised_irqs_ptr, 1U); ++} ++ ++static irqreturn_t handle_IPI(int irq_nr, void *dev_id) ++{ ++ unsigned int *ipi_mask_ptr = dev_id; ++ unsigned int ipi_mask = __atomic_exchange_n(ipi_mask_ptr, 0U, ++ __ATOMIC_SEQ_CST); ++ ++ if (ipi_mask & IPI_MASK(IPI_RECEIVE_BROADCAST)) { ++ /* Useful in NO_HZ_FULL case where no task is running. */ ++ tick_receive_broadcast(); ++ } ++ ++ if (ipi_mask & IPI_MASK(IPI_CALL_FUNC)) ++ generic_smp_call_function_interrupt(); ++ ++ if (ipi_mask & IPI_MASK(IPI_RESCHEDULE)) ++ scheduler_ipi(); ++ ++ if (ipi_mask & IPI_MASK(IPI_IRQ_WORK)) ++ irq_work_run(); ++ ++ return IRQ_HANDLED; ++} ++ ++void __init setup_smp_ipi(void) ++{ ++ /* This is run on the boot cpu only. We need to enable others later. */ ++ ++ if (request_percpu_irq(WASM_IRQ_IPI, handle_IPI, "IPI", &raised_ipis)) ++ panic("Failed to register IPI IRQ"); ++ ++ enable_percpu_irq(WASM_IRQ_IPI, IRQ_TYPE_NONE); ++} ++ ++void arch_cpu_idle(void) ++{ ++ /* Note: The idle task will not migrate so per_cpu state is stable. */ ++ unsigned int *raised_irqs_ptr = this_cpu_ptr(&raised_irqs); ++ unsigned int raised_irqs; ++ long long *expiry_ptr = this_cpu_ptr(&local_timer_expiries); ++ long long expiry; ++ long long timeout; ++ unsigned long long now; ++ int irq_nr; ++ ++ /* ++ * This function is supposed to sleep until an interrupt comes in. The ++ * fact these events can only be detected from the idle task makes these ++ * "interrupts" unreliable unless there are no tasks on this CPU's ++ * runqueue at all times. Therefore, one CPU (IRQ_CPU) is dedicated to ++ * handle interrupts only, no user tasks are allowed to run on it. ++ * ++ * Additionally, the clockevent subsystem can wake us, either because it ++ * wants to program a new timer expiry (arming or re-arming the timer), ++ * or because an already armed timer is expiring. The clockevent ++ * subsystem can also request a broadcast - i.e. waking up other CPUs ++ * from a dedicated broadcast device (living on IRQ_CPU). It's important ++ * that all CPUs can handle programming of timers, since it's being used ++ * when the system boots (before NO_HZ_IDLE kicks in). Additionally, ++ * some kernel functions (e.g. schedule_timeout()) rely on timers to ++ * wake them up when no task is running on the CPU. These events and ++ * broadcasts will of course happen in a best-effort fashion on CPUs ++ * where there are tasks running, as they cannot be stopped. ++ * ++ * Wasm-specific wait primitives are used so that the Wasm VM can yield ++ * to the host OS. In a sense, it's like calling schedule(), but on the ++ * host. Callling schedule() here would just send us back, busy-waiting. ++ */ ++ for (;;) { ++ expiry = __atomic_load_n(expiry_ptr, __ATOMIC_SEQ_CST); ++ ++reprocess: ++ if (expiry > 0LL) { ++ now = wasm_cpu_clock_get_monotonic(); ++ ++ /* This will realistically never happen. */ ++ if (now > (unsigned long long)LLONG_MAX) ++ panic("time is too far into the future"); ++ ++ if ((long long)now >= expiry) ++ timeout = 0LL; ++ else ++ timeout = expiry - now; ++ } else { ++ /* ++ * Just like magic: ++ * If expiry is 0 => timeout becomes 0. ++ * If expiry is forever => timeout becomes forever. ++ */ ++ timeout = expiry; ++ } ++ ++ /* timeout == 0 iff the timer expired this iteration */ ++ if (timeout == 0LL) { ++ /* ++ * It may be tempting to raise the timer interrupt ++ * already here, but that would not comply with the ++ * clockevent API, which mandates that re-programming ++ * of the device also cancels any pending event first. ++ */ ++ ++ /* Try resetting the timer to never expire. */ ++ if (!__atomic_compare_exchange_n(expiry_ptr, &expiry, ++ TIMER_NEVER_EXPIRE, false, ++ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { ++ /* ++ * Expiry changed under our rug - re-process it. ++ * This goto is slightly faster than "continue;" ++ * as the compare-and-swap above will already ++ * have loaded the new expiry value on failure. ++ */ ++ goto reprocess; ++ } ++ ++ raise_interrupt(smp_processor_id(), WASM_IRQ_TIMER); ++ ++ if (smp_processor_id() != IRQ_CPU) ++ timeout = TIMER_NEVER_EXPIRE; ++ } ++ ++ if (timeout != 0LL) ++ __builtin_wasm_memory_atomic_wait32(raised_irqs_ptr, 0U, ++ timeout); ++ ++ raised_irqs = __atomic_exchange_n(raised_irqs_ptr, 0U, ++ __ATOMIC_SEQ_CST); ++ ++ /* ++ * In the case of some raised_irqs, handle it, then we will come ++ * back here in a future invocation of this function. This ++ * function retuns so that that idle framework can do its job, ++ * for example if TIF_NEEDS_RESCHED is set by some IPI. ++ */ ++ if (raised_irqs) ++ break; ++ } ++ ++ irq_nr = 0; ++ while (raised_irqs) { ++ if (raised_irqs & 1U) ++ do_irq_stacked(irq_nr); ++ ++ raised_irqs >>= 1; ++ ++irq_nr; ++ } ++} +diff --git a/arch/wasm/kernel/stack.c b/arch/wasm/kernel/stack.c +new file mode 100644 +index 000000000..a0f1c314b +--- /dev/null ++++ b/arch/wasm/kernel/stack.c +@@ -0,0 +1,26 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct task_struct *alloc_task_struct_node(int node) ++{ ++ struct page *page = ++ alloc_pages_node(node, THREADINFO_GFP, THREAD_SIZE_ORDER); ++ ++ if (unlikely(!page)) ++ return NULL; ++ ++ return (struct task_struct *)((unsigned long)page_address(page) + ++ ALIGN_DOWN(THREAD_SIZE - sizeof(struct task_struct), ++ L1_CACHE_BYTES)); ++} ++ ++void free_task_struct(struct task_struct *tsk) ++{ ++ free_pages((unsigned long)tsk & THREAD_MASK, THREAD_SIZE_ORDER); ++} +diff --git a/arch/wasm/kernel/sys_wasm.c b/arch/wasm/kernel/sys_wasm.c +new file mode 100644 +index 000000000..c3b2404e0 +--- /dev/null ++++ b/arch/wasm/kernel/sys_wasm.c +@@ -0,0 +1,19 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++ ++// SYS_mmap2() ++SYSCALL_DEFINE6(mmap2, unsigned long, addr, unsigned long, len, ++ unsigned long, prot, unsigned long, flags, unsigned long, fd, ++ unsigned long, pgoff) ++{ ++ /* ++ * The "page size" for mmap2 should always be 4K (shift 12). Some ++ * architectures use their native page size or whatnot, and that's why ++ * this syscall exists in its own version for each architecture... ++ * ++ * Some architectures check the alignment, but that's out of spec. ++ */ ++ return ksys_mmap_pgoff(addr, len, prot, flags, fd, ++ pgoff >> (PAGE_SHIFT - 12)); ++} +diff --git a/arch/wasm/kernel/syscall_table.c b/arch/wasm/kernel/syscall_table.c +new file mode 100644 +index 000000000..3c4e9b5e6 +--- /dev/null ++++ b/arch/wasm/kernel/syscall_table.c +@@ -0,0 +1,37 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++#include ++#include ++ ++/* ++ * We should probably use some soft variant of CONFIG_COMPAT yet to be invented. ++ * TODO: This hack should be replaced with proper selection of compat syscalls! ++ */ ++ ++ static long sys_truncate64_fixup(const char __user *pathname, ++ unsigned long length_lo, ++ unsigned long length_hi) ++{ ++ return sys_truncate64(pathname, ++ ((unsigned long long)length_hi << 32) | length_lo); ++} ++ ++static long sys_ftruncate64_fixup(unsigned int fd, ++ unsigned long length_lo, ++ unsigned long length_hi) ++{ ++ return sys_ftruncate64(fd, ++ ((unsigned long long)length_hi << 32) | length_lo); ++} ++ ++void (* const sys_call_table[__NR_syscalls])(void) = { ++ [0 ... __NR_syscalls-1] = (void (*)(void))sys_ni_syscall, ++ ++#undef __SYSCALL ++#define __SYSCALL(nr, call) [nr] = (void (*)(void))(call), ++#include ++ ++ [__NR_truncate64] = (void (*)(void))sys_truncate64_fixup, ++ [__NR_ftruncate64] = (void (*)(void))sys_ftruncate64_fixup, ++}; +diff --git a/arch/wasm/kernel/time.c b/arch/wasm/kernel/time.c +new file mode 100644 +index 000000000..af65bc3f0 +--- /dev/null ++++ b/arch/wasm/kernel/time.c +@@ -0,0 +1,88 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++extern unsigned long long wasm_cpu_clock_get_monotonic(void); ++ ++/* Wasm clock source: derived from Wasm host cpu clock (monotonic). */ ++ ++static unsigned long long wasm_clocksource_read(struct clocksource *cs) ++{ ++ return wasm_cpu_clock_get_monotonic(); ++} ++ ++static struct clocksource wasm_clocksource = { ++ .name = "wasm_cpu_clock", ++ .flags = CLOCK_SOURCE_IS_CONTINUOUS, ++ .rating = 200, ++ .read = wasm_clocksource_read, ++ .mask = CLOCKSOURCE_MASK(64), ++}; ++ ++static int __init wasm_clocksource_init(void) ++{ ++ return clocksource_register_khz(&wasm_clocksource, 1000000U /* 1 ns */); ++} ++ ++/* Wasm clock event: derived from Wasm atomic wait timeouts (in smp.c). */ ++ ++static int wasm_clockevent_set_next_event(unsigned long delta, ++ struct clock_event_device *dev) ++{ ++ wasm_program_timer(delta); ++ return 0; ++} ++ ++static DEFINE_PER_CPU(struct clock_event_device, wasm_clockevents) = { ++ .name = "wasm_timer", ++ .features = CLOCK_EVT_FEAT_ONESHOT, ++ .rating = 300, ++ .set_next_event = wasm_clockevent_set_next_event, ++}; ++ ++static irqreturn_t __irq_entry wasm_timer_interrupt(int irq_nr, void *dev_id) ++{ ++ struct clock_event_device *ce_dev = dev_id; ++ ++ ce_dev->event_handler(ce_dev); ++ ++ return IRQ_HANDLED; ++} ++ ++static int __init wasm_clockevent_init(void) ++{ ++ /* Requested here, enabled in wasm_clockevent_enable() for each cpu. */ ++ return request_percpu_irq(WASM_IRQ_TIMER, wasm_timer_interrupt, ++ "wasm-timer", &wasm_clockevents); ++} ++ ++void wasm_clockevent_enable(void) ++{ ++ struct clock_event_device *ce_dev = this_cpu_ptr(&wasm_clockevents); ++ ++ ce_dev->cpumask = cpumask_of(smp_processor_id()); ++ ce_dev->irq = WASM_IRQ_TIMER; ++ clockevents_config_and_register(ce_dev, 1000000000, 0, ~0U); ++ ++ enable_percpu_irq(WASM_IRQ_TIMER, IRQ_TYPE_NONE); ++} ++ ++/* Called very early in the boot, only CPU 0 is up so far! */ ++void __init time_init(void) ++{ ++ /* Time is an illusion and yet here we are... */ ++ if (wasm_clocksource_init()) ++ panic("Failed to initialize Wasm clocksource"); ++ ++ if (wasm_clockevent_init()) ++ panic("Failed to initialize Wasm clock_event"); ++ ++ /* Only for CPU 0, secondaries will be enabled as they come up. */ ++ wasm_clockevent_enable(); ++} +diff --git a/arch/wasm/kernel/traps.c b/arch/wasm/kernel/traps.c +new file mode 100644 +index 000000000..928a2338a +--- /dev/null ++++ b/arch/wasm/kernel/traps.c +@@ -0,0 +1,207 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static inline void exception_enter(struct pt_regs *regs) ++{ ++ unsigned long *cpuflags = this_cpu_ptr(&wasm_cpuflags); ++ ++ regs->cpuflags = *cpuflags; ++ *cpuflags &= ~(BIT(CPUFLAGS_USER_MODE) | BIT(CPUFLAGS_INTERRUPT)); ++} ++ ++static inline void exception_exit(struct pt_regs *regs) ++{ ++ unsigned long *cpuflags = this_cpu_ptr(&wasm_cpuflags); ++ ++ *cpuflags = regs->cpuflags; ++} ++ ++#define WASM_SYSCALL_N(x, args, cast_args, ...) \ ++ __visible long __wasm_syscall_##x args \ ++ { \ ++ long syscall = n; \ ++ struct pt_regs *regs = current_pt_regs(); \ ++ void (*fn)(void); \ ++ long syscall_args[] = {__MAP(x,__SC_ARGS,__VA_ARGS__)}; \ ++ bool restart; \ ++ \ ++ exception_enter(regs); \ ++ \ ++ regs->syscall_nr = n; \ ++ memcpy(regs->syscall_args, syscall_args, sizeof(syscall_args)); \ ++ regs->syscall_ret = -ENOSYS; \ ++ \ ++ if (user_mode(regs)) { \ ++ do { \ ++ syscall = syscall_enter_from_user_mode( \ ++ regs, syscall); \ ++ \ ++ if (syscall >= 0 && syscall < __NR_syscalls) { \ ++ fn = sys_call_table[syscall]; \ ++ if (syscall == __NR_restart_syscall) { \ ++ regs->syscall_ret = sys_restart_syscall(); \ ++ } else if (fn != (void (*)(void))sys_ni_syscall) { \ ++ regs->syscall_ret = ((long (*)(cast_args)) \ ++ fn)(__MAP(x,__SC_ARGS,__VA_ARGS__)); \ ++ } \ ++ } \ ++ \ ++ syscall_exit_to_user_mode(regs); \ ++ \ ++ switch (regs->syscall_ret) { \ ++ case -ERESTART_RESTARTBLOCK: \ ++ syscall = __NR_restart_syscall; \ ++ fallthrough; \ ++ case -ERESTARTNOHAND: \ ++ case -ERESTARTSYS: \ ++ case -ERESTARTNOINTR: \ ++ restart = true; \ ++ break; \ ++ default: \ ++ restart = false; \ ++ } \ ++ } while (restart); \ ++ } else { \ ++ irqentry_state_t state = irqentry_nmi_enter(regs); \ ++ \ ++ panic("Syscall called when in kernel mode"); \ ++ \ ++ irqentry_nmi_exit(regs, state); \ ++ } \ ++ \ ++ exception_exit(regs); \ ++ \ ++ return regs->syscall_ret; \ ++ } ++#define WASM_SYSCALL(x, ...) WASM_SYSCALL_N( \ ++ x, \ ++ (long n, __MAP(x,__SC_DECL,__VA_ARGS__)), \ ++ __MAP(x,__SC_DECL,__VA_ARGS__), \ ++ __VA_ARGS__) ++ ++WASM_SYSCALL_N(0, (long n), void) ++WASM_SYSCALL(1, long, a) ++WASM_SYSCALL(2, long, a, long, b) ++WASM_SYSCALL(3, long, a, long, b, long, c) ++WASM_SYSCALL(4, long, a, long, b, long, c, long, d) ++WASM_SYSCALL(5, long, a, long, b, long, c, long, d, long, e) ++WASM_SYSCALL(6, long, a, long, b, long, c, long, d, long, e, long, f) ++ ++/* ++ * Final check before syscall return (after pt_regs have been restored). ++ * ++ * If exec() was called, we reload user program code. If there is a signal ++ * handler to call, we call it. (Both will not happen, as exec blocks handlers.) ++ * ++ * Returns the direction of program flow: ++ * -1 if exec() was called and the Wasm host should reload the user program. ++ * 1 if a signal was delivered => the Wasm host should start signal handling. ++ * 2 if a sigreturn happened => the Wasm host should cancel signal handling. ++ * 3 if a signal was delivered AND a sigreturn (of an older signal handler) ++ * happened => the Wasm host should first handle the new signal (stacked), ++ * then cancel the old signal handler (after the stacked signal returns). ++ * 0 if nothing should be done and the syscall should return normally. ++ * In the case of exec(), the syscall should never fully return to the caller. ++ */ ++int user_mode_tail(void) ++{ ++ struct thread_info *thread_info = current_thread_info(); ++ const bool reload = thread_info->flags & _TIF_RELOAD_PROGRAM; ++ const bool deliver = thread_info->flags & _TIF_DELIVER_SIGNAL; ++ const bool retn = thread_info->flags & _TIF_RETURN_SIGNAL; ++ ++ if (reload) { ++ BUG_ON(deliver); ++ BUG_ON(retn); ++ ++ thread_info->flags &= ~_TIF_RELOAD_PROGRAM; ++ return -1; ++ } else if (deliver || retn) { ++ BUG_ON(reload); ++ ++ if (deliver) ++ thread_info->flags &= ~_TIF_DELIVER_SIGNAL; ++ ++ if (retn) ++ thread_info->flags &= ~_TIF_RETURN_SIGNAL; ++ ++ return (deliver ? 1 : 0) | (retn ? 2 : 0); ++ } ++ ++ return 0; ++} ++ ++static void do_irq(struct pt_regs *regs, int irq_nr) ++{ ++ struct pt_regs *old_regs; ++ irqentry_state_t state = irqentry_enter(regs); ++ ++ irq_enter_rcu(); ++ old_regs = set_irq_regs(regs); ++ generic_handle_irq(irq_nr); ++ set_irq_regs(old_regs); ++ irq_exit_rcu(); ++ ++ irqentry_exit(regs, state); ++} ++ ++void do_irq_stacked(int irq_nr) ++{ ++ /* ++ * This is a bit odd but somewhere in this function's frame we start an ++ * exception frame. Exactly where the boundary is does not matter in ++ * practice, some data may end up on either "wrong" end of the boundary. ++ */ ++ struct pt_regs regs = PT_REGS_INIT; ++ regs.stack_pointer = (unsigned long)®s + sizeof(regs); ++ exception_enter(®s); ++ ++ do_irq(®s, irq_nr); ++ ++ exception_exit(®s); ++} ++ ++/* Do an exception. There are currently no exception types in Wasm. */ ++static void do_exception(struct pt_regs *regs) ++{ ++ /* ++ * The host is currently responsible for reporting the full error. We ++ * just mark this error as SIGILL but it could be anything. ++ */ ++ if (user_mode(regs)) { ++ irqentry_enter_from_user_mode(regs); ++ force_sig_fault(SIGILL, ILL_ILLOPC, (void __user *)0U); ++ irqentry_exit_to_user_mode(regs); ++ } else { ++ irqentry_state_t state = irqentry_nmi_enter(regs); ++ make_task_dead(SIGILL); ++ irqentry_nmi_exit(regs, state); ++ } ++} ++ ++/* ++* This function is called from the host when things break either in kernel code ++* or user code. That code will never continue to execute - we have to report the ++* error and try to recover in the best way possible. ++*/ ++__visible void raise_exception(void) ++{ ++ /* ++ * This is a bit odd but somewhere in this function's frame we start an ++ * exception frame. Exactly where the boundary is does not matter in ++ * practice, some data may end up on either "wrong" end of the boundary. ++ */ ++ struct pt_regs regs = PT_REGS_INIT; ++ regs.stack_pointer = (unsigned long)®s + sizeof(regs); ++ exception_enter(®s); ++ ++ do_exception(®s); ++ ++ exception_exit(®s); ++} +diff --git a/arch/wasm/kernel/vmlinux.lds.S b/arch/wasm/kernel/vmlinux.lds.S +new file mode 100644 +index 000000000..1ae0641dc +--- /dev/null ++++ b/arch/wasm/kernel/vmlinux.lds.S +@@ -0,0 +1,65 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++ ++/* Put init_task after init_stack in the thread stack. */ ++#define INIT_TASK_OFFSET THREAD_TASK_STRUCT_OFFSET ++ ++#include ++#include ++#include ++#include ++ ++SECTIONS ++{ ++ /* To refer to addres 0 in assembly, but as a relocation. */ ++ zeroptr = 0; ++ ++ /* Begin 1 Wasm page (65k) in so that we can dodge null-pointer. */ ++ . = 0x10000; ++ ++ __init_begin = .; ++ HEAD_TEXT_SECTION ++ INIT_TEXT_SECTION(PAGE_SIZE) ++ INIT_DATA_SECTION(16) ++ PERCPU_SECTION(L1_CACHE_BYTES) ++ __init_end = .; ++ ++ .text : { ++ _text = .; ++ _stext = .; ++ TEXT_TEXT ++ SCHED_TEXT ++ LOCK_TEXT ++ KPROBES_TEXT ++ ENTRY_TEXT ++ IRQENTRY_TEXT ++ SOFTIRQENTRY_TEXT ++ _etext = .; ++ } ++ ++ _sdata = .; ++ RO_DATA(PAGE_SIZE) ++ RW_DATA(L1_CACHE_BYTES, PAGE_SIZE, THREAD_SIZE) ++ _edata = .; ++ ++ EXCEPTION_TABLE(16) ++ ++ BSS_SECTION(0, 0, 0) ++ _end = .; ++ ++/* ++ Not supported by wasm-ld linker script hack: ++ STABS_DEBUG ++ DWARF_DEBUG ++ ELF_DETAILS ++*/ ++ ++ DISCARDS // must be the last ++} ++ ++/* ++ * Due to the way linker scripts are implemented in wasm-ld, any symbol-alias ++ * assignments have to happen after the symbol has been placed into the output. ++ */ ++jiffies = jiffies_64; +diff --git a/arch/wasm/lib/Makefile b/arch/wasm/lib/Makefile +new file mode 100644 +index 000000000..8e4e35012 +--- /dev/null ++++ b/arch/wasm/lib/Makefile +@@ -0,0 +1,3 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++ ++lib-y += delay.o +diff --git a/arch/wasm/lib/delay.c b/arch/wasm/lib/delay.c +new file mode 100644 +index 000000000..4db76b463 +--- /dev/null ++++ b/arch/wasm/lib/delay.c +@@ -0,0 +1,19 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++ ++void calibrate_delay(void) ++{ ++ /* Wasm convention: lpj = nanoseconds per Hz */ ++ loops_per_jiffy = 1000000000 / HZ; ++} ++ ++void __delay(unsigned long cycles) ++{ ++ unsigned int dummy = 0U; ++ ++ mb(); ++ __builtin_wasm_memory_atomic_wait32(&dummy, 0U, (long long)cycles); ++ mb(); ++} ++EXPORT_SYMBOL(__delay); +diff --git a/arch/wasm/mm/Makefile b/arch/wasm/mm/Makefile +new file mode 100644 +index 000000000..661744a43 +--- /dev/null ++++ b/arch/wasm/mm/Makefile +@@ -0,0 +1,3 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++ ++obj-y += init.o +diff --git a/arch/wasm/mm/init.c b/arch/wasm/mm/init.c +new file mode 100644 +index 000000000..5469d62e5 +--- /dev/null ++++ b/arch/wasm/mm/init.c +@@ -0,0 +1,21 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include ++#include ++#include ++#include ++ ++unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)] __page_aligned_bss; ++EXPORT_SYMBOL(empty_zero_page); ++ ++void __init mem_init(void) ++{ ++ /* These are needed by some code to know which pages are valid. */ ++ high_memory = (void *)memory_end; ++ max_pfn = PFN_DOWN(memory_end); ++ min_low_pfn = PFN_DOWN(memory_start); ++ max_low_pfn = max_pfn; ++ set_max_mapnr(max_low_pfn - min_low_pfn); ++ ++ memblock_free_all(); ++} +diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h +index b331a3947..86d5a3ee4 100644 +--- a/include/asm-generic/vmlinux.lds.h ++++ b/include/asm-generic/vmlinux.lds.h +@@ -404,11 +404,15 @@ + . = ALIGN(align); \ + *(.data..cacheline_aligned) + ++#ifndef INIT_TASK_OFFSET ++#define INIT_TASK_OFFSET (0) ++#endif + #define INIT_TASK_DATA(align) \ + . = ALIGN(align); \ + __start_init_task = .; \ + init_thread_union = .; \ + init_stack = .; \ ++ . = . + INIT_TASK_OFFSET; \ + KEEP(*(.data..init_task)) \ + KEEP(*(.data..init_thread_info)) \ + . = __start_init_task + THREAD_SIZE; \ +diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h +index d676ed2b2..f0aee0f22 100644 +--- a/include/uapi/linux/audit.h ++++ b/include/uapi/linux/audit.h +@@ -437,6 +437,7 @@ enum { + #define AUDIT_ARCH_TILEGX32 (EM_TILEGX|__AUDIT_ARCH_LE) + #define AUDIT_ARCH_TILEPRO (EM_TILEPRO|__AUDIT_ARCH_LE) + #define AUDIT_ARCH_UNICORE (EM_UNICORE|__AUDIT_ARCH_LE) ++#define AUDIT_ARCH_WASM32 (EM_WASM32|__AUDIT_ARCH_LE) + #define AUDIT_ARCH_X86_64 (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) + #define AUDIT_ARCH_XTENSA (EM_XTENSA) + #define AUDIT_ARCH_LOONGARCH32 (EM_LOONGARCH|__AUDIT_ARCH_LE) +diff --git a/include/uapi/linux/elf-em.h b/include/uapi/linux/elf-em.h +index ef38c2bc5..aaca659a7 100644 +--- a/include/uapi/linux/elf-em.h ++++ b/include/uapi/linux/elf-em.h +@@ -52,6 +52,7 @@ + #define EM_BPF 247 /* Linux BPF - in-kernel virtual machine */ + #define EM_CSKY 252 /* C-SKY */ + #define EM_LOONGARCH 258 /* LoongArch */ ++#define EM_WASM32 264 /* WebAssembly wasm32 */ + #define EM_FRV 0x5441 /* Fujitsu FR-V */ + + /* +diff --git a/scripts/Makefile.clang b/scripts/Makefile.clang +index 058a4c0f8..4253c0177 100644 +--- a/scripts/Makefile.clang ++++ b/scripts/Makefile.clang +@@ -9,6 +9,7 @@ CLANG_TARGET_FLAGS_mips := mipsel-linux-gnu + CLANG_TARGET_FLAGS_powerpc := powerpc64le-linux-gnu + CLANG_TARGET_FLAGS_riscv := riscv64-linux-gnu + CLANG_TARGET_FLAGS_s390 := s390x-linux-gnu ++CLANG_TARGET_FLAGS_wasm := wasm32-unknown-unknown + CLANG_TARGET_FLAGS_x86 := x86_64-linux-gnu + CLANG_TARGET_FLAGS_um := $(CLANG_TARGET_FLAGS_$(SUBARCH)) + CLANG_TARGET_FLAGS := $(CLANG_TARGET_FLAGS_$(SRCARCH)) +diff --git a/scripts/Makefile.vmlinux_o b/scripts/Makefile.vmlinux_o +index 0edfdb403..9fdae5e6d 100644 +--- a/scripts/Makefile.vmlinux_o ++++ b/scripts/Makefile.vmlinux_o +@@ -44,12 +44,20 @@ objtool-args = $(vmlinux-objtool-args-y) --link + # Link of vmlinux.o used for section mismatch analysis + # --------------------------------------------------------------------------- + ++ifneq ($(ARCH),wasm) ++ circular-resolved-libs = -start-group $(KBUILD_VMLINUX_LIBS) --end-group ++else ++ # LLVM wasm-ld does not support --start-group and --end-group. This is ++ # not as good as grouping them, but it might just work! ++ circular-resolved-libs = $(KBUILD_VMLINUX_LIBS) $(KBUILD_VMLINUX_LIBS) ++endif ++ + quiet_cmd_ld_vmlinux.o = LD $@ + cmd_ld_vmlinux.o = \ + $(LD) ${KBUILD_LDFLAGS} -r -o $@ \ + $(addprefix -T , $(initcalls-lds)) \ + --whole-archive vmlinux.a --no-whole-archive \ +- --start-group $(KBUILD_VMLINUX_LIBS) --end-group \ ++ $(circular-resolved-libs) \ + $(cmd_objtool) + + define rule_ld_vmlinux.o +diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh +index a432b171b..023eed789 100755 +--- a/scripts/link-vmlinux.sh ++++ b/scripts/link-vmlinux.sh +@@ -66,7 +66,15 @@ vmlinux_link() + libs= + else + objs=vmlinux.a +- libs="${KBUILD_VMLINUX_LIBS}" ++ ++ if [ "${ARCH}" = "wasm" ]; then ++ # LLVM wasm-ld does not support --start-group and ++ # --end-group. This is not as good as grouping them, but ++ # it might just work! ++ libs="${KBUILD_VMLINUX_LIBS} ${KBUILD_VMLINUX_LIBS}" ++ else ++ libs="${wl}--start-group ${KBUILD_VMLINUX_LIBS} ${wl}--end-group" ++ fi + fi + + if is_enabled CONFIG_MODULES; then +@@ -87,6 +95,16 @@ vmlinux_link() + ldlibs= + fi + ++ # wasm-ld has very simple linker scripts and needs some extra setup. ++ if [ "${ARCH}" = "wasm" ]; then ++ ldflags="${ldflags} --no-entry --error-limit=0" ++ ldflags="${ldflags} --export-all --export-table" ++ ldflags="${ldflags} --no-merge-data-segments -no-gc-sections" ++ ldflags="${ldflags} --import-memory --shared-memory" ++ ldflags="${ldflags} --max-memory=$((1<<32))" ++ ldflags="${ldflags} --import-undefined" ++ fi ++ + ldflags="${ldflags} ${wl}--script=${objtree}/${KBUILD_LDS}" + + # The kallsyms linking does not need debug symbols included. +@@ -100,8 +118,7 @@ vmlinux_link() + + ${ld} ${ldflags} -o ${output} \ + ${wl}--whole-archive ${objs} ${wl}--no-whole-archive \ +- ${wl}--start-group ${libs} ${wl}--end-group \ +- $@ ${ldlibs} ++ ${libs} $@ ${ldlibs} + } + + # generate .BTF typeinfo from DWARF debuginfo +-- +2.25.1 + diff --git a/patches/kernel/0006-Add-Wasm-binfmt.patch b/patches/kernel/0006-Add-Wasm-binfmt.patch new file mode 100644 index 0000000..a218d93 --- /dev/null +++ b/patches/kernel/0006-Add-Wasm-binfmt.patch @@ -0,0 +1,517 @@ +From 66c917eaad32544aaf992bffa61f1dac0ad37746 Mon Sep 17 00:00:00 2001 +From: Joel Severin +Date: Sun, 12 May 2024 17:15:17 +0200 +Subject: [PATCH] Add Wasm binfmt + +While ELF is used for basically every other Linux-supported +architecture, current Wasm toolchains produce binaries in the .wasm +format. The .wasm file format is also the format all major Wasm +runtimes/VMs (e.g. browsers) consumes. +--- + arch/wasm/Kconfig | 2 + + fs/Kconfig.binfmt | 9 + + fs/Makefile | 1 + + fs/binfmt_wasm.c | 446 ++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 458 insertions(+) + create mode 100644 fs/binfmt_wasm.c + +diff --git a/arch/wasm/Kconfig b/arch/wasm/Kconfig +index f6e566f50..744e8c676 100644 +--- a/arch/wasm/Kconfig ++++ b/arch/wasm/Kconfig +@@ -40,6 +40,8 @@ config WASM + select ARCH_SUPPORTS_LTO_CLANG + select ARCH_SUPPORTS_LTO_CLANG_THIN + ++ select ARCH_HAS_BINFMT_WASM ++ + # TODO: Very inefficient, replace with native stuff. Our atomic impl. + # of xchg and cmpxchg already supports 64-bit integers, we could use it. + select GENERIC_ATOMIC64 +diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt +index 93539aac0..cbddea6a0 100644 +--- a/fs/Kconfig.binfmt ++++ b/fs/Kconfig.binfmt +@@ -142,6 +142,15 @@ config BINFMT_ZFLAT + help + Support FLAT format compressed binaries + ++config ARCH_HAS_BINFMT_WASM ++ bool ++ ++config BINFMT_WASM ++ bool "Kernel support for Wasm binaries" ++ depends on ARCH_HAS_BINFMT_WASM ++ help ++ Support WebAssembly format binaries. ++ + config BINFMT_MISC + tristate "Kernel support for MISC binaries" + help +diff --git a/fs/Makefile b/fs/Makefile +index 5bfdbf0d7..ab4581f7d 100644 +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -44,6 +44,7 @@ obj-$(CONFIG_BINFMT_ELF) += binfmt_elf.o + obj-$(CONFIG_COMPAT_BINFMT_ELF) += compat_binfmt_elf.o + obj-$(CONFIG_BINFMT_ELF_FDPIC) += binfmt_elf_fdpic.o + obj-$(CONFIG_BINFMT_FLAT) += binfmt_flat.o ++obj-$(CONFIG_BINFMT_WASM) += binfmt_wasm.o + + obj-$(CONFIG_FS_MBCACHE) += mbcache.o + obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o +diff --git a/fs/binfmt_wasm.c b/fs/binfmt_wasm.c +new file mode 100644 +index 000000000..51f268246 +--- /dev/null ++++ b/fs/binfmt_wasm.c +@@ -0,0 +1,446 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* Somewhat based on binfmt_flat.c and binfmt_elf_fdpic.c */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define WASM_STACK_SIZE (2UL * PAGE_SIZE) ++ ++/* ++ * Userland expects the stack to be page aligned as of now, which allows it to ++ * find the initial stack pointer by rounding up the current stack pointer to ++ * the next page in the _start function. This allows _start to be written in C. ++ * If this restriction can be lifted we could instead use something like this: ++ * max_t(unsigned long, sizeof(void *), ARCH_SLAB_MINALIGN) ++ */ ++#define WASM_STACK_ALIGN PAGE_SIZE ++ ++#define WASM_DYLINK_MEMINFO (0x01) ++ ++/* ++ * Parse the env- and arg-strings in new user memory and create the pointer ++ * tables from them, and put their addresses on the "stack", recording the new ++ * stack pointer value. ++ */ ++static int create_wasm_tables(struct linux_binprm *bprm, unsigned long arg_start) ++{ ++ char __user *p; ++ unsigned long __user *sp; ++ long i, len; ++ const struct cred *cred = current_cred(); ++ ++ // We emulate common ELF auxillary vectors to help userland out a bit. ++ const u32 wasm_auxv[] = { ++ AT_NOTELF, 1U, ++ AT_PAGESZ, PAGE_SIZE, ++ AT_UID, from_kuid_munged(cred->user_ns, cred->uid), ++ AT_EUID, from_kuid_munged(cred->user_ns, cred->euid), ++ AT_GID, from_kgid_munged(cred->user_ns, cred->gid), ++ AT_EGID, from_kgid_munged(cred->user_ns, cred->gid), ++ AT_SECURE, bprm->secureexec, ++ AT_NULL, 0U /* end */ ++ }; ++ ++ p = (char __user *)arg_start; ++ sp = (unsigned long __user *)current->mm->start_stack; ++ ++ sp -= (sizeof(wasm_auxv) + (sizeof(unsigned long) - 1U)) / ++ sizeof(unsigned long); ++ sp -= bprm->envc + 1; ++ sp -= bprm->argc + 1; ++ sp -= 1; /* &argc */ ++ ++ current->mm->start_stack = (unsigned long)sp & -WASM_STACK_ALIGN; ++ sp = (unsigned long __user *)current->mm->start_stack; ++ ++ if (put_user(bprm->argc, sp++)) ++ return -EFAULT; ++ ++ current->mm->arg_start = (unsigned long)p; ++ for (i = bprm->argc; i > 0; i--) { ++ if (put_user((unsigned long)p, sp++)) ++ return -EFAULT; ++ len = strnlen_user(p, MAX_ARG_STRLEN); ++ if (!len || len > MAX_ARG_STRLEN) ++ return -EINVAL; ++ p += len; ++ } ++ if (put_user(0, sp++)) ++ return -EFAULT; ++ current->mm->arg_end = (unsigned long)p; ++ ++ current->mm->env_start = (unsigned long)p; ++ for (i = bprm->envc; i > 0; i--) { ++ if (put_user((unsigned long)p, sp++)) ++ return -EFAULT; ++ len = strnlen_user(p, MAX_ARG_STRLEN); ++ if (!len || len > MAX_ARG_STRLEN) ++ return -EINVAL; ++ p += len; ++ } ++ if (put_user(0, sp++)) ++ return -EFAULT; ++ current->mm->env_end = (unsigned long)p; ++ ++ memcpy(sp, wasm_auxv, sizeof(wasm_auxv)); ++ ++ return 0; ++} ++ ++/* ++ * Read unsigned LEB128 encoded value, encoding a maximum of 32 bits, limited to ++ * a certain length. The input can be anywhere from 0 to 5 bytes in length, ++ * unless limited by the count artgument. A count of 5 should normally be used. ++ */ ++static bool ++wasm_consume_varU32(char **bufp, unsigned int *output, unsigned long count) ++{ ++ unsigned int result = 0; ++ char* buf = *bufp; ++ char* end = buf + count; ++ unsigned char chunk; ++ int shift = 0; ++ ++ while (buf != end) { ++ chunk = *(buf++); ++ ++ result |= (chunk & 0x7F) << shift; ++ shift += 7; ++ ++ if (!(chunk & 0x80)) ++ break; ++ } ++ ++ *output = result; ++ *bufp = buf; ++ ++ /* ++ * Return false to signal if the "continue bit" was set on the last ++ * byte, indicating faulty input data, or premature exit if count < 5. ++ */ ++ return !(chunk & 0x80); ++} ++ ++/* ++ * User data version of wasm_consume_varU32. ++ */ ++static bool wasm_consume_varU32_user( ++ unsigned long *bufp, unsigned int *output, unsigned long count) ++{ ++ unsigned int result = 0; ++ unsigned long buf = *bufp; ++ unsigned long end = buf + count; ++ unsigned char chunk; ++ int shift = 0; ++ ++ while (buf != end) { ++ if (get_user(chunk, (unsigned char __user *)(buf++))) ++ return false; ++ ++ result |= (chunk & 0x7F) << shift; ++ shift += 7; ++ ++ if (!(chunk & 0x80)) ++ break; ++ } ++ ++ *output = result; ++ *bufp = buf; ++ ++ /* ++ * Return false to signal if the "continue bit" was set on the last ++ * byte, indicating faulty input data, or premature exit if count < 5. ++ */ ++ return !(chunk & 0x80); ++} ++ ++static int load_wasm_file(struct linux_binprm *bprm, unsigned long extra_stack) ++{ ++ unsigned long data_start = 0; /* Will contain data and bss */ ++ unsigned long stack_size; ++ unsigned long whole_start, whole_p, whole_size, whole_end; ++ loff_t whole_size_ll; ++ char *parsed = bprm->buf; ++ int ret; ++ ++ /* Related to Wasm dylink.0 parsing: */ ++ unsigned int dylink_0_length; ++ unsigned long count; ++ u8 subsection_id; ++ unsigned int subsection_length; ++ unsigned long subsection_end; ++ ++ /* Related to WASM_DYLINK_MEMINFO parsing: */ ++ bool has_meminfo = false; ++ unsigned int data_size; /* memorysize */ ++ unsigned int data_align; /* memoryalignment unpacked */ ++ unsigned int table_size; /* tablesize */ ++ unsigned int table_align; /* tablealign unpacked */ ++ ++ if (memcmp(parsed, "\x00" "asm", 4UL)) { /* Wasm binary magic header */ ++ return -ENOEXEC; ++ } ++ parsed += 4UL; ++ ++ /* We only know version 1 of the format. */ ++ if (memcmp(parsed, "\x01\x00\x00\x00", 4UL)) { /* Version 0x1 (MVP) */ ++ return -ENOEXEC; ++ } ++ parsed += 4UL; ++ ++ /* ++ * We can only allow position independent code since Wasm has no MMU. ++ * This is currently flagged by a "dylink.0" custom section (first type ++ * byte 0), and should come as the first section in the file. If not, we ++ * can't run this file. However, we could allow some other magic binfmt ++ * to handle this (e.g. emulate support), so don't hard fail. ++ */ ++ if (*(parsed++) != 0x00 ++ || !wasm_consume_varU32(&parsed, &dylink_0_length, 5UL) ++ || dylink_0_length < 9U ++ || memcmp(parsed, "\x08" "dylink.0", 9UL)) { ++ return -ENOEXEC; ++ } ++ parsed += 9UL; ++ ++ /* ++ * Map the whole file into memory so we can read it and hand it off to ++ * the host. We will unmap this as soon as the host has made its copy ++ * (the host would not be able to use a shared buffer as source anyway). ++ */ ++ whole_size_ll = i_size_read(file_inode(bprm->file)); ++ if (whole_size_ll > (loff_t)ULONG_MAX) ++ return -ENOMEM; ++ ++ whole_size = (unsigned long)whole_size_ll; ++ if (whole_size < (unsigned long)(parsed - bprm->buf)) ++ return -ENOEXEC; ++ ++ /* ++ * This would be a placed to check RLIMITs, but since Wasm can allocate ++ * as much memory it wants on its own stack that makes little sense. ++ */ ++ ++ ret = begin_new_exec(bprm); ++ if (ret) ++ return ret; ++ ++ set_personality(PER_LINUX_32BIT); ++ setup_new_exec(bprm); ++ ++ whole_start = vm_mmap(bprm->file, 0, whole_size, ++ PROT_READ | PROT_EXEC, MAP_PRIVATE, 0); ++ if (!whole_start || IS_ERR_VALUE(whole_start)) { ++ ret = whole_start ? (int)whole_start : -ENOMEM; ++ pr_err("Unable to mmap process binary, errno: %d\n", ret); ++ return ret; ++ } ++ whole_end = whole_start + whole_size; ++ ++ /* Move parsed to the whole file, since bprm->buf is cut off. */ ++ whole_p = whole_start + ++ ((unsigned long)parsed - (unsigned long)bprm->buf); ++ ++ /* Time to read some subsections of the dylink.0 section! */ ++ while (!has_meminfo) { ++ if (whole_p == whole_end) { ++ pr_err("No dylink.0 subsection id"); ++ ret = -ENOEXEC; ++ goto out_unmap; ++ } else if (get_user(subsection_id, (u8 __user *)(whole_p++))) { ++ pr_err("Failed to read dylink.0 subsection id"); ++ ret = -EFAULT; ++ goto out_unmap; ++ } ++ ++ count = min_t(unsigned long, 5UL, whole_end - whole_p); ++ if (!wasm_consume_varU32_user(&whole_p, &subsection_length, count)) { ++ pr_err("Failed to read dylink.0 subsection length"); ++ ret = -ENOEXEC; ++ goto out_unmap; ++ } ++ ++ subsection_end = whole_p + subsection_length; ++ if (subsection_end < whole_p /* overflow */ ++ || subsection_end > whole_end) { ++ pr_err("dylink.0 subsection length overflow"); ++ ret = -ENOEXEC; ++ goto out_unmap; ++ } ++ ++ if (subsection_id == WASM_DYLINK_MEMINFO) { ++ count = min_t(unsigned long, 5UL, subsection_end - whole_p); ++ if (!wasm_consume_varU32_user(&whole_p, &data_size, count)) { ++ pr_err("Failed to read dylink.0 meminfo memory size"); ++ ret = -ENOEXEC; ++ goto out_unmap; ++ } ++ data_size = PAGE_ALIGN(data_size); ++ ++ count = min_t(unsigned long, 5UL, subsection_end - whole_p); ++ if (!wasm_consume_varU32_user(&whole_p, &data_align, count)) { ++ pr_err("Failed to read dylink.0 meminfo memory alignment"); ++ ret = -ENOEXEC; ++ goto out_unmap; ++ } else if (data_align > 31U) { ++ pr_err("dylink.0 meminfo memory alignment too large"); ++ ret = -ENOEXEC; ++ goto out_unmap; ++ } ++ data_align = 1UL << (int)data_align; ++ ++ count = min_t(unsigned long, 5UL, subsection_end - whole_p); ++ if (!wasm_consume_varU32_user(&whole_p, &table_size, count)) { ++ pr_err("Failed to read dylink.0 meminfo table size"); ++ ret = -ENOEXEC; ++ goto out_unmap; ++ } ++ table_size = PAGE_ALIGN(table_size); ++ ++ count = min_t(unsigned long, 5UL, subsection_end - whole_p); ++ if (!wasm_consume_varU32_user(&whole_p, &table_align, count)) { ++ pr_err("Failed to read dylink.0 meminfo table alignment"); ++ ret = -ENOEXEC; ++ goto out_unmap; ++ } else if (table_align > 31U) { ++ pr_err("dylink.0 meminfo table alignment too large"); ++ ret = -ENOEXEC; ++ goto out_unmap; ++ } ++ table_align = 1UL << (int)table_align; ++ ++ has_meminfo = true; ++ } ++ ++ whole_p = subsection_end; ++ } ++ ++ if (!has_meminfo) { ++ pr_err("No dylink.0 meminfo found"); ++ ret = -ENOEXEC; ++ goto out_unmap; ++ } ++ ++ /* ++ * MAP_ANMONYMOUS clears the data (and bss). In Wasm, the runtime ++ * manages bss inside the data area. The runtime may rely on the data ++ * being zeroed as it is placing bss inside (or rather not touchhing bss ++ * pages at all). Thus data and bss are the same and zeroed. ++ */ ++ data_start = vm_mmap(NULL, 0, data_size, ++ PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0); ++ if (!data_start || IS_ERR_VALUE(data_start)) { ++ ret = data_start ? (int)data_start : -ENOMEM; ++ pr_err("Unable to allocate RAM for process data, errno: %d\n", ++ ret); ++ goto out_unmap; ++ } ++ ++ /* ++ * Create a stack, and put the brk at the start of this area. ++ */ ++ stack_size = PAGE_ALIGN(WASM_STACK_SIZE + extra_stack); ++ current->mm->start_brk = vm_mmap(NULL, 0, stack_size, ++ PROT_READ|PROT_WRITE, ++ MAP_PRIVATE|MAP_ANONYMOUS|MAP_GROWSDOWN, 0); ++ if (!current->mm->start_brk || IS_ERR_VALUE(current->mm->start_brk)) { ++ ret = current->mm->start_brk ? ++ (int)current->mm->start_brk : -ENOMEM; ++ pr_err("Unable to allocate RAM for stack, errno: %d\n", ret); ++ current->mm->start_brk = 0; ++ goto out_unmap; ++ } ++ current->mm->brk = current->mm->start_brk; /* Already page aligned... */ ++#ifndef CONFIG_MMU ++ current->mm->context.end_brk = current->mm->start_brk + stack_size; ++#endif ++ current->mm->start_stack = current->mm->start_brk + stack_size; ++ ++ /* Only set these if the above succeeds. */ ++ current->mm->start_code = whole_start; ++ current->mm->end_code = whole_end; ++ current->mm->start_data = data_start; ++ current->mm->end_data = data_start + data_size; ++ ++ return 0; ++ ++out_unmap: ++ vm_munmap(whole_start, whole_size); ++ if (data_start) ++ vm_munmap(data_start, data_size); ++ return ret; ++} ++ ++static int load_wasm_binary(struct linux_binprm *bprm); ++ ++static struct linux_binfmt wasm_format = { ++ .module = THIS_MODULE, ++ .load_binary = load_wasm_binary, ++}; ++ ++static int load_wasm_binary(struct linux_binprm *bprm) ++{ ++ struct pt_regs *regs = current_pt_regs(); ++ unsigned long extra_stack = 0; ++ int res; ++ ++ /* ++ * We have to add the size of our arguments to our stack size ++ * otherwise it's too easy for users to create stack overflows ++ * by passing in a huge argument list. And yes, we have to be ++ * pedantic and include space for the argv/envp array as it may have ++ * a lot of entries. ++ */ ++#ifndef CONFIG_MMU ++ extra_stack += PAGE_SIZE * MAX_ARG_PAGES - bprm->p; /* the strings */ ++#endif ++ extra_stack += (bprm->argc + 1) * sizeof(char *); /* the argv array */ ++ extra_stack += (bprm->envc + 1) * sizeof(char *); /* the envp array */ ++ extra_stack = ALIGN(extra_stack, WASM_STACK_ALIGN); ++ ++ res = load_wasm_file(bprm, extra_stack); ++ if (res < 0) ++ return res; ++ ++ set_binfmt(&wasm_format); ++ ++#ifdef CONFIG_MMU ++ res = setup_arg_pages(bprm, STACK_TOP, EXSTACK_DEFAULT); ++ if (!res) ++ res = create_wasm_tables(bprm, bprm->p); ++#else ++ res = transfer_args_to_stack(bprm, ¤t->mm->start_stack); ++ if (!res) ++ res = create_wasm_tables(bprm, current->mm->start_stack); ++#endif ++ if (res) ++ return res; ++ ++ finalize_exec(bprm); ++ start_thread(regs, current->mm->start_stack); ++ ++ return 0; ++} ++ ++static int __init init_wasm_binfmt(void) ++{ ++ register_binfmt(&wasm_format); ++ return 0; ++} ++core_initcall(init_wasm_binfmt); +-- +2.25.1 + diff --git a/patches/kernel/0007-Use-.section-format-compatible-with-LLVM-as-when-tar.patch b/patches/kernel/0007-Use-.section-format-compatible-with-LLVM-as-when-tar.patch new file mode 100644 index 0000000..4a61999 --- /dev/null +++ b/patches/kernel/0007-Use-.section-format-compatible-with-LLVM-as-when-tar.patch @@ -0,0 +1,170 @@ +From 18c7f0ad07fdf735784c0125e5e05095c01c31eb Mon Sep 17 00:00:00 2001 +From: Joel Severin +Date: Sun, 12 May 2024 17:27:43 +0200 +Subject: [PATCH] Use .section format compatible with LLVM as when targeting + Wasm + +LLVM as does apparently need sizes and no "a" flag for sections when +assembling Wasm files. This seems to differ from other targets. +--- + scripts/kallsyms.c | 38 +++++++++++++++++++++++++++----------- + usr/initramfs_data.S | 7 +++++-- + 2 files changed, 32 insertions(+), 13 deletions(-) + +diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c +index 13af6d0ff..f090766f5 100644 +--- a/scripts/kallsyms.c ++++ b/scripts/kallsyms.c +@@ -287,13 +287,18 @@ static void read_map(const char *in) + fclose(fp); + } + +-static void output_label(const char *label) ++static void output_label_begin(const char *label) + { + printf(".globl %s\n", label); + printf("\tALGN\n"); + printf("%s:\n", label); + } + ++static void output_label_end(const char *label) ++{ ++ printf(".size\t%s,.-%s\n", label, label); ++} ++ + /* Provide proper symbols relocatability by their '_text' relativeness. */ + static void output_address(unsigned long long addr) + { +@@ -395,10 +400,11 @@ static void write_src(void) + printf("#define ALGN .balign 4\n"); + printf("#endif\n"); + +- printf("\t.section .rodata, \"a\"\n"); ++ printf("\t.section .rodata, \"\", @\n"); + +- output_label("kallsyms_num_syms"); ++ output_label_begin("kallsyms_num_syms"); + printf("\t.long\t%u\n", table_cnt); ++ output_label_end("kallsyms_num_syms"); + printf("\n"); + + /* table of offset markers, that give the offset in the compressed stream +@@ -410,7 +416,7 @@ static void write_src(void) + exit(EXIT_FAILURE); + } + +- output_label("kallsyms_names"); ++ output_label_begin("kallsyms_names"); + off = 0; + for (i = 0; i < table_cnt; i++) { + if ((i & 0xFF) == 0) +@@ -447,6 +453,7 @@ static void write_src(void) + printf(", 0x%02x", table[i]->sym[k]); + printf("\n"); + } ++ output_label_end("kallsyms_names"); + printf("\n"); + + /* +@@ -458,14 +465,15 @@ static void write_src(void) + strcpy((char *)table[i]->sym, buf); + } + +- output_label("kallsyms_markers"); ++ output_label_begin("kallsyms_markers"); + for (i = 0; i < ((table_cnt + 255) >> 8); i++) + printf("\t.long\t%u\n", markers[i]); ++ output_label_end("kallsyms_markers"); + printf("\n"); + + free(markers); + +- output_label("kallsyms_token_table"); ++ output_label_begin("kallsyms_token_table"); + off = 0; + for (i = 0; i < 256; i++) { + best_idx[i] = off; +@@ -473,17 +481,19 @@ static void write_src(void) + printf("\t.asciz\t\"%s\"\n", buf); + off += strlen(buf) + 1; + } ++ output_label_end("kallsyms_token_table"); + printf("\n"); + +- output_label("kallsyms_token_index"); ++ output_label_begin("kallsyms_token_index"); + for (i = 0; i < 256; i++) + printf("\t.short\t%d\n", best_idx[i]); ++ output_label_end("kallsyms_token_index"); + printf("\n"); + + if (!base_relative) +- output_label("kallsyms_addresses"); ++ output_label_begin("kallsyms_addresses"); + else +- output_label("kallsyms_offsets"); ++ output_label_begin("kallsyms_offsets"); + + for (i = 0; i < table_cnt; i++) { + if (base_relative) { +@@ -521,11 +531,16 @@ static void write_src(void) + printf("\tPTR\t%#llx\n", table[i]->addr); + } + } ++ if (!base_relative) ++ output_label_end("kallsyms_addresses"); ++ else ++ output_label_end("kallsyms_offsets"); + printf("\n"); + + if (base_relative) { +- output_label("kallsyms_relative_base"); ++ output_label_begin("kallsyms_relative_base"); + output_address(relative_base); ++ output_label_end("kallsyms_relative_base"); + printf("\n"); + } + +@@ -534,12 +549,13 @@ static void write_src(void) + cleanup_symbol_name((char *)table[i]->sym); + + sort_symbols_by_name(); +- output_label("kallsyms_seqs_of_names"); ++ output_label_begin("kallsyms_seqs_of_names"); + for (i = 0; i < table_cnt; i++) + printf("\t.byte 0x%02x, 0x%02x, 0x%02x\n", + (unsigned char)(table[i]->seq >> 16), + (unsigned char)(table[i]->seq >> 8), + (unsigned char)(table[i]->seq >> 0)); ++ output_label_end("kallsyms_seqs_of_names"); + printf("\n"); + } + +diff --git a/usr/initramfs_data.S b/usr/initramfs_data.S +index cd67edc38..dd1990aaf 100644 +--- a/usr/initramfs_data.S ++++ b/usr/initramfs_data.S +@@ -22,11 +22,13 @@ + in the ELF header, as required by certain architectures. + */ + +-.section .init.ramfs,"a" ++.section .init.ramfs,"",@ + __irf_start: + .incbin "usr/initramfs_inc_data" ++.size __irf_start,.-__irf_start + __irf_end: +-.section .init.ramfs.info,"a" ++.size __irf_end,0 ++.section .init.ramfs.info,"",@ + .globl __initramfs_size + __initramfs_size: + #ifdef CONFIG_64BIT +@@ -34,3 +36,4 @@ __initramfs_size: + #else + .long __irf_end - __irf_start + #endif ++.size __initramfs_size,.-__initramfs_size +-- +2.25.1 + diff --git a/patches/kernel/0008-Provide-Wasm-support-in-mk_elfconfig.patch b/patches/kernel/0008-Provide-Wasm-support-in-mk_elfconfig.patch new file mode 100644 index 0000000..9b102af --- /dev/null +++ b/patches/kernel/0008-Provide-Wasm-support-in-mk_elfconfig.patch @@ -0,0 +1,75 @@ +From dd91ef3a67b1034191966eccdee8f583a69db9fa Mon Sep 17 00:00:00 2001 +From: Joel Severin +Date: Sun, 12 May 2024 19:22:35 +0200 +Subject: [PATCH] Provide Wasm support in mk_elfconfig + +This is some kind of emulation for the modpost parts of Kbuild which +currently assumes that ELF is used by all targets. +--- + scripts/mod/mk_elfconfig.c | 48 +++++++++++++++++++++----------------- + 1 file changed, 27 insertions(+), 21 deletions(-) + +diff --git a/scripts/mod/mk_elfconfig.c b/scripts/mod/mk_elfconfig.c +index 680eade89..782522b23 100644 +--- a/scripts/mod/mk_elfconfig.c ++++ b/scripts/mod/mk_elfconfig.c +@@ -14,29 +14,35 @@ main(int argc, char **argv) + fprintf(stderr, "Error: input truncated\n"); + return 1; + } +- if (memcmp(ei, ELFMAG, SELFMAG) != 0) { +- fprintf(stderr, "Error: not ELF\n"); +- return 1; +- } +- switch (ei[EI_CLASS]) { +- case ELFCLASS32: ++ ++ if (memcmp(ei, "\x00" "asm", 4UL) == 0) { ++ /* Wasm (not ELF) - provide some emulation. */ + printf("#define KERNEL_ELFCLASS ELFCLASS32\n"); +- break; +- case ELFCLASS64: +- printf("#define KERNEL_ELFCLASS ELFCLASS64\n"); +- break; +- default: +- exit(1); +- } +- switch (ei[EI_DATA]) { +- case ELFDATA2LSB: + printf("#define KERNEL_ELFDATA ELFDATA2LSB\n"); +- break; +- case ELFDATA2MSB: +- printf("#define KERNEL_ELFDATA ELFDATA2MSB\n"); +- break; +- default: +- exit(1); ++ } else if (memcmp(ei, ELFMAG, SELFMAG) != 0) { ++ fprintf(stderr, "Error: not ELF, nor Wasm\n"); ++ return 1; ++ } else { ++ switch (ei[EI_CLASS]) { ++ case ELFCLASS32: ++ printf("#define KERNEL_ELFCLASS ELFCLASS32\n"); ++ break; ++ case ELFCLASS64: ++ printf("#define KERNEL_ELFCLASS ELFCLASS64\n"); ++ break; ++ default: ++ exit(1); ++ } ++ switch (ei[EI_DATA]) { ++ case ELFDATA2LSB: ++ printf("#define KERNEL_ELFDATA ELFDATA2LSB\n"); ++ break; ++ case ELFDATA2MSB: ++ printf("#define KERNEL_ELFDATA ELFDATA2MSB\n"); ++ break; ++ default: ++ exit(1); ++ } + } + + if (sizeof(unsigned long) == 4) { +-- +2.25.1 + diff --git a/patches/kernel/0009-Add-dummy-ELF-constants-for-Wasm.patch b/patches/kernel/0009-Add-dummy-ELF-constants-for-Wasm.patch new file mode 100644 index 0000000..9b6231f --- /dev/null +++ b/patches/kernel/0009-Add-dummy-ELF-constants-for-Wasm.patch @@ -0,0 +1,101 @@ +From 452439e4c22ae6cb0c3f9055a572ba862c78271a Mon Sep 17 00:00:00 2001 +From: Joel Severin +Date: Sun, 12 May 2024 19:26:03 +0200 +Subject: [PATCH] Add dummy ELF constants for Wasm + +The kernel needs ELF to build, but Wasm currently only support for the +.wasm file format. Some dummy ELF constants are added as a workaround. +--- + arch/wasm/include/uapi/asm/elf.h | 80 ++++++++++++++++++++++++++++++++ + 1 file changed, 80 insertions(+) + create mode 100644 arch/wasm/include/uapi/asm/elf.h + +diff --git a/arch/wasm/include/uapi/asm/elf.h b/arch/wasm/include/uapi/asm/elf.h +new file mode 100644 +index 000000000..853cd3786 +--- /dev/null ++++ b/arch/wasm/include/uapi/asm/elf.h +@@ -0,0 +1,80 @@ ++/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ ++ ++#ifndef _UAPI_ASM_WASM_ELF_H ++#define _UAPI_ASM_WASM_ELF_H ++ ++#include ++ ++/* ELF register definitions */ ++typedef unsigned long elf_greg_t; ++typedef struct user_regs_struct elf_gregset_t; ++#define ELF_NGREG (sizeof(elf_gregset_t) / sizeof(elf_greg_t)) ++ ++/* We don't support f without d, or q. */ ++typedef __u64 elf_fpreg_t; ++typedef union __wasm_fp_state elf_fpregset_t; ++#define ELF_NFPREG (sizeof(struct __wasm_d_ext_state) / sizeof(elf_fpreg_t)) ++ ++//#define ELF_WASM_R_SYM(r_info) ELF32_R_SYM(r_info) ++//#define ELF_WASM_R_TYPE(r_info) ELF32_R_TYPE(r_info) ++ ++/* Relocation types used by the dynamic linker */ ++#define R_WASM_NONE 0 ++#define R_WASM_32 1 ++#define R_WASM_64 2 ++#define R_WASM_RELATIVE 3 ++#define R_WASM_COPY 4 ++#define R_WASM_JUMP_SLOT 5 ++#define R_WASM_TLS_DTPMOD32 6 ++#define R_WASM_TLS_DTPMOD64 7 ++#define R_WASM_TLS_DTPREL32 8 ++#define R_WASM_TLS_DTPREL64 9 ++#define R_WASM_TLS_TPREL32 10 ++#define R_WASM_TLS_TPREL64 11 ++ ++/* Relocation types not used by the dynamic linker */ ++#define R_WASM_BRANCH 16 ++#define R_WASM_JAL 17 ++#define R_WASM_CALL 18 ++#define R_WASM_CALL_PLT 19 ++#define R_WASM_GOT_HI20 20 ++#define R_WASM_TLS_GOT_HI20 21 ++#define R_WASM_TLS_GD_HI20 22 ++#define R_WASM_PCREL_HI20 23 ++#define R_WASM_PCREL_LO12_I 24 ++#define R_WASM_PCREL_LO12_S 25 ++#define R_WASM_HI20 26 ++#define R_WASM_LO12_I 27 ++#define R_WASM_LO12_S 28 ++#define R_WASM_TPREL_HI20 29 ++#define R_WASM_TPREL_LO12_I 30 ++#define R_WASM_TPREL_LO12_S 31 ++#define R_WASM_TPREL_ADD 32 ++#define R_WASM_ADD8 33 ++#define R_WASM_ADD16 34 ++#define R_WASM_ADD32 35 ++#define R_WASM_ADD64 36 ++#define R_WASM_SUB8 37 ++#define R_WASM_SUB16 38 ++#define R_WASM_SUB32 39 ++#define R_WASM_SUB64 40 ++#define R_WASM_GNU_VTINHERIT 41 ++#define R_WASM_GNU_VTENTRY 42 ++#define R_WASM_ALIGN 43 ++#define R_WASM_RVC_BRANCH 44 ++#define R_WASM_RVC_JUMP 45 ++#define R_WASM_LUI 46 ++#define R_WASM_GPREL_I 47 ++#define R_WASM_GPREL_S 48 ++#define R_WASM_TPREL_I 49 ++#define R_WASM_TPREL_S 50 ++#define R_WASM_RELAX 51 ++#define R_WASM_SUB6 52 ++#define R_WASM_SET6 53 ++#define R_WASM_SET8 54 ++#define R_WASM_SET16 55 ++#define R_WASM_SET32 56 ++#define R_WASM_32_PCREL 57 ++ ++ ++#endif /* _UAPI_ASM_WASM_ELF_H */ +-- +2.25.1 + diff --git a/patches/kernel/0010-Add-Wasm-console-support.patch b/patches/kernel/0010-Add-Wasm-console-support.patch new file mode 100644 index 0000000..8805082 --- /dev/null +++ b/patches/kernel/0010-Add-Wasm-console-support.patch @@ -0,0 +1,121 @@ +From 2a52409cba31a1d5709451eceedfa7c8868bcfb8 Mon Sep 17 00:00:00 2001 +From: Joel Severin +Date: Sun, 30 Jun 2024 00:47:26 +0200 +Subject: [PATCH] Add Wasm console support + +Adds a simple HVC + earlycon tty driver that calls out to the Wasm host +using simple exported functions on the vmlinux Module for reads/writes. +--- + arch/wasm/Kconfig | 2 +- + arch/wasm/Makefile | 1 + + arch/wasm/drivers/Kconfig | 22 ++++++++++++++++++++++ + arch/wasm/drivers/Makefile | 3 +++ + arch/wasm/drivers/hvc_wasm.c | 35 +++++++++++++++++++++++++++++++++++ + 5 files changed, 62 insertions(+), 1 deletion(-) + create mode 100644 arch/wasm/drivers/Kconfig + create mode 100644 arch/wasm/drivers/Makefile + create mode 100644 arch/wasm/drivers/hvc_wasm.c + +diff --git a/arch/wasm/Kconfig b/arch/wasm/Kconfig +index 744e8c676..2e01d91b3 100644 +--- a/arch/wasm/Kconfig ++++ b/arch/wasm/Kconfig +@@ -77,4 +77,4 @@ config ARCH_HAVE_PANIC_NOTIFY + + endmenu + +-source "drivers/Kconfig" ++source "arch/wasm/drivers/Kconfig" +diff --git a/arch/wasm/Makefile b/arch/wasm/Makefile +index b86103e0b..841f3b006 100644 +--- a/arch/wasm/Makefile ++++ b/arch/wasm/Makefile +@@ -12,6 +12,7 @@ KCFLAGS += -Xclang -target-feature -Xclang +bulk-memory + core-y += arch/wasm/kernel/ + core-y += arch/wasm/mm/ + libs-y += arch/wasm/lib/ ++drivers-y += arch/wasm/drivers/ + + PHONY += bzImage + +diff --git a/arch/wasm/drivers/Kconfig b/arch/wasm/drivers/Kconfig +new file mode 100644 +index 000000000..be8b75496 +--- /dev/null ++++ b/arch/wasm/drivers/Kconfig +@@ -0,0 +1,22 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++ ++menu "Wasm Character Devices" ++ ++config HVC_WASM ++ bool "Wasm console support" ++ select HVC_DRIVER ++ help ++ This config option enables support for a console managed by the Wasm ++ host, for example to read printk output during boot as well as ++ receiving user input. It works both as an earlycon and a tty. As an ++ earlycon, very early debug logging is output. As a tty (enabled later ++ on in the boot process), it also supports user input from the host. ++ ++ In addition to enabling this config option at build time, you also ++ need to specify console=hvc as a parameter on the kernel command line ++ to activate the feature at runtime. Just specifying console=hvc is ++ enough to enable the earlycon aspects of this console driver as well. ++ ++ If you don't know what to do here, say Y. ++ ++endmenu +diff --git a/arch/wasm/drivers/Makefile b/arch/wasm/drivers/Makefile +new file mode 100644 +index 000000000..0ebdc20a8 +--- /dev/null ++++ b/arch/wasm/drivers/Makefile +@@ -0,0 +1,3 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++ ++obj-$(CONFIG_HVC_WASM) += hvc_wasm.o +diff --git a/arch/wasm/drivers/hvc_wasm.c b/arch/wasm/drivers/hvc_wasm.c +new file mode 100644 +index 000000000..78f34c060 +--- /dev/null ++++ b/arch/wasm/drivers/hvc_wasm.c +@@ -0,0 +1,35 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++#include "../../../drivers/tty/hvc/hvc_console.h" ++ ++extern int wasm_driver_hvc_put(const char *buf, int count); ++extern int wasm_driver_hvc_get(char *buf, int count); ++ ++static int hvc_wasm_put_chars(uint32_t vtermno, const char *buf, int count) ++{ ++ return wasm_driver_hvc_put(buf, count); ++} ++ ++static int hvc_wasm_get_chars(uint32_t vtermno, char *buf, int count) ++{ ++ return wasm_driver_hvc_get(buf, count); ++} ++ ++static const struct hv_ops hvc_wasm_ops = { ++ .get_chars = hvc_wasm_get_chars, ++ .put_chars = hvc_wasm_put_chars, ++}; ++ ++static int __init hvc_wasm_init(void) ++{ ++ return PTR_ERR_OR_ZERO(hvc_alloc(0, 0, &hvc_wasm_ops, PAGE_SIZE)); ++} ++device_initcall(hvc_wasm_init); ++ ++static int __init hvc_wasm_console_init(void) ++{ ++ hvc_instantiate(0, 0, &hvc_wasm_ops); ++ ++ return 0; ++} ++console_initcall(hvc_wasm_console_init); +-- +2.25.1 + diff --git a/patches/kernel/0011-Add-wasm_defconfig.patch b/patches/kernel/0011-Add-wasm_defconfig.patch new file mode 100644 index 0000000..edd913f --- /dev/null +++ b/patches/kernel/0011-Add-wasm_defconfig.patch @@ -0,0 +1,41 @@ +From d1f576fb102da92fe3392c12125b5f0be03601f1 Mon Sep 17 00:00:00 2001 +From: Joel Severin +Date: Sun, 12 May 2024 19:31:55 +0200 +Subject: [PATCH] Add wasm_defconfig + +This patch needs to be updated to use an auto-generated config instead. +--- + arch/wasm/configs/wasm_defconfig | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + create mode 100644 arch/wasm/configs/wasm_defconfig + +diff --git a/arch/wasm/configs/wasm_defconfig b/arch/wasm/configs/wasm_defconfig +new file mode 100644 +index 000000000..10fb9e0ab +--- /dev/null ++++ b/arch/wasm/configs/wasm_defconfig +@@ -0,0 +1,21 @@ ++CONFIG_SLAB=y ++ ++CONFIG_NO_HZ_FULL=y ++ ++CONFIG_MAGIC_SYSRQ=y ++CONFIG_DEBUG_KERNEL=y ++CONFIG_DEBUG_INFO_DWARF5=y ++CONFIG_HVC_WASM=y ++ ++CONFIG_BLK_DEV_INITRD=y ++ ++CONFIG_BINFMT_WASM=y ++CONFIG_BINFMT_MISC=m ++ ++#CONFIG_MODULES=y ++#CONFIG_MODULE_UNLOAD=y ++ ++#CONFIG_NET=y ++#CONFIG_PACKET=y ++#CONFIG_UNIX=y ++#CONFIG_INET=y +-- +2.25.1 + diff --git a/patches/kernel/0012-HACK-Workaround-broken-wq_worker_comm.patch b/patches/kernel/0012-HACK-Workaround-broken-wq_worker_comm.patch new file mode 100644 index 0000000..aef1cfa --- /dev/null +++ b/patches/kernel/0012-HACK-Workaround-broken-wq_worker_comm.patch @@ -0,0 +1,29 @@ +From f3e782cb608b0bdb63bc3a2280cfbbb8acf464c0 Mon Sep 17 00:00:00 2001 +From: Joel Severin +Date: Sat, 13 Sep 2025 21:55:10 +0200 +Subject: [PATCH] HACK: Workaround broken wq_worker_comm + +worker->pool seems to be corrupted somehow, root cause unknown. This +likely happens due to an unrelated memory corruption bug. +--- + kernel/workqueue.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/kernel/workqueue.c b/kernel/workqueue.c +index c913e333c..317e62987 100644 +--- a/kernel/workqueue.c ++++ b/kernel/workqueue.c +@@ -5129,7 +5129,9 @@ void wq_worker_comm(char *buf, size_t size, struct task_struct *task) + struct worker *worker = kthread_data(task); + struct worker_pool *pool = worker->pool; + +- if (pool) { ++ if ((unsigned long)pool == -1UL) ++ scnprintf(buf + off, size - off, "-BROKEN"); ++ else if (pool) { + raw_spin_lock_irq(&pool->lock); + /* + * ->desc tracks information (wq name or +-- +2.25.1 + diff --git a/patches/llvm/0001-Hack-patch-to-allow-GNU-ld-style-linker-scripts-in-w.patch b/patches/llvm/0001-Hack-patch-to-allow-GNU-ld-style-linker-scripts-in-w.patch new file mode 100644 index 0000000..02a222a --- /dev/null +++ b/patches/llvm/0001-Hack-patch-to-allow-GNU-ld-style-linker-scripts-in-w.patch @@ -0,0 +1,2220 @@ +From 939b82d11279ee2700c19c41a770051cc28e045f Mon Sep 17 00:00:00 2001 +From: Joel Severin +Date: Tue, 26 Mar 2024 12:37:11 +0100 +Subject: [PATCH] Hack patch to allow GNU ld style linker scripts in wasm-ld + +--- + lld/wasm/CMakeLists.txt | 2 + + lld/wasm/Config.h | 1 + + lld/wasm/Driver.cpp | 30 +- + lld/wasm/InputFiles.cpp | 4 + + lld/wasm/Options.td | 2 + + lld/wasm/ScriptLexer.cpp | 328 ++++++++++++ + lld/wasm/ScriptLexer.h | 56 ++ + lld/wasm/ScriptParser.cpp | 1056 +++++++++++++++++++++++++++++++++++++ + lld/wasm/ScriptParser.h | 341 ++++++++++++ + lld/wasm/Writer.cpp | 241 ++++++++- + 10 files changed, 2051 insertions(+), 10 deletions(-) + create mode 100644 lld/wasm/ScriptLexer.cpp + create mode 100644 lld/wasm/ScriptLexer.h + create mode 100644 lld/wasm/ScriptParser.cpp + create mode 100644 lld/wasm/ScriptParser.h + +diff --git a/lld/wasm/CMakeLists.txt b/lld/wasm/CMakeLists.txt +index 6033bfbf9..53048d818 100644 +--- a/lld/wasm/CMakeLists.txt ++++ b/lld/wasm/CMakeLists.txt +@@ -12,6 +12,8 @@ add_lld_library(lldWasm + OutputSections.cpp + OutputSegment.cpp + Relocations.cpp ++ ScriptLexer.cpp ++ ScriptParser.cpp + SymbolTable.cpp + Symbols.cpp + SyntheticSections.cpp +diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h +index 97c508bda..e42ffdb94 100644 +--- a/lld/wasm/Config.h ++++ b/lld/wasm/Config.h +@@ -57,6 +57,7 @@ struct Configuration { + bool growableTable; + bool gcSections; + llvm::StringSet<> keepSections; ++ std::optional linkerScript; + std::optional> memoryImport; + std::optional memoryExport; + bool sharedMemory; +diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp +index 635f19f78..af849f9b7 100644 +--- a/lld/wasm/Driver.cpp ++++ b/lld/wasm/Driver.cpp +@@ -327,6 +327,15 @@ static std::optional findFromSearchPaths(StringRef path) { + return std::nullopt; + } + ++// If a linker/version script doesn't exist in the current directory, we also ++// look for the script in the '-L' search paths. This matches the behaviour of ++// '-T', --version-script=, and linker script INPUT() command in ld.bfd. ++static std::optional searchScript(StringRef name) { ++ if (fs::exists(name)) ++ return name.str(); ++ return findFromSearchPaths(name); ++} ++ + // This is for -l. We'll look for lib.a from + // search paths. + static std::optional searchLibraryBaseName(StringRef name) { +@@ -388,6 +397,13 @@ void LinkerDriver::createFiles(opt::InputArgList &args) { + error("stray --end-lib"); + inLib = false; + break; ++ case OPT_script: ++ if (std::optional path = searchScript(arg->getValue())) { ++ config->linkerScript = readFile(*path); ++ } else { ++ error(Twine("cannot find linker script ") + arg->getValue()); ++ } ++ break; + } + } + if (files.empty() && errorCount() == 0) +@@ -617,12 +633,6 @@ static void setConfigs() { + // pointer. + if (!config->tableBase) + config->tableBase = 1; +- // The default offset for static/global data, for when --global-base is +- // not specified on the command line. The precise value of 1024 is +- // somewhat arbitrary, and pre-dates wasm-ld (Its the value that +- // emscripten used prior to wasm-ld). +- if (!config->globalBase && !config->relocatable && !config->stackFirst) +- config->globalBase = 1024; + } + + if (config->relocatable) { +@@ -1195,6 +1205,14 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { + if (errorCount()) + return; + ++ // The default offset for static/global data, for when --global-base is ++ // not specified on the command line. The precise value of 1024 is ++ // somewhat arbitrary, and pre-dates wasm-ld (Its the value that ++ // emscripten used prior to wasm-ld). ++ if (!config->globalBase && !config->linkerScript && !ctx.isPic && ++ !config->relocatable && !config->stackFirst) ++ config->globalBase = 1024; ++ + checkOptions(args); + if (errorCount()) + return; +diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp +index f5e946aca..db768fe63 100644 +--- a/lld/wasm/InputFiles.cpp ++++ b/lld/wasm/InputFiles.cpp +@@ -362,6 +362,10 @@ static bool shouldMerge(const WasmSection &sec) { + } + + static bool shouldMerge(const WasmSegment &seg) { ++ // No merge chunks when using linker scripts. ++ if (config->linkerScript) ++ return false; ++ + // As of now we only support merging strings, and only with single byte + // alignment (2^0). + if (!(seg.Data.LinkingFlags & WASM_SEG_FLAG_STRINGS) || +diff --git a/lld/wasm/Options.td b/lld/wasm/Options.td +index 8190717ce..bc1cd6b74 100644 +--- a/lld/wasm/Options.td ++++ b/lld/wasm/Options.td +@@ -294,6 +294,8 @@ def thinlto_jobs: JJ<"thinlto-jobs=">, + def lto_debug_pass_manager: FF<"lto-debug-pass-manager">, + HelpText<"Debug new pass manager">; + ++defm script: Eq<"script", "Use linker script ((very) limited support for GNU ld/ELF linker scripts)">; ++ + // Experimental PIC mode. + def experimental_pic: FF<"experimental-pic">, + HelpText<"Enable Experimental PIC">; +diff --git a/lld/wasm/ScriptLexer.cpp b/lld/wasm/ScriptLexer.cpp +new file mode 100644 +index 000000000..49fb05a2e +--- /dev/null ++++ b/lld/wasm/ScriptLexer.cpp +@@ -0,0 +1,328 @@ ++//===- ScriptLexer.cpp ----------------------------------------------------===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++// ++// This file defines a lexer for the linker script. ++// ++// The linker script's grammar is not complex but ambiguous due to the ++// lack of the formal specification of the language. What we are trying to ++// do in this and other files in LLD is to make a "reasonable" linker ++// script processor. ++// ++// Among simplicity, compatibility and efficiency, we put the most ++// emphasis on simplicity when we wrote this lexer. Compatibility with the ++// GNU linkers is important, but we did not try to clone every tiny corner ++// case of their lexers, as even ld.bfd and ld.gold are subtly different ++// in various corner cases. We do not care much about efficiency because ++// the time spent in parsing linker scripts is usually negligible. ++// ++// Our grammar of the linker script is LL(2), meaning that it needs at ++// most two-token lookahead to parse. The only place we need two-token ++// lookahead is labels in version scripts, where we need to parse "local :" ++// as if "local:". ++// ++// Overall, this lexer works fine for most linker scripts. There might ++// be room for improving compatibility, but that's probably not at the ++// top of our todo list. ++// ++//===----------------------------------------------------------------------===// ++ ++#include "ScriptLexer.h" ++#include "lld/Common/ErrorHandler.h" ++#include "llvm/ADT/Twine.h" ++#include "llvm/Support/ErrorHandling.h" ++#include ++ ++using namespace llvm; ++using namespace lld; ++using namespace lld::wasm; ++ ++// Returns a whole line containing the current token. ++StringRef ScriptLexer::getLine() { ++ StringRef s = getCurrentMB().getBuffer(); ++ StringRef tok = tokens[pos - 1]; ++ ++ size_t pos = s.rfind('\n', tok.data() - s.data()); ++ if (pos != StringRef::npos) ++ s = s.substr(pos + 1); ++ return s.substr(0, s.find_first_of("\r\n")); ++} ++ ++// Returns 1-based line number of the current token. ++size_t ScriptLexer::getLineNumber() { ++ if (pos == 0) ++ return 1; ++ StringRef s = getCurrentMB().getBuffer(); ++ StringRef tok = tokens[pos - 1]; ++ const size_t tokOffset = tok.data() - s.data(); ++ ++ // For the first token, or when going backwards, start from the beginning of ++ // the buffer. If this token is after the previous token, start from the ++ // previous token. ++ size_t line = 1; ++ size_t start = 0; ++ if (lastLineNumberOffset > 0 && tokOffset >= lastLineNumberOffset) { ++ start = lastLineNumberOffset; ++ line = lastLineNumber; ++ } ++ ++ line += s.substr(start, tokOffset - start).count('\n'); ++ ++ // Store the line number of this token for reuse. ++ lastLineNumberOffset = tokOffset; ++ lastLineNumber = line; ++ ++ return line; ++} ++ ++// Returns 0-based column number of the current token. ++size_t ScriptLexer::getColumnNumber() { ++ StringRef tok = tokens[pos - 1]; ++ return tok.data() - getLine().data(); ++} ++ ++std::string ScriptLexer::getCurrentLocation() { ++ std::string filename = std::string(getCurrentMB().getBufferIdentifier()); ++ return (filename + ":" + Twine(getLineNumber())).str(); ++} ++ ++ScriptLexer::ScriptLexer(MemoryBufferRef mb) { tokenize(mb); } ++ ++// We don't want to record cascading errors. Keep only the first one. ++void ScriptLexer::setError(const Twine &msg) { ++ if (errorCount()) ++ return; ++ ++ std::string s = (getCurrentLocation() + ": " + msg).str(); ++ if (pos) ++ s += "\n>>> " + getLine().str() + "\n>>> " + ++ std::string(getColumnNumber(), ' ') + "^"; ++ error(s); ++} ++ ++// Split S into linker script tokens. ++void ScriptLexer::tokenize(MemoryBufferRef mb) { ++ std::vector vec; ++ mbs.push_back(mb); ++ StringRef s = mb.getBuffer(); ++ StringRef begin = s; ++ ++ for (;;) { ++ s = skipSpace(s); ++ if (s.empty()) ++ break; ++ ++ // Quoted token. Note that double-quote characters are parts of a token ++ // because, in a glob match context, only unquoted tokens are interpreted ++ // as glob patterns. Double-quoted tokens are literal patterns in that ++ // context. ++ if (s.starts_with("\"")) { ++ size_t e = s.find("\"", 1); ++ if (e == StringRef::npos) { ++ StringRef filename = mb.getBufferIdentifier(); ++ size_t lineno = begin.substr(0, s.data() - begin.data()).count('\n'); ++ error(filename + ":" + Twine(lineno + 1) + ": unclosed quote"); ++ return; ++ } ++ ++ vec.push_back(s.take_front(e + 1)); ++ s = s.substr(e + 1); ++ continue; ++ } ++ ++ // Some operators form separate tokens. ++ if (s.starts_with("<<=") || s.starts_with(">>=")) { ++ vec.push_back(s.substr(0, 3)); ++ s = s.substr(3); ++ continue; ++ } ++ if (s.size() > 1 && ((s[1] == '=' && strchr("*/+-<>&|", s[0])) || ++ (s[0] == s[1] && strchr("<>&|", s[0])))) { ++ vec.push_back(s.substr(0, 2)); ++ s = s.substr(2); ++ continue; ++ } ++ ++ // Unquoted token. This is more relaxed than tokens in C-like language, ++ // so that you can write "file-name.cpp" as one bare token, for example. ++ size_t pos = s.find_first_not_of( ++ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ++ "0123456789_.$/\\~=+[]*?-!^:"); ++ ++ // A character that cannot start a word (which is usually a ++ // punctuation) forms a single character token. ++ if (pos == 0) ++ pos = 1; ++ vec.push_back(s.substr(0, pos)); ++ s = s.substr(pos); ++ } ++ ++ tokens.insert(tokens.begin() + pos, vec.begin(), vec.end()); ++} ++ ++// Skip leading whitespace characters or comments. ++StringRef ScriptLexer::skipSpace(StringRef s) { ++ for (;;) { ++ if (s.starts_with("/*")) { ++ size_t e = s.find("*/", 2); ++ if (e == StringRef::npos) { ++ setError("unclosed comment in a linker script"); ++ return ""; ++ } ++ s = s.substr(e + 2); ++ continue; ++ } ++ if (s.starts_with("#")) { ++ size_t e = s.find('\n', 1); ++ if (e == StringRef::npos) ++ e = s.size() - 1; ++ s = s.substr(e + 1); ++ continue; ++ } ++ size_t size = s.size(); ++ s = s.ltrim(); ++ if (s.size() == size) ++ return s; ++ } ++} ++ ++// An erroneous token is handled as if it were the last token before EOF. ++bool ScriptLexer::atEOF() { return errorCount() || tokens.size() == pos; } ++ ++// Split a given string as an expression. ++// This function returns "3", "*" and "5" for "3*5" for example. ++static std::vector tokenizeExpr(StringRef s) { ++ StringRef ops = "!~*/+-<>?:="; // List of operators ++ ++ // Quoted strings are literal strings, so we don't want to split it. ++ if (s.starts_with("\"")) ++ return {s}; ++ ++ // Split S with operators as separators. ++ std::vector ret; ++ while (!s.empty()) { ++ size_t e = s.find_first_of(ops); ++ ++ // No need to split if there is no operator. ++ if (e == StringRef::npos) { ++ ret.push_back(s); ++ break; ++ } ++ ++ // Get a token before the operator. ++ if (e != 0) ++ ret.push_back(s.substr(0, e)); ++ ++ // Get the operator as a token. ++ // Keep !=, ==, >=, <=, << and >> operators as a single tokens. ++ if (s.substr(e).starts_with("!=") || s.substr(e).starts_with("==") || ++ s.substr(e).starts_with(">=") || s.substr(e).starts_with("<=") || ++ s.substr(e).starts_with("<<") || s.substr(e).starts_with(">>")) { ++ ret.push_back(s.substr(e, 2)); ++ s = s.substr(e + 2); ++ } else { ++ ret.push_back(s.substr(e, 1)); ++ s = s.substr(e + 1); ++ } ++ } ++ return ret; ++} ++ ++// In contexts where expressions are expected, the lexer should apply ++// different tokenization rules than the default one. By default, ++// arithmetic operator characters are regular characters, but in the ++// expression context, they should be independent tokens. ++// ++// For example, "foo*3" should be tokenized to "foo", "*" and "3" only ++// in the expression context. ++// ++// This function may split the current token into multiple tokens. ++void ScriptLexer::maybeSplitExpr() { ++ if (!inExpr || errorCount() || atEOF()) ++ return; ++ ++ std::vector v = tokenizeExpr(tokens[pos]); ++ if (v.size() == 1) ++ return; ++ tokens.erase(tokens.begin() + pos); ++ tokens.insert(tokens.begin() + pos, v.begin(), v.end()); ++} ++ ++StringRef ScriptLexer::next() { ++ maybeSplitExpr(); ++ ++ if (errorCount()) ++ return ""; ++ if (atEOF()) { ++ setError("unexpected EOF"); ++ return ""; ++ } ++ return tokens[pos++]; ++} ++ ++StringRef ScriptLexer::peek() { ++ StringRef tok = next(); ++ if (errorCount()) ++ return ""; ++ pos = pos - 1; ++ return tok; ++} ++ ++StringRef ScriptLexer::peek2() { ++ skip(); ++ StringRef tok = next(); ++ if (errorCount()) ++ return ""; ++ pos = pos - 2; ++ return tok; ++} ++ ++bool ScriptLexer::consume(StringRef tok) { ++ if (peek() == tok) { ++ skip(); ++ return true; ++ } ++ return false; ++} ++ ++// Consumes Tok followed by ":". Space is allowed between Tok and ":". ++bool ScriptLexer::consumeLabel(StringRef tok) { ++ if (consume((tok + ":").str())) ++ return true; ++ if (tokens.size() >= pos + 2 && tokens[pos] == tok && ++ tokens[pos + 1] == ":") { ++ pos += 2; ++ return true; ++ } ++ return false; ++} ++ ++void ScriptLexer::skip() { (void)next(); } ++ ++void ScriptLexer::expect(StringRef expect) { ++ if (errorCount()) ++ return; ++ StringRef tok = next(); ++ if (tok != expect) ++ setError(expect + " expected, but got " + tok); ++} ++ ++// Returns true if S encloses T. ++static bool encloses(StringRef s, StringRef t) { ++ return s.bytes_begin() <= t.bytes_begin() && t.bytes_end() <= s.bytes_end(); ++} ++ ++MemoryBufferRef ScriptLexer::getCurrentMB() { ++ // Find input buffer containing the current token. ++ assert(!mbs.empty()); ++ if (pos == 0) ++ return mbs.back(); ++ for (MemoryBufferRef mb : mbs) ++ if (encloses(mb.getBuffer(), tokens[pos - 1])) ++ return mb; ++ llvm_unreachable("getCurrentMB: failed to find a token"); ++} +diff --git a/lld/wasm/ScriptLexer.h b/lld/wasm/ScriptLexer.h +new file mode 100644 +index 000000000..33e2bbd05 +--- /dev/null ++++ b/lld/wasm/ScriptLexer.h +@@ -0,0 +1,56 @@ ++//===- ScriptLexer.h --------------------------------------------*- C++ -*-===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++ ++#ifndef LLD_WASM_SCRIPT_LEXER_H ++#define LLD_WASM_SCRIPT_LEXER_H ++ ++#include "lld/Common/LLVM.h" ++#include "llvm/ADT/StringRef.h" ++#include "llvm/Support/MemoryBufferRef.h" ++#include ++ ++namespace lld::wasm { ++ ++class ScriptLexer { ++public: ++ explicit ScriptLexer(MemoryBufferRef mb); ++ ++ void setError(const Twine &msg); ++ void tokenize(MemoryBufferRef mb); ++ StringRef skipSpace(StringRef s); ++ bool atEOF(); ++ StringRef next(); ++ StringRef peek(); ++ StringRef peek2(); ++ void skip(); ++ bool consume(StringRef tok); ++ void expect(StringRef expect); ++ bool consumeLabel(StringRef tok); ++ std::string getCurrentLocation(); ++ ++ std::vector mbs; ++ std::vector tokens; ++ bool inExpr = false; ++ size_t pos = 0; ++ ++ size_t lastLineNumber = 0; ++ size_t lastLineNumberOffset = 0; ++ ++protected: ++ MemoryBufferRef getCurrentMB(); ++ ++private: ++ void maybeSplitExpr(); ++ StringRef getLine(); ++ size_t getLineNumber(); ++ size_t getColumnNumber(); ++}; ++ ++} // namespace lld::wasm ++ ++#endif +diff --git a/lld/wasm/ScriptParser.cpp b/lld/wasm/ScriptParser.cpp +new file mode 100644 +index 000000000..4f246f85a +--- /dev/null ++++ b/lld/wasm/ScriptParser.cpp +@@ -0,0 +1,1056 @@ ++//===- ScriptParser.cpp ---------------------------------------------------===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++// ++// This file contains a recursive-descendent parser for linker scripts. ++// Parsed results are stored to Config and Script global objects. ++// ++//===----------------------------------------------------------------------===// ++ ++#include "ScriptParser.h" ++#include "OutputSections.h" ++#include "OutputSegment.h" ++#include "ScriptLexer.h" ++#include "SymbolTable.h" ++#include "Symbols.h" ++#include "lld/Common/CommonLinkerContext.h" ++#include "llvm/ADT/SmallString.h" ++#include "llvm/ADT/StringRef.h" ++#include "llvm/ADT/StringSet.h" ++#include "llvm/ADT/StringSwitch.h" ++#include "llvm/Support/Casting.h" ++#include "llvm/Support/ErrorHandling.h" ++#include "llvm/Support/FileSystem.h" ++#include "llvm/Support/MathExtras.h" ++#include "llvm/Support/Path.h" ++#include "llvm/Support/SaveAndRestore.h" ++#include "llvm/Support/TimeProfiler.h" ++#include ++#include ++#include ++ ++using namespace llvm; ++using namespace llvm::support::endian; ++using namespace lld; ++using namespace lld::wasm; ++ ++static StringRef unquote(StringRef s) { ++ if (s.starts_with("\"")) ++ return s.substr(1, s.size() - 2); ++ return s; ++} ++ ++// Some operations only support one non absolute value. Move the ++// absolute one to the right hand side for convenience. ++static void moveAbsRight(ExprValue &a, ExprValue &b) { ++ if (a.sec == nullptr || (a.forceAbsolute && !b.isAbsolute())) ++ std::swap(a, b); ++ if (!b.isAbsolute()) ++ error(a.loc + ": at least one side of the expression must be absolute"); ++} ++ ++static ExprValue add(ExprValue a, ExprValue b) { ++ moveAbsRight(a, b); ++ return {a.sec, a.forceAbsolute, a.getSectionOffset() + b.getValue(), a.loc}; ++} ++ ++static ExprValue sub(ExprValue a, ExprValue b) { ++ // The distance between two symbols in sections is absolute. ++ if (!a.isAbsolute() && !b.isAbsolute()) ++ return a.getValue() - b.getValue(); ++ return {a.sec, false, a.getSectionOffset() - b.getValue(), a.loc}; ++} ++ ++static ExprValue bitAnd(ExprValue a, ExprValue b) { ++ moveAbsRight(a, b); ++ return {a.sec, a.forceAbsolute, ++ (a.getValue() & b.getValue()) - a.getSecAddr(), a.loc}; ++} ++ ++static ExprValue bitOr(ExprValue a, ExprValue b) { ++ moveAbsRight(a, b); ++ return {a.sec, a.forceAbsolute, ++ (a.getValue() | b.getValue()) - a.getSecAddr(), a.loc}; ++} ++ ++uint64_t ExprValue::getValue() const { ++ if (sec) ++ return alignToPowerOf2(sec->address + sec->getOffset(val), ++ alignment); ++ return alignToPowerOf2(val, alignment); ++} ++ ++uint64_t ExprValue::getSecAddr() const { ++ return sec ? sec->address + sec->getOffset(0) : 0; ++} ++ ++uint64_t ExprValue::getSectionOffset() const { ++ // If the alignment is trivial, we don't have to compute the full ++ // value to know the offset. This allows this function to succeed in ++ // cases where the output section is not yet known. ++ if (alignment == 1 && !sec) ++ return val; ++ return getValue() - getSecAddr(); ++} ++ ++void ScriptParser::readLinkerScript() { ++ while (!atEOF()) { ++ StringRef tok = next(); ++ if (tok == ";") ++ continue; ++ ++ if (tok == "SECTIONS") { ++ readSections(); ++ } else if (SymbolAssignment *cmd = readAssignment(tok)) { ++ sectionCommands.push_back(cmd); ++ } else { ++ setError("unknown directive: " + tok); ++ } ++ } ++} ++ ++void ScriptParser::readSections() { ++ expect("{"); ++ SmallVector v; ++ while (!errorCount() && !consume("}")) { ++ StringRef tok = next(); ++ if (tok == "OVERLAY") { ++ setError("OVERLAY not supported"); ++ continue; ++ } ++ ++ if (SectionCommand *cmd = readAssignment(tok)) ++ v.push_back(cmd); ++ else ++ v.push_back(readOutputSectionDescription(tok)); ++ } ++ ++ // If DATA_SEGMENT_RELRO_END is absent, for sections after DATA_SEGMENT_ALIGN, ++ // the relro fields should be cleared. ++/* ++ if (!seenRelroEnd) ++ for (SectionCommand *cmd : v) ++ if (auto *osd = dyn_cast(cmd)) ++ osd->osec.relro = false; ++*/ ++ sectionCommands.insert(sectionCommands.end(), v.begin(), v.end()); ++ ++ ++ if (atEOF() || !consume("INSERT")) { ++ hasSectionsCommand = true; ++ return; ++ } ++ ++ setError("INSERT BEFORE/AFTER not supported"); ++} ++ ++static int precedence(StringRef op) { ++ return StringSwitch(op) ++ .Cases("*", "/", "%", 10) ++ .Cases("+", "-", 9) ++ .Cases("<<", ">>", 8) ++ .Cases("<", "<=", ">", ">=", 7) ++ .Cases("==", "!=", 6) ++ .Case("&", 5) ++ .Case("|", 4) ++ .Case("&&", 3) ++ .Case("||", 2) ++ .Case("?", 1) ++ .Default(-1); ++} ++ ++StringMatcher ScriptParser::readFilePatterns() { ++ StringMatcher Matcher; ++ ++ while (!errorCount() && !consume(")")) ++ Matcher.addPattern(SingleStringMatcher(next())); ++ return Matcher; ++} ++ ++SortSectionPolicy ScriptParser::peekSortKind() { ++ return StringSwitch(peek()) ++ .Cases("SORT", "SORT_BY_NAME", SortSectionPolicy::Name) ++ .Case("SORT_BY_ALIGNMENT", SortSectionPolicy::Alignment) ++ .Case("SORT_BY_INIT_PRIORITY", SortSectionPolicy::Priority) ++ .Case("SORT_NONE", SortSectionPolicy::None) ++ .Default(SortSectionPolicy::Default); ++} ++ ++SortSectionPolicy ScriptParser::readSortKind() { ++ SortSectionPolicy ret = peekSortKind(); ++ if (ret != SortSectionPolicy::Default) ++ skip(); ++ return ret; ++} ++ ++// Reads SECTIONS command contents in the following form: ++// ++// ::= * ++// ::= ? ++// ::= "EXCLUDE_FILE" "(" + ")" ++// ++// For example, ++// ++// *(.foo EXCLUDE_FILE (a.o) .bar EXCLUDE_FILE (b.o) .baz) ++// ++// is parsed as ".foo", ".bar" with "a.o", and ".baz" with "b.o". ++// The semantics of that is section .foo in any file, section .bar in ++// any file but a.o, and section .baz in any file but b.o. ++SmallVector ScriptParser::readInputSectionsList() { ++ SmallVector ret; ++ while (!errorCount() && peek() != ")") { ++ StringMatcher excludeFilePat; ++ if (consume("EXCLUDE_FILE")) { ++ expect("("); ++ excludeFilePat = readFilePatterns(); ++ } ++ ++ StringMatcher SectionMatcher; ++ // Break if the next token is ), EXCLUDE_FILE, or SORT*. ++ while (!errorCount() && peek() != ")" && peek() != "EXCLUDE_FILE" && ++ peekSortKind() == SortSectionPolicy::Default) ++ SectionMatcher.addPattern(unquote(next())); ++ ++ if (!SectionMatcher.empty()) ++ ret.push_back({std::move(excludeFilePat), std::move(SectionMatcher)}); ++ else if (excludeFilePat.empty()) ++ break; ++ else ++ setError("section pattern is expected"); ++ } ++ return ret; ++} ++ ++// Reads contents of "SECTIONS" directive. That directive contains a ++// list of glob patterns for input sections. The grammar is as follows. ++// ++// ::= ++// | "(" ")" ++// | "(" "(" ")" ")" ++// ++// ::= "SORT" | "SORT_BY_NAME" | "SORT_BY_ALIGNMENT" ++// | "SORT_BY_INIT_PRIORITY" | "SORT_NONE" ++// ++// is parsed by readInputSectionsList(). ++InputSectionDescription * ++ScriptParser::readInputSectionRules(StringRef filePattern, uint64_t withFlags, ++ uint64_t withoutFlags) { ++ auto *cmd = ++ make(filePattern, withFlags, withoutFlags); ++ expect("("); ++ ++ while (!errorCount() && !consume(")")) { ++ SortSectionPolicy outer = readSortKind(); ++ SortSectionPolicy inner = SortSectionPolicy::Default; ++ SmallVector v; ++ if (outer != SortSectionPolicy::Default) { ++ expect("("); ++ inner = readSortKind(); ++ if (inner != SortSectionPolicy::Default) { ++ expect("("); ++ v = readInputSectionsList(); ++ expect(")"); ++ } else { ++ v = readInputSectionsList(); ++ } ++ expect(")"); ++ } else { ++ v = readInputSectionsList(); ++ } ++ ++ for (SectionPattern &pat : v) { ++ pat.sortInner = inner; ++ pat.sortOuter = outer; ++ } ++ ++ std::move(v.begin(), v.end(), std::back_inserter(cmd->sectionPatterns)); ++ } ++ return cmd; ++} ++ ++InputSectionDescription * ++ScriptParser::readInputSectionDescription(StringRef tok) { ++ // Input section wildcard can be surrounded by KEEP. ++ // https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep ++ uint64_t withFlags = 0; ++ uint64_t withoutFlags = 0; ++ if (tok == "KEEP") { ++ expect("("); ++ if (consume("INPUT_SECTION_FLAGS")) ++ setError("INPUT_SECTION_FLAGS not supported"); ++ InputSectionDescription *cmd = ++ readInputSectionRules(next(), withFlags, withoutFlags); ++ expect(")"); ++ keptSections.push_back(cmd); ++ return cmd; ++ } ++ if (tok == "INPUT_SECTION_FLAGS") { ++ setError("INPUT_SECTION_FLAGS not supported"); ++ tok = next(); ++ } ++ return readInputSectionRules(tok, withFlags, withoutFlags); ++} ++ ++void ScriptParser::readSort() { ++ expect("("); ++ expect("CONSTRUCTORS"); ++ expect(")"); ++} ++ ++Expr ScriptParser::readAssert() { ++ expect("("); ++ Expr e = readExpr(); ++ expect(","); ++ StringRef msg = unquote(next()); ++ expect(")"); ++ ++ return [=] { ++ if (!e().getValue()) ++ error(msg); ++ return dot; ++ }; ++} ++ ++/* ++#define ECase(X) \ ++ { #X, X } ++constexpr std::pair typeMap[] = { ++ ECase(SHT_PROGBITS), ECase(SHT_NOTE), ECase(SHT_NOBITS), ++ ECase(SHT_INIT_ARRAY), ECase(SHT_FINI_ARRAY), ECase(SHT_PREINIT_ARRAY), ++}; ++#undef ECase ++*/ ++// Tries to read the special directive for an output section definition which ++// can be one of following: "(NOLOAD)", "(COPY)", "(INFO)", "(OVERLAY)", and ++// "(TYPE=)". ++// Tok1 and Tok2 are next 2 tokens peeked. See comment for ++// readSectionAddressType below. ++bool ScriptParser::readSectionDirective(SectionBase *osec, StringRef tok1, StringRef tok2) { ++ if (tok1 != "(") ++ return false; ++ if (tok2 != "NOLOAD" && tok2 != "COPY" && tok2 != "INFO" && ++ tok2 != "OVERLAY" && tok2 != "TYPE") ++ return false; ++ ++ expect("("); ++ setError("section directive " + tok2 + " currently not supported"); ++ if (consume("TYPE")) ++ { expect("="); readExpr(); } ++ else ++ skip(); ++ ++ // cmd = osec->outputSection applies below ++/* if (consume("NOLOAD")) { ++ cmd->type = SHT_NOBITS; ++ cmd->typeIsSet = true; ++ } else if (consume("TYPE")) { ++ expect("="); ++ StringRef value = peek(); ++ auto it = llvm::find_if(typeMap, [=](auto e) { return e.first == value; }); ++ if (it != std::end(typeMap)) { ++ // The value is a recognized literal SHT_*. ++ cmd->type = it->second; ++ skip(); ++ } else if (value.starts_with("SHT_")) { ++ setError("unknown section type " + value); ++ } else { ++ // Otherwise, read an expression. ++ cmd->type = readExpr()().getValue(); ++ } ++ cmd->typeIsSet = true; ++ } else { ++ skip(); // This is "COPY", "INFO" or "OVERLAY". ++ cmd->nonAlloc = true; ++ } ++*/ ++ expect(")"); ++ return true; ++} ++ ++// Reads an expression and/or the special directive for an output ++// section definition. Directive is one of following: "(NOLOAD)", ++// "(COPY)", "(INFO)" or "(OVERLAY)". ++// ++// An output section name can be followed by an address expression ++// and/or directive. This grammar is not LL(1) because "(" can be ++// interpreted as either the beginning of some expression or beginning ++// of directive. ++// ++// https://sourceware.org/binutils/docs/ld/Output-Section-Address.html ++// https://sourceware.org/binutils/docs/ld/Output-Section-Type.html ++void ScriptParser::readSectionAddressType(SectionBase *osec) { ++ // Temporarily set inExpr to support TYPE= without spaces. ++ bool saved = std::exchange(inExpr, true); ++ bool isDirective = readSectionDirective(osec, peek(), peek2()); ++ inExpr = saved; ++ if (isDirective) ++ return; ++ ++ osec->address = readExpr()().getValue(); ++ setError("setting address for " + osec->name + " to " + Twine(osec->address)); ++ if (peek() == "(" && !readSectionDirective(osec, "(", peek2())) ++ setError("unknown section directive: " + peek2()); ++} ++ ++static Expr checkAlignment(Expr e, std::string &loc) { ++ return [=] { ++ uint64_t alignment = std::max((uint64_t)1, e().getValue()); ++ if (!isPowerOf2_64(alignment)) { ++ error(loc + ": alignment must be power of 2"); ++ return (uint64_t)1; // Return a dummy value. ++ } ++ return alignment; ++ }; ++} ++ ++OutputDesc *ScriptParser::readOutputSectionDescription(StringRef outSec) { ++ OutputDesc *cmd = createOutputSection(outSec, getCurrentLocation()); ++ SectionBase *osec = &cmd->osec; ++ // Maybe relro. Will reset to false if DATA_SEGMENT_RELRO_END is absent. ++ //osec->relro = seenDataAlign && !seenRelroEnd; ++ ++ //size_t symbolsReferenced = referencedSymbols.size(); ++ ++ if (peek() != ":") { ++ readSectionAddressType(osec); ++ } ++ expect(":"); ++ ++ std::string location = getCurrentLocation(); ++ if (consume("AT")) ++ //cmd->lmaExpr = readParenExpr(); ++ cmd->osec.address = readParenExpr()().getValue(); ++ if (consume("ALIGN")) ++ //cmd->alignExpr = checkAlignment(readParenExpr(), location); ++ //{ uint64_t align = checkAlignment(readParenExpr(), location)(); ++ // cmd->osec.address = (cmd->osec->address + (align - 1U)) & ~(align - 1U); } ++ setError("setting ALIGN on a section unsupported, align the dot instead"); ++ if (consume("SUBALIGN")) ++ error("SUBALIGN unsupported"); ++ //osec->subalignExpr = checkAlignment(readParenExpr(), location); ++ ++ // Parse constraints. ++ if (consume("ONLY_IF_RO")) ++ setError("constraints like ONLY_IF_RO unsuported"); ++ //osec->constraint = ConstraintKind::ReadOnly; ++ if (consume("ONLY_IF_RW")) ++ setError("constraints like ONLY_IF_RW unsuported"); ++ //osec->constraint = ConstraintKind::ReadWrite; ++ expect("{"); ++ ++ while (!errorCount() && !consume("}")) { ++ StringRef tok = next(); ++ if (tok == ";") { ++ // Empty commands are allowed. Do nothing here. ++ } else if (SymbolAssignment *assign = readAssignment(tok)) { ++ osec->commands.push_back(assign); ++ } else if (ByteCommand *data = readByteCommand(tok)) { ++ osec->commands.push_back(data); ++ } else if (tok == "CONSTRUCTORS") { ++ // CONSTRUCTORS is a keyword to make the linker recognize C++ ctors/dtors ++ // by name. This is for very old file formats such as ECOFF/XCOFF. ++ // For ELF, we should ignore. ++ } else if (tok == "FILL") { ++ // We handle the FILL command as an alias for =fillexp section attribute, ++ // which is different from what GNU linkers do. ++ // https://sourceware.org/binutils/docs/ld/Output-Section-Data.html ++ if (peek() != "(") ++ setError("( expected, but got " + peek()); ++ setError("FILL unsupported"); //osec->filler = readFill(); ++ } else if (tok == "SORT") { ++ readSort(); ++ } else if (tok == "INCLUDE") { ++ setError("INCLUDE not supported"); ++ } else if (tok == "(" || tok == ")") { ++ setError("expected filename pattern"); ++ } else if (peek() == "(") { ++ osec->commands.push_back(readInputSectionDescription(tok)); ++ } else { ++ // We have a file name and no input sections description. It is not a ++ // commonly used syntax, but still acceptable. In that case, all sections ++ // from the file will be included. ++ // FIXME: GNU ld permits INPUT_SECTION_FLAGS to be used here. We do not ++ // handle this case here as it will already have been matched by the ++ // case above. ++ auto *isd = make(tok); ++ isd->sectionPatterns.push_back({{}, StringMatcher("*")}); ++ osec->commands.push_back(isd); ++ } ++ } ++ ++ if (consume(">")) ++ setError("using > not supported"); ++ //osec->memoryRegionName = std::string(next()); ++ ++ if (consume("AT")) { ++ setError("using AT > not supported"); ++ expect(">"); ++ //osec->lmaRegionName = std::string(next()); ++ } ++ ++ //if (osec->lmaExpr && !osec->lmaRegionName.empty()) ++ // error("section can't have both LMA and a load region"); ++ ++ //osec->phdrs = readOutputSectionPhdrs(); ++ ++ if (peek() == "=" || peek().starts_with("=")) { ++ inExpr = true; ++ consume("="); ++ setError("filler unsupported"); ++ //osec->filler = readFill(); ++ inExpr = false; ++ } ++ ++ // Consume optional comma following output section command. ++ consume(","); ++ ++ //if (referencedSymbols.size() > symbolsReferenced) ++ // osec->expressionsUseSymbols = true; ++ return cmd; ++} ++ ++// Reads a `=` expression and returns its value as a big-endian number. ++// https://sourceware.org/binutils/docs/ld/Output-Section-Fill.html ++// We do not support using symbols in such expressions. ++// ++// When reading a hexstring, ld.bfd handles it as a blob of arbitrary ++// size, while ld.gold always handles it as a 32-bit big-endian number. ++// We are compatible with ld.gold because it's easier to implement. ++// Also, we require that expressions with operators must be wrapped into ++// round brackets. We did it to resolve the ambiguity when parsing scripts like: ++// SECTIONS { .foo : { ... } =120+3 /DISCARD/ : { ... } } ++std::array ScriptParser::readFill() { ++ uint64_t value = readPrimary()().val; ++ if (value > UINT32_MAX) ++ setError("filler expression result does not fit 32-bit: 0x" + ++ Twine::utohexstr(value)); ++ ++ std::array buf; ++ write32be(buf.data(), (uint32_t)value); ++ return buf; ++} ++ ++SymbolAssignment *ScriptParser::readProvideHidden(bool provide, bool hidden) { ++ expect("("); ++ StringRef name = next(), eq = peek(); ++ if (eq != "=") { ++ setError("= expected, but got " + next()); ++ while (!atEOF() && next() != ")") ++ ; ++ return nullptr; ++ } ++ SymbolAssignment *cmd = readSymbolAssignment(name); ++ cmd->provide = provide; ++ cmd->hidden = hidden; ++ expect(")"); ++ return cmd; ++} ++ ++SymbolAssignment *ScriptParser::readAssignment(StringRef tok) { ++ // Assert expression returns Dot, so this is equal to ".=." ++ if (tok == "ASSERT") ++ return make(".", readAssert(), getCurrentLocation()); ++ ++ size_t oldPos = pos; ++ SymbolAssignment *cmd = nullptr; ++ const StringRef op = peek(); ++ if (op.starts_with("=")) { ++ // Support = followed by an expression without whitespace. ++ SaveAndRestore saved(inExpr, true); ++ cmd = readSymbolAssignment(tok); ++ } else if ((op.size() == 2 && op[1] == '=' && strchr("*/+-&|", op[0])) || ++ op == "<<=" || op == ">>=") { ++ cmd = readSymbolAssignment(tok); ++ } else if (tok == "PROVIDE") { ++ SaveAndRestore saved(inExpr, true); ++ cmd = readProvideHidden(true, false); ++ } else if (tok == "HIDDEN") { ++ SaveAndRestore saved(inExpr, true); ++ cmd = readProvideHidden(false, true); ++ } else if (tok == "PROVIDE_HIDDEN") { ++ SaveAndRestore saved(inExpr, true); ++ cmd = readProvideHidden(true, true); ++ } ++ ++ if (cmd) { ++ cmd->commandString = ++ tok.str() + " " + ++ llvm::join(tokens.begin() + oldPos, tokens.begin() + pos, " "); ++ expect(";"); ++ } ++ return cmd; ++} ++ ++SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef name) { ++ name = unquote(name); ++ StringRef op = next(); ++ assert(op == "=" || op == "*=" || op == "/=" || op == "+=" || op == "-=" || ++ op == "&=" || op == "|=" || op == "<<=" || op == ">>="); ++ // Note: GNU ld does not support %= or ^=. ++ Expr e = readExpr(); ++ if (op != "=") { ++ std::string loc = getCurrentLocation(); ++ e = [=, c = op[0]]() -> ExprValue { ++ ExprValue lhs = getSymbolValue(name, loc); ++ switch (c) { ++ case '*': ++ return lhs.getValue() * e().getValue(); ++ case '/': ++ if (uint64_t rv = e().getValue()) ++ return lhs.getValue() / rv; ++ error(loc + ": division by zero"); ++ return 0; ++ case '+': ++ return add(lhs, e()); ++ case '-': ++ return sub(lhs, e()); ++ case '<': ++ return lhs.getValue() << e().getValue(); ++ case '>': ++ return lhs.getValue() >> e().getValue(); ++ case '&': ++ return lhs.getValue() & e().getValue(); ++ case '|': ++ return lhs.getValue() | e().getValue(); ++ default: ++ llvm_unreachable(""); ++ } ++ }; ++ } ++ return make(name, e, getCurrentLocation()); ++} ++ ++// This is an operator-precedence parser to parse a linker ++// script expression. ++Expr ScriptParser::readExpr() { ++ // Our lexer is context-aware. Set the in-expression bit so that ++ // they apply different tokenization rules. ++ bool orig = inExpr; ++ inExpr = true; ++ Expr e = readExpr1(readPrimary(), 0); ++ inExpr = orig; ++ return e; ++} ++ ++Expr ScriptParser::combine(StringRef op, Expr l, Expr r) { ++ if (op == "+") ++ return [=] { return add(l(), r()); }; ++ if (op == "-") ++ return [=] { return sub(l(), r()); }; ++ if (op == "*") ++ return [=] { return l().getValue() * r().getValue(); }; ++ if (op == "/") { ++ std::string loc = getCurrentLocation(); ++ return [=]() -> uint64_t { ++ if (uint64_t rv = r().getValue()) ++ return l().getValue() / rv; ++ error(loc + ": division by zero"); ++ return 0; ++ }; ++ } ++ if (op == "%") { ++ std::string loc = getCurrentLocation(); ++ return [=]() -> uint64_t { ++ if (uint64_t rv = r().getValue()) ++ return l().getValue() % rv; ++ error(loc + ": modulo by zero"); ++ return 0; ++ }; ++ } ++ if (op == "<<") ++ return [=] { return l().getValue() << r().getValue(); }; ++ if (op == ">>") ++ return [=] { return l().getValue() >> r().getValue(); }; ++ if (op == "<") ++ return [=] { return l().getValue() < r().getValue(); }; ++ if (op == ">") ++ return [=] { return l().getValue() > r().getValue(); }; ++ if (op == ">=") ++ return [=] { return l().getValue() >= r().getValue(); }; ++ if (op == "<=") ++ return [=] { return l().getValue() <= r().getValue(); }; ++ if (op == "==") ++ return [=] { return l().getValue() == r().getValue(); }; ++ if (op == "!=") ++ return [=] { return l().getValue() != r().getValue(); }; ++ if (op == "||") ++ return [=] { return l().getValue() || r().getValue(); }; ++ if (op == "&&") ++ return [=] { return l().getValue() && r().getValue(); }; ++ if (op == "&") ++ return [=] { return bitAnd(l(), r()); }; ++ if (op == "|") ++ return [=] { return bitOr(l(), r()); }; ++ llvm_unreachable("invalid operator"); ++} ++ ++// This is a part of the operator-precedence parser. This function ++// assumes that the remaining token stream starts with an operator. ++Expr ScriptParser::readExpr1(Expr lhs, int minPrec) { ++ while (!atEOF() && !errorCount()) { ++ // Read an operator and an expression. ++ StringRef op1 = peek(); ++ if (precedence(op1) < minPrec) ++ break; ++ if (consume("?")) ++ return readTernary(lhs); ++ skip(); ++ Expr rhs = readPrimary(); ++ ++ // Evaluate the remaining part of the expression first if the ++ // next operator has greater precedence than the previous one. ++ // For example, if we have read "+" and "3", and if the next ++ // operator is "*", then we'll evaluate 3 * ... part first. ++ while (!atEOF()) { ++ StringRef op2 = peek(); ++ if (precedence(op2) <= precedence(op1)) ++ break; ++ rhs = readExpr1(rhs, precedence(op2)); ++ } ++ ++ lhs = combine(op1, lhs, rhs); ++ } ++ return lhs; ++} ++ ++Expr ScriptParser::getPageSize() { ++ return [] { return 0xFFFF; }; // Wasm page size is 65k. ++} ++ ++Expr ScriptParser::readConstant() { ++ StringRef s = readParenLiteral(); ++ if (s == "COMMONPAGESIZE") ++ return getPageSize(); ++ if (s == "MAXPAGESIZE") ++ return getPageSize(); ++ setError("unknown constant: " + s); ++ return [] { return 0; }; ++} ++ ++// Parses Tok as an integer. It recognizes hexadecimal (prefixed with ++// "0x" or suffixed with "H") and decimal numbers. Decimal numbers may ++// have "K" (Ki) or "M" (Mi) suffixes. ++static std::optional parseInt(StringRef tok) { ++ // Hexadecimal ++ uint64_t val; ++ if (tok.starts_with_insensitive("0x")) { ++ if (!to_integer(tok.substr(2), val, 16)) ++ return std::nullopt; ++ return val; ++ } ++ if (tok.ends_with_insensitive("H")) { ++ if (!to_integer(tok.drop_back(), val, 16)) ++ return std::nullopt; ++ return val; ++ } ++ ++ // Decimal ++ if (tok.ends_with_insensitive("K")) { ++ if (!to_integer(tok.drop_back(), val, 10)) ++ return std::nullopt; ++ return val * 1024; ++ } ++ if (tok.ends_with_insensitive("M")) { ++ if (!to_integer(tok.drop_back(), val, 10)) ++ return std::nullopt; ++ return val * 1024 * 1024; ++ } ++ if (!to_integer(tok, val, 10)) ++ return std::nullopt; ++ return val; ++} ++ ++ByteCommand *ScriptParser::readByteCommand(StringRef tok) { ++ int size = StringSwitch(tok) ++ .Case("BYTE", 1) ++ .Case("SHORT", 2) ++ .Case("LONG", 4) ++ .Case("QUAD", 8) ++ .Default(-1); ++ if (size == -1) ++ return nullptr; ++ ++ size_t oldPos = pos; ++ Expr e = readParenExpr(); ++ std::string commandString = ++ tok.str() + " " + ++ llvm::join(tokens.begin() + oldPos, tokens.begin() + pos, " "); ++ return make(e, size, commandString); ++} ++ ++StringRef ScriptParser::readParenLiteral() { ++ expect("("); ++ bool orig = inExpr; ++ inExpr = false; ++ StringRef tok = next(); ++ inExpr = orig; ++ expect(")"); ++ return tok; ++} ++ ++static void checkIfExists(const SectionBase &osec, StringRef location) { ++ if (osec.location.empty()) ++ error(location + ": undefined section " + osec.name); ++} ++ ++static bool isValidSymbolName(StringRef s) { ++ auto valid = [](char c) { ++ return isAlnum(c) || c == '$' || c == '.' || c == '_'; ++ }; ++ return !s.empty() && !isDigit(s[0]) && llvm::all_of(s, valid); ++} ++ ++Expr ScriptParser::readPrimary() { ++ if (peek() == "(") ++ return readParenExpr(); ++ ++ if (consume("~")) { ++ Expr e = readPrimary(); ++ return [=] { return ~e().getValue(); }; ++ } ++ if (consume("!")) { ++ Expr e = readPrimary(); ++ return [=] { return !e().getValue(); }; ++ } ++ if (consume("-")) { ++ Expr e = readPrimary(); ++ return [=] { return -e().getValue(); }; ++ } ++ ++ StringRef tok = next(); ++ std::string location = getCurrentLocation(); ++ ++ // Built-in functions are parsed here. ++ // https://sourceware.org/binutils/docs/ld/Builtin-Functions.html. ++ if (tok == "ABSOLUTE") { ++ Expr inner = readParenExpr(); ++ return [=] { ++ ExprValue i = inner(); ++ i.forceAbsolute = true; ++ return i; ++ }; ++ } ++ if (tok == "ADDR") { ++ StringRef name = readParenLiteral(); ++ SectionBase *osec = &getOrCreateOutputSection(name)->osec; ++ //osec->usedInExpression = true; ++ return [=]() -> ExprValue { ++ checkIfExists(*osec, location); ++ return {osec, false, 0, location}; ++ }; ++ } ++ if (tok == "ALIGN") { ++ expect("("); ++ Expr e = readExpr(); ++ if (consume(")")) { ++ e = checkAlignment(e, location); ++ return [=] { return alignToPowerOf2(dot, e().getValue()); }; ++ } ++ expect(","); ++ Expr e2 = checkAlignment(readExpr(), location); ++ expect(")"); ++ return [=] { ++ ExprValue v = e(); ++ v.alignment = e2().getValue(); ++ return v; ++ }; ++ } ++ if (tok == "ALIGNOF") { ++ setError("ALIGNOF unsupported"); ++ StringRef name = readParenLiteral(); ++ SectionBase *osec = &getOrCreateOutputSection(name)->osec; ++ return [=] { ++ checkIfExists(*osec, location); ++ return 0;//osec->addralign; ++ }; ++ } ++ if (tok == "ASSERT") ++ return readAssert(); ++ if (tok == "CONSTANT") ++ return readConstant(); ++ if (tok == "DATA_SEGMENT_ALIGN") { ++ expect("("); ++ Expr e = readExpr(); ++ expect(","); ++ readExpr(); ++ expect(")"); ++ seenDataAlign = true; ++ return [=] { ++ uint64_t align = std::max(uint64_t(1), e().getValue()); ++ return (dot + align - 1) & -align; ++ }; ++ } ++ if (tok == "DATA_SEGMENT_END") { ++ expect("("); ++ expect("."); ++ expect(")"); ++ return [=] { return dot; }; // = added ++ } ++ if (tok == "DATA_SEGMENT_RELRO_END") { ++ setError("unsupported DATA_SEGMENT_RELRO_END"); ++ ++ // GNU linkers implements more complicated logic to handle ++ // DATA_SEGMENT_RELRO_END. We instead ignore the arguments and ++ // just align to the next page boundary for simplicity. ++ expect("("); ++ readExpr(); ++ expect(","); ++ readExpr(); ++ expect(")"); ++ seenRelroEnd = true; ++ Expr e = getPageSize(); ++ return [=] { return alignToPowerOf2(dot, e().getValue()); }; ++ } ++ if (tok == "DEFINED") { ++ StringRef name = unquote(readParenLiteral()); ++ return [=] { ++ Symbol *b = symtab->find(name); ++ return (b && b->isDefined()) ? 1 : 0; ++ }; ++ } ++ if (tok == "LENGTH") { ++ setError("LENGTH command not supported (no memory region support)"); ++ return 0; ++ } ++ if (tok == "LOADADDR") { ++ setError("LOADADDR unsuppported"); ++ /* ++ StringRef name = readParenLiteral(); ++ OutputSection *osec = &getOrCreateOutputSection(name)->osec; ++ osec->usedInExpression = true; ++ return [=] { ++ checkIfExists(*osec, location); ++ return osec->getLMA(); ++ }; ++ */ ++ } ++ if (tok == "LOG2CEIL") { ++ expect("("); ++ Expr a = readExpr(); ++ expect(")"); ++ return [=] { ++ // LOG2CEIL(0) is defined to be 0. ++ return llvm::Log2_64_Ceil(std::max(a().getValue(), UINT64_C(1))); ++ }; ++ } ++ if (tok == "MAX" || tok == "MIN") { ++ expect("("); ++ Expr a = readExpr(); ++ expect(","); ++ Expr b = readExpr(); ++ expect(")"); ++ if (tok == "MIN") ++ return [=] { return std::min(a().getValue(), b().getValue()); }; ++ return [=] { return std::max(a().getValue(), b().getValue()); }; ++ } ++ if (tok == "ORIGIN") { ++ setError("ORIGIN command not supported (no memory region support)"); ++ return 0; ++ } ++ if (tok == "SEGMENT_START") { ++ expect("("); ++ skip(); ++ expect(","); ++ Expr e = readExpr(); ++ expect(")"); ++ return [=] { return e(); }; ++ } ++ if (tok == "SIZEOF") { ++ setError("SIZEOF unsupported"); ++ //StringRef name = readParenLiteral(); ++ //SectionBase *cmd = &getOrCreateOutputSection(name)->osec; ++ // Linker script does not create an output section if its content is empty. ++ // We want to allow SIZEOF(.foo) where .foo is a section which happened to ++ // be empty. ++ return [=] { return 0;/*cmd->size;*/ }; ++ } ++ if (tok == "SIZEOF_HEADERS") ++ return [=] { return /*elf::getHeaderSize();*/ 0; }; ++ ++ // Tok is the dot. ++ if (tok == ".") ++ return [=] { return getSymbolValue(tok, location); }; ++ ++ // Tok is a literal number. ++ if (std::optional val = parseInt(tok)) ++ return [=] { return *val; }; ++ ++ // Tok is a symbol name. ++ if (tok.starts_with("\"")) ++ tok = unquote(tok); ++ else if (!isValidSymbolName(tok)) ++ setError("malformed number: " + tok); ++ //referencedSymbols.push_back(tok); ++ return [=] { return getSymbolValue(tok, location); }; ++} ++ ++Expr ScriptParser::readTernary(Expr cond) { ++ Expr l = readExpr(); ++ expect(":"); ++ Expr r = readExpr(); ++ return [=] { return cond().getValue() ? l() : r(); }; ++} ++ ++Expr ScriptParser::readParenExpr() { ++ expect("("); ++ Expr e = readExpr(); ++ expect(")"); ++ return e; ++} ++ ++OutputDesc *ScriptParser::createOutputSection(StringRef name, ++ StringRef location) { ++ OutputDesc *&secRef = nameToOutputSection[CachedHashStringRef(name)]; ++ OutputDesc *sec; ++ if (secRef && secRef->osec.location.empty()) { ++ // There was a forward reference. ++ sec = secRef; ++ } else { ++ sec = make(name); ++ if (!secRef) ++ secRef = sec; ++ } ++ sec->osec.location = std::string(location); ++ return sec; ++} ++ ++OutputDesc *ScriptParser::getOrCreateOutputSection(StringRef name) { ++ OutputDesc *&cmdRef = nameToOutputSection[CachedHashStringRef(name)]; ++ if (!cmdRef) ++ cmdRef = make(name); ++ return cmdRef; ++} ++ ++ExprValue ScriptParser::getSymbolValue(StringRef name, const Twine &loc) { ++ if (name == ".") { ++ //if (state) ++ // return {state->outSec, false, dot - state->outSec->addr, loc}; ++ return {nullptr, false, dot, loc}; ++ //error(loc + ": unable to get location counter value"); ++ //return 0; ++ } ++ ++ if (Symbol *sym = symtab->find(name)) { ++ if (auto *ds = dyn_cast(sym)) { ++ // A bit of a hack to support aliases outside of SECTIONS. ++ // This only works if the evaluation happpens after placement into the output. ++ uint64_t offset = ds->segment && ds->segment->outputSeg ? ds->segment->outputSeg->startVA + ds->segment->outputSegmentOffset : ds->value; ++ ExprValue v{nullptr, false, offset, loc}; ++ // Retain the original st_type, so that the alias will get the same ++ // behavior in relocation processing. Any operation will reset st_type to ++ // STT_NOTYPE. ++ // v.type = ds->type; ++ return v; ++ } ++ //if (isa(sym)) ++ // if (!errorOnMissingSection) ++ // return {nullptr, false, 0, loc}; ++ } ++ ++ error(loc + ": symbol not found: " + name); ++ return 0; ++} +diff --git a/lld/wasm/ScriptParser.h b/lld/wasm/ScriptParser.h +new file mode 100644 +index 000000000..c0a845e65 +--- /dev/null ++++ b/lld/wasm/ScriptParser.h +@@ -0,0 +1,341 @@ ++//===- ScriptParser.h -------------------------------------------*- C++ -*-===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++ ++#ifndef LLD_WASM_SCRIPT_PARSER_H ++#define LLD_WASM_SCRIPT_PARSER_H ++ ++#include "ScriptParser.h" ++#include "OutputSections.h" ++#include "ScriptLexer.h" ++#include "SymbolTable.h" ++#include "Symbols.h" ++#include "lld/Common/CommonLinkerContext.h" ++#include "lld/Common/Strings.h" ++#include "llvm/ADT/SmallString.h" ++#include "llvm/ADT/StringRef.h" ++#include ++#include ++#include ++ ++namespace lld::wasm { ++ ++// This enum is used to implement linker script SECTIONS command. ++// https://sourceware.org/binutils/docs/ld/SECTIONS.html#SECTIONS ++enum SectionsCommandKind { ++ AssignmentKind, // . = expr or = expr ++ OutputSectionKind, ++ InputSectionKind, ++ ByteKind // BYTE(expr), SHORT(expr), LONG(expr) or QUAD(expr) ++}; ++ ++struct SectionCommand { ++ SectionCommand(int k) : kind(k) {} ++ int kind; ++}; ++ ++class SectionBase { ++public: ++/* ++ enum Kind { Regular, Synthetic, EHFrame, Merge, Output }; ++ ++ Kind kind() const { return (Kind)sectionKind; } ++ ++ uint8_t sectionKind : 3; ++ ++ // The next two bit fields are only used by InputSectionBase, but we ++ // put them here so the struct packs better. ++ ++ uint8_t bss : 1; ++ ++ // Set for sections that should not be folded by ICF. ++ uint8_t keepUnique : 1; ++ ++ uint8_t partition = 1; ++*/ ++// uint32_t type; ++ //union { ++ OutputSection *outputSection; ++ //InputChunk *inputChunk; ++ //}; ++ ++ StringRef name; ++ ++ uint64_t address; ++ //uint32_t addralign; ++ bool live; ++ ++ SmallVector commands; ++ std::string location; ++ ++/* ++ // The 1-indexed partition that this section is assigned to by the garbage ++ // collector, or 0 if this section is dead. Normally there is only one ++ // partition, so this will either be 0 or 1. ++ elf::Partition &getPartition() const; ++ ++ // These corresponds to the fields in Elf_Shdr. ++ uint64_t flags; ++ uint32_t addralign; ++ uint32_t entsize; ++ uint32_t link; ++ uint32_t info; ++*/ ++ ++ OutputSection *getOutputSection() { return outputSection; } ++ const OutputSection *getOutputSection() const { ++ return const_cast(this)->getOutputSection(); ++ } ++ ++ // Translate an offset in the input section to an offset in the output ++ // section. ++ uint64_t getOffset(uint64_t offset) const { return offset; } ++ ++ uint64_t getVA(uint64_t offset = 0) const { return offset; }; ++ ++ bool isLive() const { return live; } //return partition != 0; } ++ void markLive() { live = 1; } ++ void markDead() { live = 0; } ++ ++ SectionBase(OutputSection *osec) : outputSection(osec), name(osec->name) {} ++ ++/* ++protected: ++ constexpr SectionBase(/*Kind sectionKind,/ StringRef name, uint64_t flags, ++ uint32_t entsize, uint32_t addralign, uint32_t type, ++ uint32_t info, uint32_t link) ++ : name (name) {} ++// : sectionKind(sectionKind), bss(false), keepUnique(false), type(type), ++// name(name), flags(flags), addralign(addralign), entsize(entsize), ++// link(link), info(info) {} ++*/ ++}; ++ ++// This represents an r-value in the linker script. ++struct ExprValue { ++ ExprValue(SectionBase *sec, bool forceAbsolute, uint64_t val, ++ const Twine &loc) ++ : sec(sec), val(val), forceAbsolute(forceAbsolute), loc(loc.str()) {} ++ ++ ExprValue(uint64_t val) : ExprValue(nullptr, false, val, "") {} ++ ++ bool isAbsolute() const { return forceAbsolute || sec == nullptr; } ++ uint64_t getValue() const; ++ uint64_t getSecAddr() const; ++ uint64_t getSectionOffset() const; ++ ++ // If a value is relative to a section, it has a non-null Sec. ++ SectionBase *sec; ++ ++ uint64_t val; ++ uint64_t alignment = 1; ++ ++ // True if this expression is enclosed in ABSOLUTE(). ++ // This flag affects the return value of getValue(). ++ bool forceAbsolute; ++ ++ // Original source location. Used for error messages. ++ std::string loc; ++}; ++ ++// This represents an expression in the linker script. ++// ScriptParser::readExpr reads an expression and returns an Expr. ++// Later, we evaluate the expression by calling the function. ++using Expr = std::function; ++ ++// This represents ". = " or " = ". ++struct SymbolAssignment : SectionCommand { ++ SymbolAssignment(StringRef name, Expr e, std::string loc) ++ : SectionCommand(AssignmentKind), name(name), expression(e), ++ location(loc) {} ++ ++ static bool classof(const SectionCommand *c) { ++ return c->kind == AssignmentKind; ++ } ++ ++ // The LHS of an expression. Name is either a symbol name or ".". ++ StringRef name; ++ DefinedData *sym = nullptr; ++ ++ // The RHS of an expression. ++ Expr expression; ++ ++ // Command attributes for PROVIDE, HIDDEN and PROVIDE_HIDDEN. ++ bool provide = false; ++ bool hidden = false; ++ ++ // Holds file name and line number for error reporting. ++ std::string location; ++ ++ // A string representation of this command. We use this for -Map. ++ std::string commandString; ++ ++ // Address of this assignment command. ++ uint64_t addr; ++ ++ // Size of this assignment command. This is usually 0, but if ++ // you move '.' this may be greater than 0. ++ uint64_t size; ++}; ++ ++struct OutputDesc final : SectionCommand { ++ SectionBase osec; ++ explicit OutputDesc(StringRef name) ++ : SectionCommand(OutputSectionKind), osec(make(ArrayRef())) { ++ osec.name = name; ++ } ++ ++ static bool classof(const SectionCommand *c) { ++ return c->kind == OutputSectionKind; ++ } ++}; ++ ++// For --sort-section and linkerscript sorting rules. ++enum class SortSectionPolicy { Default, None, Alignment, Name, Priority }; ++ ++// This struct represents one section match pattern in SECTIONS() command. ++// It can optionally have negative match pattern for EXCLUDED_FILE command. ++// Also it may be surrounded with SORT() command, so contains sorting rules. ++class SectionPattern { ++ ++ // Cache of the most recent input argument and result of excludesFile(). ++ mutable std::optional> excludesFileCache; ++ ++public: ++ SectionPattern(StringMatcher &&pat1, StringMatcher &&pat2) ++ : excludedFilePat(pat1), sectionPat(pat2), ++ sortOuter(SortSectionPolicy::Default), ++ sortInner(SortSectionPolicy::Default) {} ++ ++ bool excludesFile(const InputFile *file) const; ++ ++ StringMatcher excludedFilePat; ++ StringMatcher sectionPat; ++ SortSectionPolicy sortOuter; ++ SortSectionPolicy sortInner; ++}; ++ ++class InputSectionDescription : public SectionCommand { ++ // Cache of the most recent input argument and result of matchesFile(). ++ mutable std::optional> matchesFileCache; ++ ++public: ++ InputSectionDescription(StringRef filePattern, uint64_t withFlags = 0, ++ uint64_t withoutFlags = 0) ++ : SectionCommand(InputSectionKind), filePat(filePattern), ++ withFlags(withFlags), withoutFlags(withoutFlags) {} ++ ++ static bool classof(const SectionCommand *c) { ++ return c->kind == InputSectionKind; ++ } ++ ++ bool matchesFile(const InputFile *file) const; ++ ++ SingleStringMatcher filePat; ++ ++ // Input sections that matches at least one of SectionPatterns ++ // will be associated with this InputSectionDescription. ++ SmallVector sectionPatterns; ++ ++ // Includes InputSections and MergeInputSections. Used temporarily during ++ // assignment of input sections to output sections. ++ //SmallVector sectionBases; ++ ++ // Used after the finalizeInputSections() pass. MergeInputSections have been ++ // merged into MergeSyntheticSections. ++ SmallVector sections; ++ ++ // Temporary record of synthetic ThunkSection instances and the pass that ++ // they were created in. This is used to insert newly created ThunkSections ++ // into Sections at the end of a createThunks() pass. ++ //SmallVector, 0> thunkSections; ++ ++ // SectionPatterns can be filtered with the INPUT_SECTION_FLAGS command. ++ uint64_t withFlags; ++ uint64_t withoutFlags; ++}; ++ ++// Represents BYTE(), SHORT(), LONG(), or QUAD(). ++struct ByteCommand : SectionCommand { ++ ByteCommand(Expr e, unsigned size, std::string commandString) ++ : SectionCommand(ByteKind), commandString(commandString), expression(e), ++ size(size) {} ++ ++ static bool classof(const SectionCommand *c) { return c->kind == ByteKind; } ++ ++ // Keeps string representing the command. Used for -Map" is perhaps better. ++ std::string commandString; ++ ++ Expr expression; ++ ++ // This is just an offset of this assignment command in the output section. ++ unsigned offset; ++ ++ // Size of this data command. ++ unsigned size; ++}; ++ ++class ScriptParser final : ScriptLexer { ++public: ++ ScriptParser(MemoryBufferRef mb) : ScriptLexer(mb) { } ++ ++ void readLinkerScript(); ++ ++private: ++ void readOutput(); ++ void readSections(); ++ ++ SymbolAssignment *readSymbolAssignment(StringRef name); ++ ByteCommand *readByteCommand(StringRef tok); ++ std::array readFill(); ++ bool readSectionDirective(SectionBase *osec, StringRef tok1, StringRef tok2); ++ void readSectionAddressType(SectionBase *osec); ++ OutputDesc *readOutputSectionDescription(StringRef outSec); ++ InputSectionDescription *readInputSectionDescription(StringRef tok); ++ StringMatcher readFilePatterns(); ++ SmallVector readInputSectionsList(); ++ InputSectionDescription *readInputSectionRules(StringRef filePattern, ++ uint64_t withFlags, ++ uint64_t withoutFlags); ++ SortSectionPolicy peekSortKind(); ++ SortSectionPolicy readSortKind(); ++ SymbolAssignment *readProvideHidden(bool provide, bool hidden); ++ SymbolAssignment *readAssignment(StringRef tok); ++ void readSort(); ++ Expr readAssert(); ++ Expr readConstant(); ++ Expr getPageSize(); ++ ++ Expr combine(StringRef op, Expr l, Expr r); ++ Expr readExpr(); ++ Expr readExpr1(Expr lhs, int minPrec); ++ StringRef readParenLiteral(); ++ Expr readPrimary(); ++ Expr readTernary(Expr cond); ++ Expr readParenExpr(); ++ ++ bool seenDataAlign = false; ++ bool seenRelroEnd = false; ++ ++ // Moved from LinkerScript to here: ++ ++ OutputDesc *createOutputSection(StringRef name, StringRef location); ++ OutputDesc *getOrCreateOutputSection(StringRef name); ++ ExprValue getSymbolValue(StringRef name, const Twine &loc); ++ ++public: ++ uint64_t dot = 0; ++ //SmallVector referencedSymbols; ++ SmallVector sectionCommands; ++ bool hasSectionsCommand = false; ++ SmallVector keptSections; ++ llvm::DenseMap nameToOutputSection; ++}; ++ ++} ++ ++#endif +diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp +index d1a06c9ac..3f718a823 100644 +--- a/lld/wasm/Writer.cpp ++++ b/lld/wasm/Writer.cpp +@@ -14,6 +14,7 @@ + #include "OutputSections.h" + #include "OutputSegment.h" + #include "Relocations.h" ++#include "ScriptParser.h" + #include "SymbolTable.h" + #include "SyntheticSections.h" + #include "WriterUtils.h" +@@ -92,6 +93,7 @@ private: + OutputSegment *createOutputSegment(StringRef name); + void combineOutputSegments(); + void layoutMemory(); ++ void runScript(); + void createHeader(); + + void addSection(OutputSection *sec); +@@ -499,6 +501,231 @@ void Writer::layoutMemory() { + } + } + ++void Writer::runScript() { ++ if (ctx.isPic || config->relocatable || config->globalBase) { ++ error("any kind of position independent/dynamic code can't be used with manual memory layout"); ++ } else if (config->stackFirst) { ++ error("--stack-first can't be used with manual memory config (place it manually instead)"); ++ } ++ ++ llvm::SmallVector inputSegments; ++ for (ObjFile *file : ctx.objectFiles) { ++ for (InputChunk *segment : file->segments) { ++ if (!segment->live) ++ continue; ++ ++ inputSegments.push_back(segment); ++ } ++ } ++ ++ // Place segments using linker script. Also assign symbols. ++ uint64_t memoryPtr = 0; ++ { ++ llvm::TimeTraceScope timeScope("Run linker script", ++ config->linkerScript->getBufferIdentifier()); ++ ScriptParser parser{*config->linkerScript}; ++ parser.readLinkerScript(); ++ ++ auto handleScriptSymbol = [&] (SymbolAssignment* assign, bool inSec) { ++ StringRef name = assign->name; ++ if (name != ".") { ++ if (!isValidCIdentifier(name)) ++ return; ++ ++ assign->addr = parser.dot; ++ ExprValue v = assign->expression(); ++ uint64_t value = v.isAbsolute() ? v.getValue() : v.getSectionOffset(); ++ log("SCRIPT SET " + name + " to " + Twine(value) + ", dot was " + Twine(parser.dot)); ++ symtab->addOptionalDataSymbol(saver().save(name), value); ++ LLVM_DEBUG(dbgs() << "setSymbolAssignment: " << name << "\n"); ++ } else { //if (assign->sym) { ++ //if (inSec) { ++ // error("Assigning to . inside section is currently not supported"); ++ //} ++ ++ uint64_t val = assign->expression().getValue(); ++ if (val < parser.dot) ++ error(assign->location + ": unable to move location counter backward for: " + name); ++ ++ log("SCRIPT DOT " + name + " from " + Twine(parser.dot) + " to " + Twine(val)); ++ parser.dot = val; ++ LLVM_DEBUG(dbgs() << "dotSymbolAssignment: " << parser.dot << "\n"); ++ } ++ }; ++ ++ auto nameComparator = [](InputChunk *a, InputChunk *b) { ++ return a->name < b->name; ++ }; ++ ++ // Output sections need to have unique names. ++ // Example: ++ // osec->name: .rodata ++ // segment->name: .rodata.123 ++ // segment->inputSegments: vector of InputChunk:s with names: ++ // .rodata.foo ++ // .rodata.foo (yes, again) ++ // .rodata.bar ++ // .my.custom.name (i.e. does not have to start with e.g. .rodata) ++ size_t osecUid = 0; ++ for (SectionCommand *base : parser.sectionCommands) { ++ if (auto *osd = dyn_cast(base)) { ++ SectionBase *osec = &osd->osec; ++ ++ for (SectionCommand *cmd : osec->commands) { ++ if (auto *assign = dyn_cast(cmd)) { ++ handleScriptSymbol(assign, true); ++ } else if (auto *isd = dyn_cast(cmd)) { ++ // If dot is assigned or read while matching, we need to have new OutputSegments, ++ // so that the startVA can move (and the assignments will work). This means that ++ // there can be several output segments with the same name (a bit unfortunate). ++ OutputSegment *segment = make( ++ saver().save(osec->name + "." + Twine(osecUid++))); ++ segment->isBss = osec->name.starts_with(".bss"); ++ if (config->sharedMemory) ++ segment->initFlags = WASM_DATA_SEGMENT_IS_PASSIVE; ++ ++ for (const SectionPattern &pat : isd->sectionPatterns) { ++ if (!isd->filePat.isTrivialMatchAll() || !pat.excludedFilePat.empty()) ++ error("Only trivial wildcard patterns are supported for file (i.e. *), no excludes"); ++ ++ if (pat.sortInner != SortSectionPolicy::Default && ++ pat.sortInner != SortSectionPolicy::None) ++ error("Only one level of sorting currently supported in linker scripts"); ++ ++ if (pat.sortOuter != SortSectionPolicy::Default && ++ pat.sortOuter != SortSectionPolicy::None && ++ pat.sortOuter != SortSectionPolicy::Name) ++ error("Only sorting on name is currently supported in linker scripts"); ++ ++ auto sortStart = segment->inputSegments.end(); ++ for (InputChunk *chunk : inputSegments) { ++ // If an input is matched once, never match it again! (This is by spec.) ++ if (chunk->outputSeg) // Set by addInputSegment() below. ++ continue; ++ ++ if (!pat.sectionPat.match(chunk->name)) ++ //|| !isd->matchesFile(sec->file) || pat.excludesFile(sec->file)) ++ continue; ++ ++ log("MAPPING " + segment->name + " <--- " + chunk->name); ++ if (osec->name == "/DISCARD/") { ++ // The output section name `/DISCARD/' is special. ++ // Any input section assigned to it is discarded. ++ chunk->discarded = true; ++ } else { ++ segment->addInputSegment(chunk); // Sets chunk->outputSeg. ++ assert(chunk->outputSeg); ++ } ++ } ++ auto sortEnd = segment->inputSegments.end(); ++ ++ // Sorting happens on each pattern, for example *(.foo SORT(.bar.*) .baz) ++ if (pat.sortOuter == SortSectionPolicy::Name) ++ std::stable_sort(sortStart, sortEnd, nameComparator); ++ } ++ ++ if (osec->name != "/DISCARD/" && !segment->inputSegments.empty()) { ++ // The linker script will align dot directly itself. However, we might have to ++ // increase the alignment to what came from the input files, moving the dot too. ++ segment->finalizeInputSegments(); // Bake everything, so that we know the size. ++ log("SCRIPT PLACE " + segment->name + " with size " + Twine(segment->size) + ++ " dot: script " + Twine(parser.dot) + ++ " seg " + Twine(alignTo(parser.dot, 1ULL << segment->alignment))); ++ ++ parser.dot = alignTo(parser.dot, 1ULL << segment->alignment); ++ segment->startVA = parser.dot; ++ parser.dot += segment->size; ++ ++ log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}", segment->name, ++ segment->startVA, segment->size, segment->alignment)); ++ ++ segments.push_back(segment); ++ } ++ } ++ } ++ } else if (auto *assign = dyn_cast(base)) { ++ handleScriptSymbol(assign, false); ++ } ++ } ++ ++ // Place any remaining segments that were not discarded. ++ OutputSegment *bonusdata = createOutputSegment(".data.bonus"); // Will call segments.push_back() ++ OutputSegment *bonusbss = createOutputSegment(".bss.bonus"); // Will call segments.push_back() ++ for (InputChunk *chunk : inputSegments) { ++ if (!chunk->outputSeg && !chunk->discarded) { ++ log("BONUS <--- " + chunk->name); ++ (chunk->name.starts_with(".bss") ? bonusbss : bonusdata)->addInputSegment(chunk); ++ } ++ } ++ ++ bonusdata->finalizeInputSegments(); ++ parser.dot = alignTo(parser.dot, 1ULL << bonusdata->alignment); ++ bonusdata->startVA = parser.dot; ++ parser.dot += bonusdata->size; ++ ++ bonusbss->finalizeInputSegments(); ++ parser.dot = alignTo(parser.dot, 1ULL << bonusbss->alignment); ++ bonusbss->startVA = parser.dot; ++ parser.dot += bonusbss->size; ++ ++ memoryPtr = parser.dot; ++ } ++ ++ // This works fine if there is only one bss segment and it comes last. ++ // But we can/will have at least two, so let's fake index. ++ size_t nonIndex = 0; ++ for (size_t i = 0; i < segments.size(); ++i) ++ if (needsPassiveInitialization(segments[i]) && !segments[i]->isBss) ++ segments[i]->index = nonIndex++; ++ else ++ segments[i]->index = static_cast(-1); ++ ++ // Make space for the memory initialization flag ++ if (config->sharedMemory && hasPassiveInitializedSegments()) { ++ memoryPtr = alignTo(memoryPtr, 4); ++ WasmSym::initMemoryFlag = symtab->addSyntheticDataSymbol( ++ "__wasm_init_memory_flag", WASM_SYMBOL_VISIBILITY_HIDDEN); ++ WasmSym::initMemoryFlag->markLive(); ++ WasmSym::initMemoryFlag->setVA(memoryPtr); ++ log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}", ++ "__wasm_init_memory_flag", memoryPtr, 4, 4)); ++ memoryPtr += 4; ++ } ++ ++ memoryPtr = alignTo(memoryPtr, WasmPageSize); ++ out.memorySec->numMemoryPages = memoryPtr / WasmPageSize; ++ log("mem: total pages = " + Twine(out.memorySec->numMemoryPages)); ++ ++ uint64_t maxMemorySetting = 1ULL << (config->is64.value_or(false) ? 48 : 32); ++ if (config->initialMemory != 0) { ++ if (config->initialMemory != alignTo(config->initialMemory, WasmPageSize)) ++ error("initial memory must be " + Twine(WasmPageSize) + "-byte aligned"); ++ if (memoryPtr > config->initialMemory) ++ error("initial memory too small, " + Twine(memoryPtr) + " bytes needed"); ++ if (config->initialMemory > maxMemorySetting) ++ error("initial memory too large, cannot be greater than " + ++ Twine(maxMemorySetting)); ++ memoryPtr = config->initialMemory; ++ } ++ ++ if (config->maxMemory != 0) { ++ if (config->maxMemory != alignTo(config->maxMemory, WasmPageSize)) ++ error("maximum memory must be " + Twine(WasmPageSize) + "-byte aligned"); ++ if (memoryPtr > config->maxMemory) ++ error("maximum memory too small, " + Twine(memoryPtr) + " bytes needed"); ++ if (config->maxMemory > maxMemorySetting) ++ error("maximum memory too large, cannot be greater than " + ++ Twine(maxMemorySetting)); ++ } ++ ++ // Check max if explicitly supplied or required by shared memory ++ if (config->maxMemory != 0 || config->sharedMemory) { ++ uint64_t max = config->maxMemory ? config->maxMemory : memoryPtr; ++ out.memorySec->maxMemoryPages = max / WasmPageSize; ++ log("mem: max pages = " + Twine(out.memorySec->maxMemoryPages)); ++ } ++} ++ + void Writer::addSection(OutputSection *sec) { + if (!sec->isNeeded()) + return; +@@ -1694,12 +1921,18 @@ void Writer::run() { + WasmSym::definedTableBase32->setVA(config->tableBase); + } + +- log("-- createOutputSegments"); +- createOutputSegments(); + log("-- createSyntheticSections"); + createSyntheticSections(); +- log("-- layoutMemory"); +- layoutMemory(); ++ ++ if (!config->linkerScript) { ++ log("-- createOutputSegments"); ++ createOutputSegments(); ++ log("-- layoutMemory"); ++ layoutMemory(); ++ } else { ++ log("-- runScript"); ++ runScript(); ++ } + + if (!config->relocatable) { + // Create linker synthesized __start_SECNAME/__stop_SECNAME symbols +-- +2.25.1 + diff --git a/patches/musl/0001-NOMERGE-Hacks-to-get-Linux-Wasm-to-compile-minimal-a.patch b/patches/musl/0001-NOMERGE-Hacks-to-get-Linux-Wasm-to-compile-minimal-a.patch new file mode 100644 index 0000000..047918d --- /dev/null +++ b/patches/musl/0001-NOMERGE-Hacks-to-get-Linux-Wasm-to-compile-minimal-a.patch @@ -0,0 +1,2353 @@ +From 725521cc7eae536cc9542e832afba665d69545ea Mon Sep 17 00:00:00 2001 +From: Joel Severin +Date: Wed, 27 Mar 2024 22:13:53 +0100 +Subject: [PATCH] NOMERGE: Hacks to get Linux/Wasm to compile (minimal and + incorrect) + +--- + arch/wasm/arch.mak | 1 + + arch/wasm/atomic_arch.h | 15 + + arch/wasm/bits/alltypes.h.in | 17 ++ + arch/wasm/bits/fenv.h | 10 + + arch/wasm/bits/float.h | 15 + + arch/wasm/bits/ipcstat.h | 1 + + arch/wasm/bits/msg.h | 18 ++ + arch/wasm/bits/posix.h | 2 + + arch/wasm/bits/ptrace.h | 2 + + arch/wasm/bits/reg.h | 3 + + arch/wasm/bits/sem.h | 13 + + arch/wasm/bits/setjmp.h | 1 + + arch/wasm/bits/shm.h | 31 +++ + arch/wasm/bits/signal.h | 85 ++++++ + arch/wasm/bits/stat.h | 25 ++ + arch/wasm/bits/stdint.h | 20 ++ + arch/wasm/bits/syscall.h.in | 305 +++++++++++++++++++++ + arch/wasm/bits/user.h | 8 + + arch/wasm/crt_arch.h | 40 +++ + arch/wasm/kstat.h | 21 ++ + arch/wasm/pthread_arch.h | 15 + + arch/wasm/reloc.h | 13 + + arch/wasm/syscall_arch.h | 12 + + configure | 13 +- + include/elf.h | 56 ++++ + include/unistd.h | 5 +- + src/internal/pthread_impl.h | 9 +- + src/internal/syscall.h | 55 ++-- + src/linux/clone.c | 21 +- + src/linux/reboot.c | 2 +- + src/misc/syscall.c | 56 +++- + src/misc/syscalls.c | 0 + src/misc/wasm/syscalls.s | 123 +++++++++ + src/network/accept.c | 2 +- + src/network/accept4.c | 2 +- + src/network/bind.c | 2 +- + src/network/connect.c | 2 +- + src/network/getpeername.c | 2 +- + src/network/getsockname.c | 2 +- + src/network/getsockopt.c | 6 +- + src/network/listen.c | 2 +- + src/network/recvmsg.c | 2 +- + src/network/sendmsg.c | 2 +- + src/network/setsockopt.c | 6 +- + src/network/shutdown.c | 2 +- + src/network/socket.c | 5 +- + src/network/socketpair.c | 5 +- + src/process/posix_spawn.c | 3 +- + src/process/vfork.c | 12 +- + src/setjmp/longjmp.c | 19 ++ + src/setjmp/wasm/longjmp.S | 13 + + src/setjmp/wasm/setjmp.S | 5 + + src/signal/wasm/restore.s | 98 +++++++ + src/signal/wasm/sigsetjmp.S | 5 + + src/thread/__syscall_cp.c | 18 -- + src/thread/__timedwait.c | 6 +- + src/thread/__wait.c | 4 +- + src/thread/pthread_barrier_wait.c | 4 +- + src/thread/pthread_cancel.c | 108 ++++++-- + src/thread/pthread_cond_timedwait.c | 4 +- + src/thread/pthread_mutex_timedlock.c | 6 +- + src/thread/pthread_mutex_trylock.c | 2 +- + src/thread/pthread_mutex_unlock.c | 2 +- + src/thread/pthread_mutexattr_setprotocol.c | 2 +- + src/thread/wasm/__set_thread_area.c | 9 + + src/thread/wasm/clone.s | 149 ++++++++++ + src/unistd/access.c | 2 +- + src/unistd/faccessat.c | 2 +- + 68 files changed, 1387 insertions(+), 141 deletions(-) + create mode 100644 arch/wasm/arch.mak + create mode 100644 arch/wasm/atomic_arch.h + create mode 100644 arch/wasm/bits/alltypes.h.in + create mode 100644 arch/wasm/bits/fenv.h + create mode 100644 arch/wasm/bits/float.h + create mode 100644 arch/wasm/bits/ipcstat.h + create mode 100644 arch/wasm/bits/msg.h + create mode 100644 arch/wasm/bits/posix.h + create mode 100644 arch/wasm/bits/ptrace.h + create mode 100644 arch/wasm/bits/reg.h + create mode 100644 arch/wasm/bits/sem.h + create mode 100644 arch/wasm/bits/setjmp.h + create mode 100644 arch/wasm/bits/shm.h + create mode 100644 arch/wasm/bits/signal.h + create mode 100644 arch/wasm/bits/stat.h + create mode 100644 arch/wasm/bits/stdint.h + create mode 100644 arch/wasm/bits/syscall.h.in + create mode 100644 arch/wasm/bits/user.h + create mode 100644 arch/wasm/crt_arch.h + create mode 100644 arch/wasm/kstat.h + create mode 100644 arch/wasm/pthread_arch.h + create mode 100644 arch/wasm/reloc.h + create mode 100644 arch/wasm/syscall_arch.h + create mode 100644 src/misc/syscalls.c + create mode 100644 src/misc/wasm/syscalls.s + create mode 100644 src/setjmp/wasm/longjmp.S + create mode 100644 src/setjmp/wasm/setjmp.S + create mode 100644 src/signal/wasm/restore.s + create mode 100644 src/signal/wasm/sigsetjmp.S + create mode 100644 src/thread/wasm/__set_thread_area.c + create mode 100644 src/thread/wasm/clone.s + +diff --git a/arch/wasm/arch.mak b/arch/wasm/arch.mak +new file mode 100644 +index 0000000..aa4d05c +--- /dev/null ++++ b/arch/wasm/arch.mak +@@ -0,0 +1 @@ ++COMPAT_SRC_DIRS = compat/time32 +diff --git a/arch/wasm/atomic_arch.h b/arch/wasm/atomic_arch.h +new file mode 100644 +index 0000000..95e840e +--- /dev/null ++++ b/arch/wasm/atomic_arch.h +@@ -0,0 +1,15 @@ ++#define a_cas a_cas ++static inline int a_cas(volatile int *p, int t, int s) ++{ ++ __atomic_compare_exchange_n(p, &t, s, (_Bool)0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); ++ return t; ++} ++ ++extern void __wasm_abort(); ++ ++#define a_crash a_crash ++static inline void a_crash() ++{ ++ __wasm_abort(); ++ __builtin_unreachable(); ++} +diff --git a/arch/wasm/bits/alltypes.h.in b/arch/wasm/bits/alltypes.h.in +new file mode 100644 +index 0000000..4b9c346 +--- /dev/null ++++ b/arch/wasm/bits/alltypes.h.in +@@ -0,0 +1,17 @@ ++#define _REDIR_TIME64 1 ++#define _Addr long ++#define _Int64 long long ++#define _Reg int ++ ++#define __BYTE_ORDER 1234 ++ ++#define __LONG_MAX 0x7fffffffL ++ ++#ifndef __cplusplus ++TYPEDEF int wchar_t; ++#endif ++ ++TYPEDEF float float_t; ++TYPEDEF double double_t; ++ ++TYPEDEF struct { long long __ll; long double __ld; } max_align_t; +diff --git a/arch/wasm/bits/fenv.h b/arch/wasm/bits/fenv.h +new file mode 100644 +index 0000000..edbdea2 +--- /dev/null ++++ b/arch/wasm/bits/fenv.h +@@ -0,0 +1,10 @@ ++#define FE_ALL_EXCEPT 0 ++#define FE_TONEAREST 0 ++ ++typedef unsigned long fexcept_t; ++ ++typedef struct { ++ unsigned long __cw; ++} fenv_t; ++ ++#define FE_DFL_ENV ((const fenv_t *) -1) +diff --git a/arch/wasm/bits/float.h b/arch/wasm/bits/float.h +new file mode 100644 +index 0000000..97ba282 +--- /dev/null ++++ b/arch/wasm/bits/float.h +@@ -0,0 +1,15 @@ ++ ++#define LDBL_TRUE_MIN 6.47517511943802511092443895822764655e-4966L ++#define LDBL_MIN 3.36210314311209350626267781732175260e-4932L ++#define LDBL_MAX 1.18973149535723176508575932662800702e+4932L ++#define LDBL_EPSILON 1.92592994438723585305597794258492732e-34L ++ ++#define LDBL_MANT_DIG 113 ++#define LDBL_MIN_EXP (-16381) ++#define LDBL_MAX_EXP 16384 ++ ++#define LDBL_DIG 33 ++#define LDBL_MIN_10_EXP (-4931) ++#define LDBL_MAX_10_EXP 4932 ++ ++#define DECIMAL_DIG 36 +diff --git a/arch/wasm/bits/ipcstat.h b/arch/wasm/bits/ipcstat.h +new file mode 100644 +index 0000000..4f4fcb0 +--- /dev/null ++++ b/arch/wasm/bits/ipcstat.h +@@ -0,0 +1 @@ ++#define IPC_STAT 0x102 +diff --git a/arch/wasm/bits/msg.h b/arch/wasm/bits/msg.h +new file mode 100644 +index 0000000..7bbbb2b +--- /dev/null ++++ b/arch/wasm/bits/msg.h +@@ -0,0 +1,18 @@ ++struct msqid_ds { ++ struct ipc_perm msg_perm; ++ unsigned long __msg_stime_lo; ++ unsigned long __msg_stime_hi; ++ unsigned long __msg_rtime_lo; ++ unsigned long __msg_rtime_hi; ++ unsigned long __msg_ctime_lo; ++ unsigned long __msg_ctime_hi; ++ unsigned long msg_cbytes; ++ msgqnum_t msg_qnum; ++ msglen_t msg_qbytes; ++ pid_t msg_lspid; ++ pid_t msg_lrpid; ++ unsigned long __unused[2]; ++ time_t msg_stime; ++ time_t msg_rtime; ++ time_t msg_ctime; ++}; +diff --git a/arch/wasm/bits/posix.h b/arch/wasm/bits/posix.h +new file mode 100644 +index 0000000..30a3871 +--- /dev/null ++++ b/arch/wasm/bits/posix.h +@@ -0,0 +1,2 @@ ++#define _POSIX_V6_ILP32_OFFBIG 1 ++#define _POSIX_V7_ILP32_OFFBIG 1 +diff --git a/arch/wasm/bits/ptrace.h b/arch/wasm/bits/ptrace.h +new file mode 100644 +index 0000000..da93e7a +--- /dev/null ++++ b/arch/wasm/bits/ptrace.h +@@ -0,0 +1,2 @@ ++#define PTRACE_GET_THREAD_AREA 25 ++#define PTRACE_SINGLEBLOCK 33 +diff --git a/arch/wasm/bits/reg.h b/arch/wasm/bits/reg.h +new file mode 100644 +index 0000000..0c7bffc +--- /dev/null ++++ b/arch/wasm/bits/reg.h +@@ -0,0 +1,3 @@ ++#undef __WORDSIZE ++#define __WORDSIZE 32 ++/* FIXME */ +diff --git a/arch/wasm/bits/sem.h b/arch/wasm/bits/sem.h +new file mode 100644 +index 0000000..6566154 +--- /dev/null ++++ b/arch/wasm/bits/sem.h +@@ -0,0 +1,13 @@ ++struct semid_ds { ++ struct ipc_perm sem_perm; ++ unsigned long __sem_otime_lo; ++ unsigned long __sem_otime_hi; ++ unsigned long __sem_ctime_lo; ++ unsigned long __sem_ctime_hi; ++ unsigned short sem_nsems; ++ char __sem_nsems_pad[sizeof(long)-sizeof(short)]; ++ long __unused3; ++ long __unused4; ++ time_t sem_otime; ++ time_t sem_ctime; ++}; +diff --git a/arch/wasm/bits/setjmp.h b/arch/wasm/bits/setjmp.h +new file mode 100644 +index 0000000..55e3a95 +--- /dev/null ++++ b/arch/wasm/bits/setjmp.h +@@ -0,0 +1 @@ ++typedef unsigned long long __jmp_buf[32]; +diff --git a/arch/wasm/bits/shm.h b/arch/wasm/bits/shm.h +new file mode 100644 +index 0000000..725fb46 +--- /dev/null ++++ b/arch/wasm/bits/shm.h +@@ -0,0 +1,31 @@ ++#define SHMLBA 4096 ++ ++struct shmid_ds { ++ struct ipc_perm shm_perm; ++ size_t shm_segsz; ++ unsigned long __shm_atime_lo; ++ unsigned long __shm_atime_hi; ++ unsigned long __shm_dtime_lo; ++ unsigned long __shm_dtime_hi; ++ unsigned long __shm_ctime_lo; ++ unsigned long __shm_ctime_hi; ++ pid_t shm_cpid; ++ pid_t shm_lpid; ++ unsigned long shm_nattch; ++ unsigned long __pad1; ++ unsigned long __pad2; ++ unsigned long __pad3; ++ time_t shm_atime; ++ time_t shm_dtime; ++ time_t shm_ctime; ++}; ++ ++struct shminfo { ++ unsigned long shmmax, shmmin, shmmni, shmseg, shmall, __unused[4]; ++}; ++ ++struct shm_info { ++ int __used_ids; ++ unsigned long shm_tot, shm_rss, shm_swp; ++ unsigned long __swap_attempts, __swap_successes; ++}; +diff --git a/arch/wasm/bits/signal.h b/arch/wasm/bits/signal.h +new file mode 100644 +index 0000000..ab24d3d +--- /dev/null ++++ b/arch/wasm/bits/signal.h +@@ -0,0 +1,85 @@ ++#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ ++ || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) ++ ++#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) ++#define MINSIGSTKSZ 2048 ++#define SIGSTKSZ 8192 ++#endif ++ ++#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) ++typedef unsigned long greg_t, gregset_t[2]; ++typedef struct sigcontext { ++ unsigned long stack_pointer; ++ unsigned long tls; ++} mcontext_t; ++#else ++typedef struct { ++ unsigned long __regs[2]; ++} mcontext_t; ++#endif ++ ++#if defined(_GNU_SOURCE) ++#define REG_SP 0 ++#define REG_TP 1 ++#endif ++ ++struct sigaltstack { ++ void *ss_sp; ++ int ss_flags; ++ size_t ss_size; ++}; ++ ++typedef struct __ucontext { ++ struct __ucontext *uc_link; ++ sigset_t uc_sigmask; ++ stack_t uc_stack; ++ mcontext_t uc_mcontext; ++} ucontext_t; ++ ++#define SA_NOCLDSTOP 1 ++#define SA_NOCLDWAIT 2 ++#define SA_SIGINFO 4 ++#define SA_ONSTACK 0x08000000 ++#define SA_RESTART 0x10000000 ++#define SA_NODEFER 0x40000000 ++#define SA_RESETHAND 0x80000000 ++#define SA_RESTORER 0x04000000 ++ ++#endif ++ ++#define SIGHUP 1 ++#define SIGINT 2 ++#define SIGQUIT 3 ++#define SIGILL 4 ++#define SIGTRAP 5 ++#define SIGABRT 6 ++#define SIGIOT SIGABRT ++#define SIGBUS 7 ++#define SIGFPE 8 ++#define SIGKILL 9 ++#define SIGUSR1 10 ++#define SIGSEGV 11 ++#define SIGUSR2 12 ++#define SIGPIPE 13 ++#define SIGALRM 14 ++#define SIGTERM 15 ++#define SIGSTKFLT 16 ++#define SIGCHLD 17 ++#define SIGCONT 18 ++#define SIGSTOP 19 ++#define SIGTSTP 20 ++#define SIGTTIN 21 ++#define SIGTTOU 22 ++#define SIGURG 23 ++#define SIGXCPU 24 ++#define SIGXFSZ 25 ++#define SIGVTALRM 26 ++#define SIGPROF 27 ++#define SIGWINCH 28 ++#define SIGIO 29 ++#define SIGPOLL 29 ++#define SIGPWR 30 ++#define SIGSYS 31 ++#define SIGUNUSED SIGSYS ++ ++#define _NSIG 65 +diff --git a/arch/wasm/bits/stat.h b/arch/wasm/bits/stat.h +new file mode 100644 +index 0000000..8a4d509 +--- /dev/null ++++ b/arch/wasm/bits/stat.h +@@ -0,0 +1,25 @@ ++/* copied from kernel definition, but with padding replaced ++ * by the corresponding correctly-sized userspace types. */ ++ ++struct stat { ++ dev_t st_dev; ++ ino_t st_ino; ++ mode_t st_mode; ++ nlink_t st_nlink; ++ uid_t st_uid; ++ gid_t st_gid; ++ dev_t st_rdev; ++ long long __st_rdev_padding; ++ off_t st_size; ++ blksize_t st_blksize; ++ int __st_blksize_padding; ++ blkcnt_t st_blocks; ++ struct { ++ long tv_sec; ++ long tv_nsec; ++ } __st_atim32, __st_mtim32, __st_ctim32; ++ unsigned __unused[2]; ++ struct timespec st_atim; ++ struct timespec st_mtim; ++ struct timespec st_ctim; ++}; +diff --git a/arch/wasm/bits/stdint.h b/arch/wasm/bits/stdint.h +new file mode 100644 +index 0000000..d1b2712 +--- /dev/null ++++ b/arch/wasm/bits/stdint.h +@@ -0,0 +1,20 @@ ++typedef int32_t int_fast16_t; ++typedef int32_t int_fast32_t; ++typedef uint32_t uint_fast16_t; ++typedef uint32_t uint_fast32_t; ++ ++#define INT_FAST16_MIN INT32_MIN ++#define INT_FAST32_MIN INT32_MIN ++ ++#define INT_FAST16_MAX INT32_MAX ++#define INT_FAST32_MAX INT32_MAX ++ ++#define UINT_FAST16_MAX UINT32_MAX ++#define UINT_FAST32_MAX UINT32_MAX ++ ++#define INTPTR_MIN INT32_MIN ++#define INTPTR_MAX INT32_MAX ++#define UINTPTR_MAX UINT32_MAX ++#define PTRDIFF_MIN INT32_MIN ++#define PTRDIFF_MAX INT32_MAX ++#define SIZE_MAX UINT32_MAX +diff --git a/arch/wasm/bits/syscall.h.in b/arch/wasm/bits/syscall.h.in +new file mode 100644 +index 0000000..5227a60 +--- /dev/null ++++ b/arch/wasm/bits/syscall.h.in +@@ -0,0 +1,305 @@ ++#define __NR_io_setup 0 ++#define __NR_io_destroy 1 ++#define __NR_io_submit 2 ++#define __NR_io_cancel 3 ++#define __NR_setxattr 5 ++#define __NR_lsetxattr 6 ++#define __NR_fsetxattr 7 ++#define __NR_getxattr 8 ++#define __NR_lgetxattr 9 ++#define __NR_fgetxattr 10 ++#define __NR_listxattr 11 ++#define __NR_llistxattr 12 ++#define __NR_flistxattr 13 ++#define __NR_removexattr 14 ++#define __NR_lremovexattr 15 ++#define __NR_fremovexattr 16 ++#define __NR_getcwd 17 ++#define __NR_lookup_dcookie 18 ++#define __NR_eventfd2 19 ++#define __NR_epoll_create1 20 ++#define __NR_epoll_ctl 21 ++#define __NR_epoll_pwait 22 ++#define __NR_dup 23 ++#define __NR_dup3 24 ++#define __NR_fcntl 25 ++#define __NR_inotify_init1 26 ++#define __NR_inotify_add_watch 27 ++#define __NR_inotify_rm_watch 28 ++#define __NR_ioctl 29 ++#define __NR_ioprio_set 30 ++#define __NR_ioprio_get 31 ++#define __NR_flock 32 ++#define __NR_mknodat 33 ++#define __NR_mkdirat 34 ++#define __NR_unlinkat 35 ++#define __NR_symlinkat 36 ++#define __NR_linkat 37 ++#define __NR_umount2 39 ++#define __NR_mount 40 ++#define __NR_pivot_root 41 ++#define __NR_nfsservctl 42 ++#define __NR_statfs64 43 ++#define __NR_fstatfs64 44 ++#define __NR_truncate 45 ++#define __NR_ftruncate 46 ++#define __NR_fallocate 47 ++#define __NR_faccessat 48 ++#define __NR_chdir 49 ++#define __NR_fchdir 50 ++#define __NR_chroot 51 ++#define __NR_fchmod 52 ++#define __NR_fchmodat 53 ++#define __NR_fchownat 54 ++#define __NR_fchown 55 ++#define __NR_openat 56 ++#define __NR_close 57 ++#define __NR_vhangup 58 ++#define __NR_pipe2 59 ++#define __NR_quotactl 60 ++#define __NR_getdents64 61 ++#define __NR_lseek 62 ++#define __NR_read 63 ++#define __NR_write 64 ++#define __NR_readv 65 ++#define __NR_writev 66 ++#define __NR_pread64 67 ++#define __NR_pwrite64 68 ++#define __NR_preadv 69 ++#define __NR_pwritev 70 ++#define __NR_sendfile 71 ++#define __NR_signalfd4 74 ++#define __NR_vmsplice 75 ++#define __NR_splice 76 ++#define __NR_tee 77 ++#define __NR_readlinkat 78 ++#define __NR_fstat 80 ++#define __NR_sync 81 ++#define __NR_fsync 82 ++#define __NR_fdatasync 83 ++#define __NR_sync_file_range 84 ++#define __NR_timerfd_create 85 ++#define __NR_acct 89 ++#define __NR_capget 90 ++#define __NR_capset 91 ++#define __NR_personality 92 ++#define __NR_exit 93 ++#define __NR_exit_group 94 ++#define __NR_waitid 95 ++#define __NR_set_tid_address 96 ++#define __NR_unshare 97 ++#define __NR_set_robust_list 99 ++#define __NR_get_robust_list 100 ++#define __NR_getitimer 102 ++#define __NR_setitimer 103 ++#define __NR_kexec_load 104 ++#define __NR_init_module 105 ++#define __NR_delete_module 106 ++#define __NR_timer_create 107 ++#define __NR_timer_getoverrun 109 ++#define __NR_timer_delete 111 ++#define __NR_syslog 116 ++#define __NR_ptrace 117 ++#define __NR_sched_setparam 118 ++#define __NR_sched_setscheduler 119 ++#define __NR_sched_getscheduler 120 ++#define __NR_sched_getparam 121 ++#define __NR_sched_setaffinity 122 ++#define __NR_sched_getaffinity 123 ++#define __NR_sched_yield 124 ++#define __NR_sched_get_priority_max 125 ++#define __NR_sched_get_priority_min 126 ++#define __NR_restart_syscall 128 ++#define __NR_kill 129 ++#define __NR_tkill 130 ++#define __NR_tgkill 131 ++#define __NR_sigaltstack 132 ++#define __NR_rt_sigsuspend 133 ++#define __NR_rt_sigaction 134 ++#define __NR_rt_sigprocmask 135 ++#define __NR_rt_sigpending 136 ++#define __NR_rt_sigqueueinfo 138 ++#define __NR_rt_sigreturn 139 ++#define __NR_setpriority 140 ++#define __NR_getpriority 141 ++#define __NR_reboot 142 ++#define __NR_setregid 143 ++#define __NR_setgid 144 ++#define __NR_setreuid 145 ++#define __NR_setuid 146 ++#define __NR_setresuid 147 ++#define __NR_getresuid 148 ++#define __NR_setresgid 149 ++#define __NR_getresgid 150 ++#define __NR_setfsuid 151 ++#define __NR_setfsgid 152 ++#define __NR_times 153 ++#define __NR_setpgid 154 ++#define __NR_getpgid 155 ++#define __NR_getsid 156 ++#define __NR_setsid 157 ++#define __NR_getgroups 158 ++#define __NR_setgroups 159 ++#define __NR_uname 160 ++#define __NR_sethostname 161 ++#define __NR_setdomainname 162 ++#define __NR_getrusage 165 ++#define __NR_umask 166 ++#define __NR_prctl 167 ++#define __NR_getcpu 168 ++#define __NR_getpid 172 ++#define __NR_getppid 173 ++#define __NR_getuid 174 ++#define __NR_geteuid 175 ++#define __NR_getgid 176 ++#define __NR_getegid 177 ++#define __NR_gettid 178 ++#define __NR_sysinfo 179 ++#define __NR_mq_open 180 ++#define __NR_mq_unlink 181 ++#define __NR_mq_notify 184 ++#define __NR_mq_getsetattr 185 ++#define __NR_msgget 186 ++#define __NR_msgctl 187 ++#define __NR_msgrcv 188 ++#define __NR_msgsnd 189 ++#define __NR_semget 190 ++#define __NR_semctl 191 ++#define __NR_semop 193 ++#define __NR_shmget 194 ++#define __NR_shmctl 195 ++#define __NR_shmat 196 ++#define __NR_shmdt 197 ++#define __NR_socket 198 ++#define __NR_socketpair 199 ++#define __NR_bind 200 ++#define __NR_listen 201 ++#define __NR_accept 202 ++#define __NR_connect 203 ++#define __NR_getsockname 204 ++#define __NR_getpeername 205 ++#define __NR_sendto 206 ++#define __NR_recvfrom 207 ++#define __NR_setsockopt 208 ++#define __NR_getsockopt 209 ++#define __NR_shutdown 210 ++#define __NR_sendmsg 211 ++#define __NR_recvmsg 212 ++#define __NR_readahead 213 ++#define __NR_brk 214 ++#define __NR_munmap 215 ++#define __NR_mremap 216 ++#define __NR_add_key 217 ++#define __NR_request_key 218 ++#define __NR_keyctl 219 ++#define __NR_clone 220 ++#define __NR_execve 221 ++#define __NR_mmap2 222 ++#define __NR_fadvise64 223 ++#define __NR_rt_tgsigqueueinfo 240 ++#define __NR_perf_event_open 241 ++#define __NR_accept4 242 ++#define __NR_arch_specific_syscall 244 ++#define __NR_prlimit64 261 ++#define __NR_fanotify_init 262 ++#define __NR_fanotify_mark 263 ++#define __NR_name_to_handle_at 264 ++#define __NR_open_by_handle_at 265 ++#define __NR_syncfs 267 ++#define __NR_setns 268 ++#define __NR_sendmmsg 269 ++#define __NR_process_vm_readv 270 ++#define __NR_process_vm_writev 271 ++#define __NR_kcmp 272 ++#define __NR_finit_module 273 ++#define __NR_sched_setattr 274 ++#define __NR_sched_getattr 275 ++#define __NR_renameat2 276 ++#define __NR_seccomp 277 ++#define __NR_getrandom 278 ++#define __NR_memfd_create 279 ++#define __NR_bpf 280 ++#define __NR_execveat 281 ++#define __NR_userfaultfd 282 ++#define __NR_membarrier 283 ++#define __NR_mlock2 284 ++#define __NR_copy_file_range 285 ++#define __NR_preadv2 286 ++#define __NR_pwritev2 287 ++#define __NR_pkey_mprotect 288 ++#define __NR_pkey_alloc 289 ++#define __NR_pkey_free 290 ++#define __NR_statx 291 ++#define __NR_rseq 293 ++#define __NR_kexec_file_load 294 ++#define __NR_clock_gettime64 403 ++#define __NR_clock_settime64 404 ++#define __NR_clock_adjtime64 405 ++#define __NR_clock_getres_time64 406 ++#define __NR_clock_nanosleep_time64 407 ++#define __NR_timer_gettime64 408 ++#define __NR_timer_settime64 409 ++#define __NR_timerfd_gettime64 410 ++#define __NR_timerfd_settime64 411 ++#define __NR_utimensat_time64 412 ++#define __NR_pselect6_time64 413 ++#define __NR_ppoll_time64 414 ++#define __NR_io_pgetevents_time64 416 ++#define __NR_recvmmsg_time64 417 ++#define __NR_mq_timedsend_time64 418 ++#define __NR_mq_timedreceive_time64 419 ++#define __NR_semtimedop_time64 420 ++#define __NR_rt_sigtimedwait_time64 421 ++#define __NR_futex_time64 422 ++#define __NR_sched_rr_get_interval_time64 423 ++#define __NR_pidfd_send_signal 424 ++#define __NR_io_uring_setup 425 ++#define __NR_io_uring_enter 426 ++#define __NR_io_uring_register 427 ++#define __NR_open_tree 428 ++#define __NR_move_mount 429 ++#define __NR_fsopen 430 ++#define __NR_fsconfig 431 ++#define __NR_fsmount 432 ++#define __NR_fspick 433 ++#define __NR_pidfd_open 434 ++#define __NR_clone3 435 ++#define __NR_close_range 436 ++#define __NR_openat2 437 ++#define __NR_pidfd_getfd 438 ++#define __NR_faccessat2 439 ++#define __NR_process_madvise 440 ++#define __NR_epoll_pwait2 441 ++#define __NR_mount_setattr 442 ++#define __NR_quotactl_fd 443 ++#define __NR_landlock_create_ruleset 444 ++#define __NR_landlock_add_rule 445 ++#define __NR_landlock_restrict_self 446 ++#define __NR_process_mrelease 448 ++#define __NR_futex_waitv 449 ++#define __NR_set_mempolicy_home_node 450 ++#define __NR_cachestat 451 ++#define __NR_fchmodat2 452 ++ ++/* These ones are NOT supported but required by musl - will return -ENOSYS. */ ++#define __NR_nanosleep 101 ++#define __NR_settimeofday 170 ++#define __NR_swapon 224 ++#define __NR_swapoff 225 ++#define __NR_mprotect 226 ++#define __NR_msync 227 ++#define __NR_mlock 228 ++#define __NR_munlock 229 ++#define __NR_mlockall 230 ++#define __NR_munlockall 231 ++#define __NR_mincore 232 ++#define __NR_madvise 233 ++#define __NR_remap_file_pages 234 ++#define __NR_mbind 235 ++#define __NR_get_mempolicy 236 ++#define __NR_set_mempolicy 237 ++#define __NR_migrate_pages 238 ++#define __NR_move_pages 239 ++ ++/* Required by musl to select the correct prototype. Notice the double __. */ ++#define SYS__llseek __NR_lseek +diff --git a/arch/wasm/bits/user.h b/arch/wasm/bits/user.h +new file mode 100644 +index 0000000..7954679 +--- /dev/null ++++ b/arch/wasm/bits/user.h +@@ -0,0 +1,8 @@ ++struct user_regs_struct { ++ unsigned long stack_pointer; ++ unsigned long tls; ++}; ++ ++typedef unsigned long elf_greg_t; ++typedef struct user_regs_struct elf_gregset_t; ++#define ELF_NGREG (sizeof(elf_gregset_t) / sizeof(elf_greg_t)) +diff --git a/arch/wasm/crt_arch.h b/arch/wasm/crt_arch.h +new file mode 100644 +index 0000000..fe3ce01 +--- /dev/null ++++ b/arch/wasm/crt_arch.h +@@ -0,0 +1,40 @@ ++/* ++ * We provide a prototype for main here (it's declared without prototype in crt1.c etc.). The reason ++ * we have to do this is to avoid an issue with LLVM wrapping and renaming it __original_main, which ++ * will fail when programs later on provide a real main with correct prototype and link it. ++ * ++ * Oh, and then it comes along and renames it __main_argc_argv because it has parameters, so we have ++ * to alias that as well and main will now be called __main_argc_argv. This cannot be a direct alias ++ * as it is not defined yet, so we just call it... ++ * ++ * To add salt to the injury, musl insists to call the 3-argument version with envp which is non- ++ * standard. What a wonderful mess... We need to clean this up someday, maybe auto-detect somehow. ++ */ ++int __main_argc_argv(int argc, char *argv[]); ++int main(int argc, char *argv[], char *envp[]) ++{ ++ (void)envp; ++ return __main_argc_argv(argc, argv); ++} ++ ++/* ++ * This must really be written in assembly (to capture the stack pointer properly), but we know that ++ * the initial stack pointer is page aligned, so we can cheat a bit and align it to the next page... ++ * The problem is that LLVM does not seem to work when functions are defined with inline asm (in C). ++ * TODO: fix this hack by refactoring musl and moving things into a .S file. ++ */ ++#ifndef SHARED ++void _start(void) ++{ ++ void _start_c(/* no prototype on purpose */); ++ int dummy; ++ _start_c(((unsigned long)(void*)&dummy + 4095UL) & ~4095UL); ++} ++#else ++void _dlstart(void) ++{ ++ void _dlstart_c(/* no prototype on purpose */); ++ int dummy; ++ _dlstart_c(((unsigned long)(void*)&dummy + 4095UL) & ~4095UL); ++} ++#endif +diff --git a/arch/wasm/kstat.h b/arch/wasm/kstat.h +new file mode 100644 +index 0000000..c144957 +--- /dev/null ++++ b/arch/wasm/kstat.h +@@ -0,0 +1,21 @@ ++struct kstat { ++ dev_t st_dev; ++ ino_t st_ino; ++ mode_t st_mode; ++ nlink_t st_nlink; ++ uid_t st_uid; ++ gid_t st_gid; ++ dev_t st_rdev; ++ long long __st_rdev_padding; ++ off_t st_size; ++ blksize_t st_blksize; ++ int __st_blksize_padding; ++ blkcnt_t st_blocks; ++ long st_atime_sec; ++ long st_atime_nsec; ++ long st_mtime_sec; ++ long st_mtime_nsec; ++ long st_ctime_sec; ++ long st_ctime_nsec; ++ unsigned __unused[2]; ++}; +diff --git a/arch/wasm/pthread_arch.h b/arch/wasm/pthread_arch.h +new file mode 100644 +index 0000000..0625bd1 +--- /dev/null ++++ b/arch/wasm/pthread_arch.h +@@ -0,0 +1,15 @@ ++#include ++ ++/* ++ * Wasm (as compiled with LLVM) handles TLS data directly, instead of the ++ * indirect approach used for ELF targets (where musl manages all TLS). There is ++ * still a pthread TLS area to keep track of, and musl manages that by these ++ * functions. In other words, __musl_tp will point to a struct pthread. ++ */ ++ ++extern _Thread_local uintptr_t __musl_tp; ++ ++static inline uintptr_t __get_tp(void) ++{ ++ return __musl_tp; ++} +diff --git a/arch/wasm/reloc.h b/arch/wasm/reloc.h +new file mode 100644 +index 0000000..833a2f5 +--- /dev/null ++++ b/arch/wasm/reloc.h +@@ -0,0 +1,13 @@ ++#define LDSO_ARCH "wasm" ++ ++#define TPOFF_K 0 ++ ++#define REL_SYMBOLIC R_WASM_64 ++#define REL_PLT R_WASM_JUMP_SLOT ++#define REL_RELATIVE R_WASM_RELATIVE ++#define REL_COPY R_WASM_COPY ++#define REL_DTPMOD R_WASM_TLS_DTPMOD64 ++#define REL_DTPOFF R_WASM_TLS_DTPREL64 ++#define REL_TPOFF R_WASM_TLS_TPREL64 ++ ++#define CRTJMP(pc,sp) abort() +diff --git a/arch/wasm/syscall_arch.h b/arch/wasm/syscall_arch.h +new file mode 100644 +index 0000000..1e06877 +--- /dev/null ++++ b/arch/wasm/syscall_arch.h +@@ -0,0 +1,12 @@ ++#define __SYSCALL_LL_E(x) \ ++((union { long long ll; long l[2]; }){ .ll = x }).l[0], \ ++((union { long long ll; long l[2]; }){ .ll = x }).l[1] ++#define __SYSCALL_LL_O(x) __SYSCALL_LL_E((x)) ++ ++extern long __syscall0(long n); ++extern long __syscall1(long n, long a); ++extern long __syscall2(long n, long a, long b); ++extern long __syscall3(long n, long a, long b, long c); ++extern long __syscall4(long n, long a, long b, long c, long d); ++extern long __syscall5(long n, long a, long b, long c, long d, long e); ++extern long __syscall6(long n, long a, long b, long c, long d, long e, long f); +diff --git a/configure b/configure +index bc9fbe4..b44705b 100755 +--- a/configure ++++ b/configure +@@ -340,6 +340,7 @@ riscv64*) ARCH=riscv64 ;; + riscv32*) ARCH=riscv32 ;; + sh[1-9bel-]*|sh|superh*) ARCH=sh ;; + s390x*) ARCH=s390x ;; ++wasm*) ARCH=wasm ;; + unknown) fail "$0: unable to detect target arch; try $0 --target=..." ;; + *) fail "$0: unknown or unsupported target \"$target\"" ;; + esac +@@ -586,33 +587,33 @@ fi + # Reduce space lost to padding for alignment purposes by sorting data + # objects according to their alignment reqirements. This approximates + # optimal packing. +-tryldflag LDFLAGS_AUTO -Wl,--sort-section,alignment +-tryldflag LDFLAGS_AUTO -Wl,--sort-common ++#tryldflag LDFLAGS_AUTO -Wl,--sort-section,alignment ++#tryldflag LDFLAGS_AUTO -Wl,--sort-common + + # When linking shared library, drop dummy weak definitions that were + # replaced by strong definitions from other translation units. + tryldflag LDFLAGS_AUTO -Wl,--gc-sections + + # Some patched GCC builds have these defaults messed up... +-tryldflag LDFLAGS_AUTO -Wl,--hash-style=both ++#tryldflag LDFLAGS_AUTO -Wl,--hash-style=both + + # Prevent linking if there are undefined symbols; if any exist, + # libc.so will crash at runtime during relocation processing. + # The common way this can happen is failure to link the compiler + # runtime library; implementation error is also a possibility. +-tryldflag LDFLAGS_AUTO -Wl,--no-undefined ++#tryldflag LDFLAGS_AUTO -Wl,--no-undefined + + # Avoid exporting symbols from compiler runtime libraries. They + # should be hidden anyway, but some toolchains including old gcc + # versions built without shared library support and pcc are broken. +-tryldflag LDFLAGS_AUTO -Wl,--exclude-libs=ALL ++#tryldflag LDFLAGS_AUTO -Wl,--exclude-libs=ALL + + # Public data symbols must be interposable to allow for copy + # relocations, but otherwise we want to bind symbols at libc link + # time to eliminate startup relocations and PLT overhead. Use + # --dynamic-list rather than -Bsymbolic-functions for greater + # control over what symbols are left unbound. +-tryldflag LDFLAGS_AUTO -Wl,--dynamic-list="$srcdir/dynamic.list" ++#tryldflag LDFLAGS_AUTO -Wl,--dynamic-list="$srcdir/dynamic.list" + + # Find compiler runtime library + test -z "$LIBCC" && tryldflag LIBCC -lgcc && tryldflag LIBCC -lgcc_eh +diff --git a/include/elf.h b/include/elf.h +index 3d5e13e..3620e8b 100644 +--- a/include/elf.h ++++ b/include/elf.h +@@ -3310,6 +3310,62 @@ enum + #define R_RISCV_TLSDESC_ADD_LO12 64 + #define R_RISCV_TLSDESC_CALL 65 + ++#define R_WASM_NONE 0 ++#define R_WASM_32 1 ++#define R_WASM_64 2 ++#define R_WASM_RELATIVE 3 ++#define R_WASM_COPY 4 ++#define R_WASM_JUMP_SLOT 5 ++#define R_WASM_TLS_DTPMOD32 6 ++#define R_WASM_TLS_DTPMOD64 7 ++#define R_WASM_TLS_DTPREL32 8 ++#define R_WASM_TLS_DTPREL64 9 ++#define R_WASM_TLS_TPREL32 10 ++#define R_WASM_TLS_TPREL64 11 ++ ++#define R_WASM_BRANCH 16 ++#define R_WASM_JAL 17 ++#define R_WASM_CALL 18 ++#define R_WASM_CALL_PLT 19 ++#define R_WASM_GOT_HI20 20 ++#define R_WASM_TLS_GOT_HI20 21 ++#define R_WASM_TLS_GD_HI20 22 ++#define R_WASM_PCREL_HI20 23 ++#define R_WASM_PCREL_LO12_I 24 ++#define R_WASM_PCREL_LO12_S 25 ++#define R_WASM_HI20 26 ++#define R_WASM_LO12_I 27 ++#define R_WASM_LO12_S 28 ++#define R_WASM_TPREL_HI20 29 ++#define R_WASM_TPREL_LO12_I 30 ++#define R_WASM_TPREL_LO12_S 31 ++#define R_WASM_TPREL_ADD 32 ++#define R_WASM_ADD8 33 ++#define R_WASM_ADD16 34 ++#define R_WASM_ADD32 35 ++#define R_WASM_ADD64 36 ++#define R_WASM_SUB8 37 ++#define R_WASM_SUB16 38 ++#define R_WASM_SUB32 39 ++#define R_WASM_SUB64 40 ++#define R_WASM_GNU_VTINHERIT 41 ++#define R_WASM_GNU_VTENTRY 42 ++#define R_WASM_ALIGN 43 ++#define R_WASM_RVC_BRANCH 44 ++#define R_WASM_RVC_JUMP 45 ++#define R_WASM_LUI 46 ++#define R_WASM_GPREL_I 47 ++#define R_WASM_GPREL_S 48 ++#define R_WASM_TPREL_I 49 ++#define R_WASM_TPREL_S 50 ++#define R_WASM_RELAX 51 ++#define R_WASM_SUB6 52 ++#define R_WASM_SET6 53 ++#define R_WASM_SET8 54 ++#define R_WASM_SET16 55 ++#define R_WASM_SET32 56 ++#define R_WASM_32_PCREL 57 ++ + #define EF_LARCH_ABI_MODIFIER_MASK 0x07 + #define EF_LARCH_ABI_SOFT_FLOAT 0x01 + #define EF_LARCH_ABI_SINGLE_FLOAT 0x02 +diff --git a/include/unistd.h b/include/unistd.h +index 5bc7f79..ca939bd 100644 +--- a/include/unistd.h ++++ b/include/unistd.h +@@ -177,7 +177,10 @@ void setusershell(void); + void endusershell(void); + char *getusershell(void); + int acct(const char *); +-long syscall(long, ...); ++#define __syscall_nargs_x(a,b,c,d,e,f,g,h,n,...) n ++#define __syscall_nargs(...) __syscall_nargs_x(__VA_ARGS__,7,6,5,4,3,2,1,0,) ++long syscall(long, long, ...); ++#define syscall(...) syscall(__syscall_nargs(__VA_ARGS__), __VA_ARGS__) + int execvpe(const char *, char *const [], char *const []); + int issetugid(void); + int getentropy(void *, size_t); +diff --git a/src/internal/pthread_impl.h b/src/internal/pthread_impl.h +index de2b9d8..e2ac283 100644 +--- a/src/internal/pthread_impl.h ++++ b/src/internal/pthread_impl.h +@@ -158,6 +158,7 @@ extern hidden void *__pthread_tsd_main[]; + extern hidden volatile int __eintr_valid_flag; + + hidden int __clone(int (*)(void *), void *, int, void *, ...); ++hidden int _Clone(int (*)(void *), void *, int, void *, pid_t *, void *, pid_t *); + hidden int __set_thread_area(void *); + hidden int __libc_sigaction(int, const struct sigaction *, struct sigaction *); + hidden void __unmapself(void *, size_t); +@@ -169,14 +170,14 @@ static inline void __wake(volatile void *addr, int cnt, int priv) + { + if (priv) priv = FUTEX_PRIVATE; + if (cnt<0) cnt = INT_MAX; +- __syscall(SYS_futex, addr, FUTEX_WAKE|priv, cnt) != -ENOSYS || +- __syscall(SYS_futex, addr, FUTEX_WAKE, cnt); ++ __syscall(SYS_futex, addr, FUTEX_WAKE|priv, cnt, 0, 0, 0) != -ENOSYS || ++ __syscall(SYS_futex, addr, FUTEX_WAKE, cnt, 0, 0, 0); + } + static inline void __futexwait(volatile void *addr, int val, int priv) + { + if (priv) priv = FUTEX_PRIVATE; +- __syscall(SYS_futex, addr, FUTEX_WAIT|priv, val, 0) != -ENOSYS || +- __syscall(SYS_futex, addr, FUTEX_WAIT, val, 0); ++ __syscall(SYS_futex, addr, FUTEX_WAIT|priv, val, 0, 0, 0) != -ENOSYS || ++ __syscall(SYS_futex, addr, FUTEX_WAIT, val, 0, 0, 0); + } + + hidden void __acquire_ptc(void); +diff --git a/src/internal/syscall.h b/src/internal/syscall.h +index 33d981f..590b6cb 100644 +--- a/src/internal/syscall.h ++++ b/src/internal/syscall.h +@@ -23,9 +23,14 @@ + typedef long syscall_arg_t; + #endif + +-hidden long __syscall_ret(unsigned long), +- __syscall_cp(syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, +- syscall_arg_t, syscall_arg_t, syscall_arg_t); ++hidden long __syscall_ret(unsigned long); ++hidden long __syscall_cp_c0(syscall_arg_t); ++hidden long __syscall_cp_c1(syscall_arg_t, syscall_arg_t); ++hidden long __syscall_cp_c2(syscall_arg_t, syscall_arg_t, syscall_arg_t); ++hidden long __syscall_cp_c3(syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t); ++hidden long __syscall_cp_c4(syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t); ++hidden long __syscall_cp_c5(syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t); ++hidden long __syscall_cp_c6(syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t); + + #define __syscall1(n,a) __syscall1(n,__scc(a)) + #define __syscall2(n,a,b) __syscall2(n,__scc(a),__scc(b)) +@@ -44,36 +49,22 @@ hidden long __syscall_ret(unsigned long), + #define __syscall(...) __SYSCALL_DISP(__syscall,__VA_ARGS__) + #define syscall(...) __syscall_ret(__syscall(__VA_ARGS__)) + +-#define socketcall(nm,a,b,c,d,e,f) __syscall_ret(__socketcall(nm,a,b,c,d,e,f)) +-#define socketcall_cp(nm,a,b,c,d,e,f) __syscall_ret(__socketcall_cp(nm,a,b,c,d,e,f)) ++#define socketcall(...) __syscall_ret(__socketcall(__VA_ARGS__)) ++#define socketcall_cp(...) __syscall_ret(__socketcall_cp(__VA_ARGS__)) + +-#define __syscall_cp0(n) (__syscall_cp)(n,0,0,0,0,0,0) +-#define __syscall_cp1(n,a) (__syscall_cp)(n,__scc(a),0,0,0,0,0) +-#define __syscall_cp2(n,a,b) (__syscall_cp)(n,__scc(a),__scc(b),0,0,0,0) +-#define __syscall_cp3(n,a,b,c) (__syscall_cp)(n,__scc(a),__scc(b),__scc(c),0,0,0) +-#define __syscall_cp4(n,a,b,c,d) (__syscall_cp)(n,__scc(a),__scc(b),__scc(c),__scc(d),0,0) +-#define __syscall_cp5(n,a,b,c,d,e) (__syscall_cp)(n,__scc(a),__scc(b),__scc(c),__scc(d),__scc(e),0) +-#define __syscall_cp6(n,a,b,c,d,e,f) (__syscall_cp)(n,__scc(a),__scc(b),__scc(c),__scc(d),__scc(e),__scc(f)) ++#define __syscall_cp0(n) __syscall_cp_c0(n) ++#define __syscall_cp1(n,a) __syscall_cp_c1(n,__scc(a)) ++#define __syscall_cp2(n,a,b) __syscall_cp_c2(n,__scc(a),__scc(b)) ++#define __syscall_cp3(n,a,b,c) __syscall_cp_c3(n,__scc(a),__scc(b),__scc(c)) ++#define __syscall_cp4(n,a,b,c,d) __syscall_cp_c4(n,__scc(a),__scc(b),__scc(c),__scc(d)) ++#define __syscall_cp5(n,a,b,c,d,e) __syscall_cp_c5(n,__scc(a),__scc(b),__scc(c),__scc(d),__scc(e)) ++#define __syscall_cp6(n,a,b,c,d,e,f) __syscall_cp_c6(n,__scc(a),__scc(b),__scc(c),__scc(d),__scc(e),__scc(f)) + + #define __syscall_cp(...) __SYSCALL_DISP(__syscall_cp,__VA_ARGS__) + #define syscall_cp(...) __syscall_ret(__syscall_cp(__VA_ARGS__)) + +-static inline long __alt_socketcall(int sys, int sock, int cp, syscall_arg_t a, syscall_arg_t b, syscall_arg_t c, syscall_arg_t d, syscall_arg_t e, syscall_arg_t f) +-{ +- long r; +- if (cp) r = __syscall_cp(sys, a, b, c, d, e, f); +- else r = __syscall(sys, a, b, c, d, e, f); +- if (r != -ENOSYS) return r; +-#ifdef SYS_socketcall +- if (cp) r = __syscall_cp(SYS_socketcall, sock, ((long[6]){a, b, c, d, e, f})); +- else r = __syscall(SYS_socketcall, sock, ((long[6]){a, b, c, d, e, f})); +-#endif +- return r; +-} +-#define __socketcall(nm, a, b, c, d, e, f) __alt_socketcall(SYS_##nm, __SC_##nm, 0, \ +- __scc(a), __scc(b), __scc(c), __scc(d), __scc(e), __scc(f)) +-#define __socketcall_cp(nm, a, b, c, d, e, f) __alt_socketcall(SYS_##nm, __SC_##nm, 1, \ +- __scc(a), __scc(b), __scc(c), __scc(d), __scc(e), __scc(f)) ++#define __socketcall(nm, ...) __syscall(SYS_##nm, __VA_ARGS__) ++#define __socketcall_cp(nm, ...) __syscall_cp(SYS_##nm, __VA_ARGS__) + + /* fixup legacy 16-bit junk */ + +@@ -374,14 +365,14 @@ static inline long __alt_socketcall(int sys, int sock, int cp, syscall_arg_t a, + #endif + + #ifdef SYS_open +-#define __sys_open2(x,pn,fl) __syscall2(SYS_open, pn, (fl)|O_LARGEFILE) ++#define __sys_open2(x,pn,fl) __syscall3(SYS_open, pn, (fl)|O_LARGEFILE, 0) + #define __sys_open3(x,pn,fl,mo) __syscall3(SYS_open, pn, (fl)|O_LARGEFILE, mo) +-#define __sys_open_cp2(x,pn,fl) __syscall_cp2(SYS_open, pn, (fl)|O_LARGEFILE) ++#define __sys_open_cp2(x,pn,fl) __syscall_cp3(SYS_open, pn, (fl)|O_LARGEFILE, 0) + #define __sys_open_cp3(x,pn,fl,mo) __syscall_cp3(SYS_open, pn, (fl)|O_LARGEFILE, mo) + #else +-#define __sys_open2(x,pn,fl) __syscall3(SYS_openat, AT_FDCWD, pn, (fl)|O_LARGEFILE) ++#define __sys_open2(x,pn,fl) __syscall4(SYS_openat, AT_FDCWD, pn, (fl)|O_LARGEFILE, 0) + #define __sys_open3(x,pn,fl,mo) __syscall4(SYS_openat, AT_FDCWD, pn, (fl)|O_LARGEFILE, mo) +-#define __sys_open_cp2(x,pn,fl) __syscall_cp3(SYS_openat, AT_FDCWD, pn, (fl)|O_LARGEFILE) ++#define __sys_open_cp2(x,pn,fl) __syscall_cp4(SYS_openat, AT_FDCWD, pn, (fl)|O_LARGEFILE, 0) + #define __sys_open_cp3(x,pn,fl,mo) __syscall_cp4(SYS_openat, AT_FDCWD, pn, (fl)|O_LARGEFILE, mo) + #endif + +diff --git a/src/linux/clone.c b/src/linux/clone.c +index 257c1ce..e3e8aa0 100644 +--- a/src/linux/clone.c ++++ b/src/linux/clone.c +@@ -31,7 +31,7 @@ int clone(int (*func)(void *), void *stack, int flags, void *arg, ...) + /* Flags that produce an invalid thread/TLS state are disallowed. */ + int badflags = CLONE_THREAD | CLONE_SETTLS | CLONE_CHILD_CLEARTID; + +- if ((flags & badflags) || !stack) ++ if ((flags & badflags) || (!stack && !(flags & CLONE_VFORK))) + return __syscall_ret(-EINVAL); + + va_start(ap, arg); +@@ -63,3 +63,22 @@ int clone(int (*func)(void *), void *stack, int flags, void *arg, ...) + __restore_sigs(&csa.sigmask); + return __syscall_ret(ret); + } ++ ++int __clone(int (*fn)(void *), void *stack, int flags, void *arg, ...) ++{ ++ /* Avoid calling assembler functions with va_args (fails on Wasm). */ ++ va_list ap; ++ pid_t *ptid = 0, *ctid = 0; ++ void *tls = 0; ++ ++ va_start(ap, arg); ++ if (flags & (CLONE_PIDFD | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID)) ++ ptid = va_arg(ap, pid_t *); ++ if (flags & CLONE_CHILD_SETTID) { ++ tls = va_arg(ap, void *); ++ ctid = va_arg(ap, pid_t *); ++ } ++ va_end(ap); ++ ++ return _Clone(fn, stack, flags, arg, ptid, tls, ctid); ++} +diff --git a/src/linux/reboot.c b/src/linux/reboot.c +index 7f12af7..f131745 100644 +--- a/src/linux/reboot.c ++++ b/src/linux/reboot.c +@@ -3,5 +3,5 @@ + + int reboot(int type) + { +- return syscall(SYS_reboot, 0xfee1dead, 672274793, type); ++ return syscall(SYS_reboot, 0xfee1dead, 672274793, type, 0); + } +diff --git a/src/misc/syscall.c b/src/misc/syscall.c +index 6f3ef65..471a9b2 100644 +--- a/src/misc/syscall.c ++++ b/src/misc/syscall.c +@@ -5,17 +5,57 @@ + + #undef syscall + +-long syscall(long n, ...) ++long syscall(long count, long n, ...) + { + va_list ap; + syscall_arg_t a,b,c,d,e,f; + va_start(ap, n); +- a=va_arg(ap, syscall_arg_t); +- b=va_arg(ap, syscall_arg_t); +- c=va_arg(ap, syscall_arg_t); +- d=va_arg(ap, syscall_arg_t); +- e=va_arg(ap, syscall_arg_t); +- f=va_arg(ap, syscall_arg_t); ++ switch (count) { ++ case 0: ++ va_end(ap); ++ return __syscall_ret(__syscall(n)); ++ case 1: ++ a = va_arg(ap, syscall_arg_t); ++ va_end(ap); ++ return __syscall_ret(__syscall(n, a)); ++ case 2: ++ a = va_arg(ap, syscall_arg_t); ++ b = va_arg(ap, syscall_arg_t); ++ va_end(ap); ++ return __syscall_ret(__syscall(n, a, b)); ++ case 3: ++ a = va_arg(ap, syscall_arg_t); ++ b = va_arg(ap, syscall_arg_t); ++ c = va_arg(ap, syscall_arg_t); ++ va_end(ap); ++ return __syscall_ret(__syscall(n, a, b, c)); ++ case 4: ++ a = va_arg(ap, syscall_arg_t); ++ b = va_arg(ap, syscall_arg_t); ++ c = va_arg(ap, syscall_arg_t); ++ d = va_arg(ap, syscall_arg_t); ++ va_end(ap); ++ return __syscall_ret(__syscall(n, a, b, c, d)); ++ case 5: ++ a = va_arg(ap, syscall_arg_t); ++ b = va_arg(ap, syscall_arg_t); ++ c = va_arg(ap, syscall_arg_t); ++ d = va_arg(ap, syscall_arg_t); ++ e = va_arg(ap, syscall_arg_t); ++ va_end(ap); ++ return __syscall_ret(__syscall(n, a, b, c, d, e)); ++ case 6: ++ a = va_arg(ap, syscall_arg_t); ++ b = va_arg(ap, syscall_arg_t); ++ c = va_arg(ap, syscall_arg_t); ++ d = va_arg(ap, syscall_arg_t); ++ e = va_arg(ap, syscall_arg_t); ++ f = va_arg(ap, syscall_arg_t); ++ va_end(ap); ++ return __syscall_ret(__syscall(n, a, b, c, d, e, f)); ++ default: ++ break; ++ } + va_end(ap); +- return __syscall_ret(__syscall(n,a,b,c,d,e,f)); ++ return -ENOSYS; + } +diff --git a/src/misc/syscalls.c b/src/misc/syscalls.c +new file mode 100644 +index 0000000..e69de29 +diff --git a/src/misc/wasm/syscalls.s b/src/misc/wasm/syscalls.s +new file mode 100644 +index 0000000..70e5069 +--- /dev/null ++++ b/src/misc/wasm/syscalls.s +@@ -0,0 +1,123 @@ ++.globaltype __stack_pointer, i32 ++.globaltype __tls_base, i32 ++.functype __wasm_syscall_0(i32, i32, i32) -> (i32) ++.functype __wasm_syscall_1(i32, i32, i32, i32) -> (i32) ++.functype __wasm_syscall_2(i32, i32, i32, i32, i32) -> (i32) ++.functype __wasm_syscall_3(i32, i32, i32, i32, i32, i32) -> (i32) ++.functype __wasm_syscall_4(i32, i32, i32, i32, i32, i32, i32) -> (i32) ++.functype __wasm_syscall_5(i32, i32, i32, i32, i32, i32, i32, i32) -> (i32) ++.functype __wasm_syscall_6(i32, i32, i32, i32, i32, i32, i32, i32, i32) -> (i32) ++ ++#define haxx ++ ++.macro __SYSCALL_HEAD ++ global.get __stack_pointer ++ global.get __tls_base ++ local.get 0 ++.endm ++ ++.macro __SYSCALL_FOOT ++ /* ++ * The kernel never changes __stack_pointer or __tls_base during most ++ * syscalls. The ones where it does (clone, signals, exec) are handled ++ * separately and don't go via this function, making it safe to ignore. ++ */ ++.endm ++ ++.globl __syscall0 ++.hidden __syscall0 ++__syscall0: ++ .functype __syscall0(i32) -> (i32) ++ ++ __SYSCALL_HEAD ++ call __wasm_syscall_0 ++ __SYSCALL_FOOT ++ ++ end_function ++ ++.globl __syscall1 ++.hidden __syscall1 ++__syscall1: ++ .functype __syscall1(i32, i32) -> (i32) ++ ++ __SYSCALL_HEAD ++ local.get 1 ++ call __wasm_syscall_1 ++ __SYSCALL_FOOT ++ ++ end_function ++ ++.globl __syscall2 ++.hidden __syscall2 ++__syscall2: ++ .functype __syscall2(i32, i32, i32) -> (i32) ++ ++ __SYSCALL_HEAD ++ local.get 1 ++ local.get 2 ++ call __wasm_syscall_2 ++ __SYSCALL_FOOT ++ ++ end_function ++ ++.globl __syscall3 ++.hidden __syscall3 ++__syscall3: ++ .functype __syscall3(i32, i32, i32, i32) -> (i32) ++ ++ __SYSCALL_HEAD ++ local.get 1 ++ local.get 2 ++ local.get 3 ++ call __wasm_syscall_3 ++ __SYSCALL_FOOT ++ ++ end_function ++ ++.globl __syscall4 ++.hidden __syscall4 ++__syscall4: ++ .functype __syscall4(i32, i32, i32, i32, i32) -> (i32) ++ ++ __SYSCALL_HEAD ++ local.get 1 ++ local.get 2 ++ local.get 3 ++ local.get 4 ++ call __wasm_syscall_4 ++ __SYSCALL_FOOT ++ ++ end_function ++ ++.globl __syscall5 ++.hidden __syscall5 ++__syscall5: ++ .functype __syscall5(i32, i32, i32, i32, i32, i32) -> (i32) ++ ++ __SYSCALL_HEAD ++ local.get 1 ++ local.get 2 ++ local.get 3 ++ local.get 4 ++ local.get 5 ++ call __wasm_syscall_5 ++ __SYSCALL_FOOT ++ ++ end_function ++ ++.globl __syscall6 ++.hidden __syscall6 ++__syscall6: ++ .functype __syscall6(i32, i32, i32, i32, i32, i32, i32) -> (i32) ++ ++ __SYSCALL_HEAD ++ local.get 1 ++ local.get 2 ++ local.get 3 ++ local.get 4 ++ local.get 5 ++ local.get 6 ++ call __wasm_syscall_6 ++ __SYSCALL_FOOT ++ ++ end_function +diff --git a/src/network/accept.c b/src/network/accept.c +index a92406f..5fa901a 100644 +--- a/src/network/accept.c ++++ b/src/network/accept.c +@@ -3,5 +3,5 @@ + + int accept(int fd, struct sockaddr *restrict addr, socklen_t *restrict len) + { +- return socketcall_cp(accept, fd, addr, len, 0, 0, 0); ++ return socketcall_cp(accept, fd, addr, len); + } +diff --git a/src/network/accept4.c b/src/network/accept4.c +index 765a38e..91575d1 100644 +--- a/src/network/accept4.c ++++ b/src/network/accept4.c +@@ -7,7 +7,7 @@ + int accept4(int fd, struct sockaddr *restrict addr, socklen_t *restrict len, int flg) + { + if (!flg) return accept(fd, addr, len); +- int ret = socketcall_cp(accept4, fd, addr, len, flg, 0, 0); ++ int ret = socketcall_cp(accept4, fd, addr, len, flg); + if (ret>=0 || (errno != ENOSYS && errno != EINVAL)) return ret; + if (flg & ~(SOCK_CLOEXEC|SOCK_NONBLOCK)) { + errno = EINVAL; +diff --git a/src/network/bind.c b/src/network/bind.c +index 07bb669..0ff11d9 100644 +--- a/src/network/bind.c ++++ b/src/network/bind.c +@@ -3,5 +3,5 @@ + + int bind(int fd, const struct sockaddr *addr, socklen_t len) + { +- return socketcall(bind, fd, addr, len, 0, 0, 0); ++ return socketcall(bind, fd, addr, len); + } +diff --git a/src/network/connect.c b/src/network/connect.c +index 289127b..ace2add 100644 +--- a/src/network/connect.c ++++ b/src/network/connect.c +@@ -3,5 +3,5 @@ + + int connect(int fd, const struct sockaddr *addr, socklen_t len) + { +- return socketcall_cp(connect, fd, addr, len, 0, 0, 0); ++ return socketcall_cp(connect, fd, addr, len); + } +diff --git a/src/network/getpeername.c b/src/network/getpeername.c +index 6567b45..fc366a3 100644 +--- a/src/network/getpeername.c ++++ b/src/network/getpeername.c +@@ -3,5 +3,5 @@ + + int getpeername(int fd, struct sockaddr *restrict addr, socklen_t *restrict len) + { +- return socketcall(getpeername, fd, addr, len, 0, 0, 0); ++ return socketcall(getpeername, fd, addr, len); + } +diff --git a/src/network/getsockname.c b/src/network/getsockname.c +index 7885fc1..2a4d3eb 100644 +--- a/src/network/getsockname.c ++++ b/src/network/getsockname.c +@@ -3,5 +3,5 @@ + + int getsockname(int fd, struct sockaddr *restrict addr, socklen_t *restrict len) + { +- return socketcall(getsockname, fd, addr, len, 0, 0, 0); ++ return socketcall(getsockname, fd, addr, len); + } +diff --git a/src/network/getsockopt.c b/src/network/getsockopt.c +index d3640d9..aaf0b7c 100644 +--- a/src/network/getsockopt.c ++++ b/src/network/getsockopt.c +@@ -8,7 +8,7 @@ int getsockopt(int fd, int level, int optname, void *restrict optval, socklen_t + long tv32[2]; + struct timeval *tv; + +- int r = __socketcall(getsockopt, fd, level, optname, optval, optlen, 0); ++ int r = __socketcall(getsockopt, fd, level, optname, optval, optlen); + + if (r==-ENOPROTOOPT) switch (level) { + case SOL_SOCKET: +@@ -20,7 +20,7 @@ int getsockopt(int fd, int level, int optname, void *restrict optval, socklen_t + if (optname==SO_RCVTIMEO) optname=SO_RCVTIMEO_OLD; + if (optname==SO_SNDTIMEO) optname=SO_SNDTIMEO_OLD; + r = __socketcall(getsockopt, fd, level, optname, +- tv32, (socklen_t[]){sizeof tv32}, 0); ++ tv32, (socklen_t[]){sizeof tv32}); + if (r<0) break; + tv = optval; + tv->tv_sec = tv32[0]; +@@ -33,7 +33,7 @@ int getsockopt(int fd, int level, int optname, void *restrict optval, socklen_t + if (optname==SO_TIMESTAMP) optname=SO_TIMESTAMP_OLD; + if (optname==SO_TIMESTAMPNS) optname=SO_TIMESTAMPNS_OLD; + r = __socketcall(getsockopt, fd, level, +- optname, optval, optlen, 0); ++ optname, optval, optlen); + break; + } + } +diff --git a/src/network/listen.c b/src/network/listen.c +index f84ad03..87eec05 100644 +--- a/src/network/listen.c ++++ b/src/network/listen.c +@@ -3,5 +3,5 @@ + + int listen(int fd, int backlog) + { +- return socketcall(listen, fd, backlog, 0, 0, 0, 0); ++ return socketcall(listen, fd, backlog); + } +diff --git a/src/network/recvmsg.c b/src/network/recvmsg.c +index 0364162..8f9b81d 100644 +--- a/src/network/recvmsg.c ++++ b/src/network/recvmsg.c +@@ -59,7 +59,7 @@ ssize_t recvmsg(int fd, struct msghdr *msg, int flags) + msg = &h; + } + #endif +- r = socketcall_cp(recvmsg, fd, msg, flags, 0, 0, 0); ++ r = socketcall_cp(recvmsg, fd, msg, flags); + if (r >= 0) __convert_scm_timestamps(msg, orig_controllen); + #if LONG_MAX > INT_MAX + if (orig) *orig = h; +diff --git a/src/network/sendmsg.c b/src/network/sendmsg.c +index acdfdf2..611186d 100644 +--- a/src/network/sendmsg.c ++++ b/src/network/sendmsg.c +@@ -28,5 +28,5 @@ ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) + } + } + #endif +- return socketcall_cp(sendmsg, fd, msg, flags, 0, 0, 0); ++ return socketcall_cp(sendmsg, fd, msg, flags); + } +diff --git a/src/network/setsockopt.c b/src/network/setsockopt.c +index 612a194..d97ce57 100644 +--- a/src/network/setsockopt.c ++++ b/src/network/setsockopt.c +@@ -12,7 +12,7 @@ int setsockopt(int fd, int level, int optname, const void *optval, socklen_t opt + time_t s; + suseconds_t us; + +- int r = __socketcall(setsockopt, fd, level, optname, optval, optlen, 0); ++ int r = __socketcall(setsockopt, fd, level, optname, optval, optlen); + + if (r==-ENOPROTOOPT) switch (level) { + case SOL_SOCKET: +@@ -30,7 +30,7 @@ int setsockopt(int fd, int level, int optname, const void *optval, socklen_t opt + if (optname==SO_SNDTIMEO) optname=SO_SNDTIMEO_OLD; + + r = __socketcall(setsockopt, fd, level, optname, +- ((long[]){s, CLAMP(us)}), 2*sizeof(long), 0); ++ ((long[]){s, CLAMP(us)}), 2*sizeof(long)); + break; + case SO_TIMESTAMP: + case SO_TIMESTAMPNS: +@@ -38,7 +38,7 @@ int setsockopt(int fd, int level, int optname, const void *optval, socklen_t opt + if (optname==SO_TIMESTAMP) optname=SO_TIMESTAMP_OLD; + if (optname==SO_TIMESTAMPNS) optname=SO_TIMESTAMPNS_OLD; + r = __socketcall(setsockopt, fd, level, +- optname, optval, optlen, 0); ++ optname, optval, optlen); + break; + } + } +diff --git a/src/network/shutdown.c b/src/network/shutdown.c +index 10ca21a..fa51bfc 100644 +--- a/src/network/shutdown.c ++++ b/src/network/shutdown.c +@@ -3,5 +3,5 @@ + + int shutdown(int fd, int how) + { +- return socketcall(shutdown, fd, how, 0, 0, 0, 0); ++ return socketcall(shutdown, fd, how); + } +diff --git a/src/network/socket.c b/src/network/socket.c +index afa1a7f..d2beb31 100644 +--- a/src/network/socket.c ++++ b/src/network/socket.c +@@ -5,12 +5,11 @@ + + int socket(int domain, int type, int protocol) + { +- int s = __socketcall(socket, domain, type, protocol, 0, 0, 0); ++ int s = __socketcall(socket, domain, type, protocol); + if ((s==-EINVAL || s==-EPROTONOSUPPORT) + && (type&(SOCK_CLOEXEC|SOCK_NONBLOCK))) { + s = __socketcall(socket, domain, +- type & ~(SOCK_CLOEXEC|SOCK_NONBLOCK), +- protocol, 0, 0, 0); ++ type & ~(SOCK_CLOEXEC|SOCK_NONBLOCK), protocol); + if (s < 0) return __syscall_ret(s); + if (type & SOCK_CLOEXEC) + __syscall(SYS_fcntl, s, F_SETFD, FD_CLOEXEC); +diff --git a/src/network/socketpair.c b/src/network/socketpair.c +index f348962..28603ed 100644 +--- a/src/network/socketpair.c ++++ b/src/network/socketpair.c +@@ -5,12 +5,11 @@ + + int socketpair(int domain, int type, int protocol, int fd[2]) + { +- int r = socketcall(socketpair, domain, type, protocol, fd, 0, 0); ++ int r = socketcall(socketpair, domain, type, protocol, fd); + if (r<0 && (errno==EINVAL || errno==EPROTONOSUPPORT) + && (type&(SOCK_CLOEXEC|SOCK_NONBLOCK))) { + r = socketcall(socketpair, domain, +- type & ~(SOCK_CLOEXEC|SOCK_NONBLOCK), +- protocol, fd, 0, 0); ++ type & ~(SOCK_CLOEXEC|SOCK_NONBLOCK), protocol, fd); + if (r < 0) return r; + if (type & SOCK_CLOEXEC) { + __syscall(SYS_fcntl, fd[0], F_SETFD, FD_CLOEXEC); +diff --git a/src/process/posix_spawn.c b/src/process/posix_spawn.c +index 8294598..668694d 100644 +--- a/src/process/posix_spawn.c ++++ b/src/process/posix_spawn.c +@@ -175,12 +175,13 @@ int posix_spawn(pid_t *restrict res, const char *restrict path, + char stack[1024+PATH_MAX]; + int ec=0, cs; + struct args args; ++ const posix_spawnattr_t dummy = {0}; + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); + + args.path = path; + args.fa = fa; +- args.attr = attr ? attr : &(const posix_spawnattr_t){0}; ++ args.attr = attr ? attr : &dummy; + args.argv = argv; + args.envp = envp; + pthread_sigmask(SIG_BLOCK, SIGALL_SET, &args.oldmask); +diff --git a/src/process/vfork.c b/src/process/vfork.c +index d430c13..0b16b9d 100644 +--- a/src/process/vfork.c ++++ b/src/process/vfork.c +@@ -1,14 +1,12 @@ + #define _GNU_SOURCE +-#include ++#include ++#include + #include + #include "syscall.h" + + pid_t vfork(void) + { +- /* vfork syscall cannot be made from C code */ +-#ifdef SYS_fork +- return syscall(SYS_fork); +-#else +- return syscall(SYS_clone, SIGCHLD, 0); +-#endif ++ fprintf(stderr, ++ "vfork() is not implemented yet, use clone() instead!\n"); ++ abort(); + } +diff --git a/src/setjmp/longjmp.c b/src/setjmp/longjmp.c +index e69de29..d03b171 100644 +--- a/src/setjmp/longjmp.c ++++ b/src/setjmp/longjmp.c +@@ -0,0 +1,19 @@ ++ ++#include ++#include ++ ++/* ++ * setjmp/sigsetjmp/longjmp is currently not supported (but it can be, ++ * LLVM has support for it with some fixup logic needed). Since some ++ * programs use setjmp but only longjmp when for example an error occurs, ++ * we can allow setjmp/sigsetjmp (by doing nothing). When it actually ++ * wants to longjmp we have to abort, let's just hope that never happens. ++ */ ++_Noreturn void longjmp(jmp_buf env, int status); ++{ ++ (void)env; ++ (void)status; ++ ++ fprintf(stderr, "longjmp() is not implemented yet!\n"); ++ abort(); ++} +diff --git a/src/setjmp/wasm/longjmp.S b/src/setjmp/wasm/longjmp.S +new file mode 100644 +index 0000000..192b0b3 +--- /dev/null ++++ b/src/setjmp/wasm/longjmp.S +@@ -0,0 +1,13 @@ ++.functype abort() -> () ++ ++.globl longjmp ++longjmp: ++ .functype longjmp(i32, i32) -> () ++ # setjmp/sigsetjmp/longjmp is currently not supported (but it can be, ++ # LLVM has support for it with some fixup logic needed). Since some ++ # programs use setjmp but only longjmp when for example an error occurs, ++ # we can allow setjmp/sigsetjmp (by doing nothing). When it actually ++ # wants to longjmp we have to abort, let's just hope that never happens. ++ call abort ++ ++ end_function +diff --git a/src/setjmp/wasm/setjmp.S b/src/setjmp/wasm/setjmp.S +new file mode 100644 +index 0000000..50b8983 +--- /dev/null ++++ b/src/setjmp/wasm/setjmp.S +@@ -0,0 +1,5 @@ ++.globl setjmp ++setjmp: ++ .functype setjmp(i32) -> () ++ # We allow this, but do nothing. longjmp() is forbidden though. ++ end_function +diff --git a/src/signal/wasm/restore.s b/src/signal/wasm/restore.s +new file mode 100644 +index 0000000..f14b43c +--- /dev/null ++++ b/src/signal/wasm/restore.s +@@ -0,0 +1,98 @@ ++.globaltype __stack_pointer, i32 ++.globaltype __tls_base, i32 ++.functype __wasm_syscall_0(i32, i32, i32) -> (i32) ++ ++.globl __restore_rt ++.hidden __restore_rt ++__restore_rt: ++ .functype __restore_rt() -> () ++ ++ global.get __stack_pointer ++ global.get __tls_base ++ i32.const 139 # SYS_rt_sigreturn ++ call __wasm_syscall_0 ++ unreachable ++ ++ end_function ++ ++.globl __restore ++.hidden __restore ++__restore: ++ .functype __restore() -> () ++ ++ call __restore_rt ++ ++ end_function ++ ++/* TODO: Move this to a better place... */ ++.globl __libc_handle_signal ++__libc_handle_signal: ++ .functype __libc_handle_signal() -> () ++ /* 0: sig_param */ ++ /* 1: info_param */ ++ /* 2: uc_param */ ++ /* 3: sa_handler */ ++ .local i32, i32, i32, i32 ++ ++ /* sig_param */ ++ global.get __stack_pointer ++ i32.load 0 ++ /* ++ * LLVM complains for some reason if we leave this on the Wasm stack. We ++ * put it in a local instead and then everything works, somehow... ++ */ ++ local.set 0 ++ ++ /* info_param */ ++ global.get __stack_pointer ++ i32.const 4 ++ i32.add ++ i32.load 0 ++ local.set 1 ++ ++ /* uc_param */ ++ global.get __stack_pointer ++ i32.const 8 ++ i32.add ++ i32.load 0 ++ local.set 2 ++ ++ /* sa_handler */ ++ global.get __stack_pointer ++ i32.const 12 ++ i32.add ++ i32.load 0 ++ local.set 3 ++ ++ /* (__stack_pointer is already 16-byte aligned by the kernel.) */ ++ ++ /* !SA_SIGINFO */ ++ block ++ local.get 1 ++ br_if 0 ++ ++ local.get 0 /* sig_param */ ++ local.get 3 /* sa_handler cast to handler */ ++ /* handler(sig) */ ++ call_indirect (i32) -> () ++ end_block ++ ++ /* SA_SIGINFO */ ++ block ++ local.get 1 ++ i32.eqz ++ br_if 0 ++ ++ local.get 0 /* sig _param*/ ++ local.get 1 /* info_param */ ++ local.get 2 /* uc_param */ ++ local.get 3 /* sa_handler cast to sigaction */ ++ /* sigaction(sig_param, info_param, uc_param) */ ++ call_indirect (i32, i32, i32) -> () ++ end_block ++ ++ /* Unless the handler itself calls SYS_rt_sigreturn we have to do it. */ ++ call __restore_rt ++ unreachable ++ ++ end_function +diff --git a/src/signal/wasm/sigsetjmp.S b/src/signal/wasm/sigsetjmp.S +new file mode 100644 +index 0000000..83dd15d +--- /dev/null ++++ b/src/signal/wasm/sigsetjmp.S +@@ -0,0 +1,5 @@ ++.globl sigsetjmp ++sigsetjmp: ++ .functype sigsetjmp(i32, i32) -> () ++ # We allow this, but do nothing. longjmp() is forbidden though. ++ end_function +diff --git a/src/thread/__syscall_cp.c b/src/thread/__syscall_cp.c +index 42a0167..f33c5d0 100644 +--- a/src/thread/__syscall_cp.c ++++ b/src/thread/__syscall_cp.c +@@ -1,20 +1,2 @@ + #include "pthread_impl.h" + #include "syscall.h" +- +-hidden long __syscall_cp_c(); +- +-static long sccp(syscall_arg_t nr, +- syscall_arg_t u, syscall_arg_t v, syscall_arg_t w, +- syscall_arg_t x, syscall_arg_t y, syscall_arg_t z) +-{ +- return __syscall(nr, u, v, w, x, y, z); +-} +- +-weak_alias(sccp, __syscall_cp_c); +- +-long (__syscall_cp)(syscall_arg_t nr, +- syscall_arg_t u, syscall_arg_t v, syscall_arg_t w, +- syscall_arg_t x, syscall_arg_t y, syscall_arg_t z) +-{ +- return __syscall_cp_c(nr, u, v, w, x, y, z); +-} +diff --git a/src/thread/__timedwait.c b/src/thread/__timedwait.c +index 666093b..ffa12cc 100644 +--- a/src/thread/__timedwait.c ++++ b/src/thread/__timedwait.c +@@ -17,13 +17,13 @@ static int __futex4_cp(volatile void *addr, int op, int val, const struct timesp + r = -ENOSYS; + if (SYS_futex == SYS_futex_time64 || !IS32BIT(s)) + r = __syscall_cp(SYS_futex_time64, addr, op, val, +- to ? ((long long[]){s, ns}) : 0); ++ to ? ((long long[]){s, ns}) : 0, 0, 0); + if (SYS_futex == SYS_futex_time64 || r!=-ENOSYS) return r; + to = to ? (void *)(long[]){CLAMP(s), ns} : 0; + #endif +- r = __syscall_cp(SYS_futex, addr, op, val, to); ++ r = __syscall_cp(SYS_futex, addr, op, val, to, 0, 0); + if (r != -ENOSYS) return r; +- return __syscall_cp(SYS_futex, addr, op & ~FUTEX_PRIVATE, val, to); ++ return __syscall_cp(SYS_futex, addr, op & ~FUTEX_PRIVATE, val, to, 0, 0); + } + + static volatile int dummy = 0; +diff --git a/src/thread/__wait.c b/src/thread/__wait.c +index dc33c1a..847de07 100644 +--- a/src/thread/__wait.c ++++ b/src/thread/__wait.c +@@ -10,8 +10,8 @@ void __wait(volatile int *addr, volatile int *waiters, int val, int priv) + } + if (waiters) a_inc(waiters); + while (*addr==val) { +- __syscall(SYS_futex, addr, FUTEX_WAIT|priv, val, 0) != -ENOSYS +- || __syscall(SYS_futex, addr, FUTEX_WAIT, val, 0); ++ __syscall(SYS_futex, addr, FUTEX_WAIT|priv, val, 0, 0, 0) != -ENOSYS ++ || __syscall(SYS_futex, addr, FUTEX_WAIT, val, 0, 0, 0); + } + if (waiters) a_dec(waiters); + } +diff --git a/src/thread/pthread_barrier_wait.c b/src/thread/pthread_barrier_wait.c +index cc2a8bb..e2fa679 100644 +--- a/src/thread/pthread_barrier_wait.c ++++ b/src/thread/pthread_barrier_wait.c +@@ -84,8 +84,8 @@ int pthread_barrier_wait(pthread_barrier_t *b) + a_spin(); + a_inc(&inst->finished); + while (inst->finished == 1) +- __syscall(SYS_futex,&inst->finished,FUTEX_WAIT|FUTEX_PRIVATE,1,0) != -ENOSYS +- || __syscall(SYS_futex,&inst->finished,FUTEX_WAIT,1,0); ++ __syscall(SYS_futex,&inst->finished,FUTEX_WAIT|FUTEX_PRIVATE,1,0,0,0) != -ENOSYS ++ || __syscall(SYS_futex,&inst->finished,FUTEX_WAIT,1,0,0,0); + return PTHREAD_BARRIER_SERIAL_THREAD; + } + +diff --git a/src/thread/pthread_cancel.c b/src/thread/pthread_cancel.c +index 139a6fc..4ba7b8e 100644 +--- a/src/thread/pthread_cancel.c ++++ b/src/thread/pthread_cancel.c +@@ -3,9 +3,7 @@ + #include "pthread_impl.h" + #include "syscall.h" + +-hidden long __cancel(), __syscall_cp_asm(), __syscall_cp_c(); +- +-long __cancel() ++hidden long __cancel() + { + pthread_t self = __pthread_self(); + if (self->canceldisable == PTHREAD_CANCEL_ENABLE || self->cancelasync) +@@ -14,27 +12,85 @@ long __cancel() + return -ECANCELED; + } + +-long __syscall_cp_asm(volatile void *, syscall_arg_t, +- syscall_arg_t, syscall_arg_t, syscall_arg_t, +- syscall_arg_t, syscall_arg_t, syscall_arg_t); ++hidden long __syscall_cp_asm0(volatile void *, syscall_arg_t); ++hidden long __syscall_cp_asm1(volatile void *, syscall_arg_t, syscall_arg_t); ++hidden long __syscall_cp_asm2(volatile void *, syscall_arg_t, syscall_arg_t, syscall_arg_t); ++hidden long __syscall_cp_asm3(volatile void *, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t); ++hidden long __syscall_cp_asm4(volatile void *, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t); ++hidden long __syscall_cp_asm5(volatile void *, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t); ++hidden long __syscall_cp_asm6(volatile void *, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t); ++ ++long __syscall_cp_asm0(volatile void *, syscall_arg_t) { abort(); /* TODO */ } ++long __syscall_cp_asm1(volatile void *, syscall_arg_t, syscall_arg_t) { abort(); /* TODO */ } ++long __syscall_cp_asm2(volatile void *, syscall_arg_t, syscall_arg_t, syscall_arg_t) { abort(); /* TODO */ } ++long __syscall_cp_asm3(volatile void *, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t) { abort(); /* TODO */ } ++long __syscall_cp_asm4(volatile void *, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t) { abort(); /* TODO */ } ++long __syscall_cp_asm5(volatile void *, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t) { abort(); /* TODO */ } ++long __syscall_cp_asm6(volatile void *, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t) { abort(); /* TODO */ } ++ ++hidden long __syscall_cp_c0(syscall_arg_t); ++hidden long __syscall_cp_c1(syscall_arg_t, syscall_arg_t); ++hidden long __syscall_cp_c2(syscall_arg_t, syscall_arg_t, syscall_arg_t); ++hidden long __syscall_cp_c3(syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t); ++hidden long __syscall_cp_c4(syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t); ++hidden long __syscall_cp_c5(syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t); ++hidden long __syscall_cp_c6(syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t); ++ ++/* It's a TODO to handle this... */ ++/* ++#define SYSCALL_CP_C(num, ...) do {\ ++ pthread_t self; \ ++ long r; \ ++ int st; \ ++ \ ++ if ((st=(self=__pthread_self())->canceldisable) \ ++ && (st==PTHREAD_CANCEL_DISABLE || nr==SYS_close)) \ ++ return __syscall(__VA_ARGS__); \ ++ \ ++ r = __syscall_cp_asm##num(&self->cancel, __VA_ARGS__); \ ++ if (r==-EINTR && nr!=SYS_close && self->cancel && \ ++ self->canceldisable != PTHREAD_CANCEL_DISABLE) \ ++ r = __cancel(); \ ++ return r; \ ++ } while(0) ++*/ ++#define SYSCALL_CP_C(num, ...) do {\ ++ return __syscall(__VA_ARGS__); \ ++ } while(0) ++ ++long __syscall_cp_c0(syscall_arg_t nr) ++{ ++ SYSCALL_CP_C(0, nr); ++} ++ ++long __syscall_cp_c1(syscall_arg_t nr, syscall_arg_t u) ++{ ++ SYSCALL_CP_C(1, nr, u); ++} ++ ++long __syscall_cp_c2(syscall_arg_t nr, syscall_arg_t u, syscall_arg_t v) ++{ ++ SYSCALL_CP_C(2, nr, u, v); ++} + +-long __syscall_cp_c(syscall_arg_t nr, +- syscall_arg_t u, syscall_arg_t v, syscall_arg_t w, +- syscall_arg_t x, syscall_arg_t y, syscall_arg_t z) ++long __syscall_cp_c3(syscall_arg_t nr, syscall_arg_t u, syscall_arg_t v, syscall_arg_t w) + { +- pthread_t self; +- long r; +- int st; +- +- if ((st=(self=__pthread_self())->canceldisable) +- && (st==PTHREAD_CANCEL_DISABLE || nr==SYS_close)) +- return __syscall(nr, u, v, w, x, y, z); +- +- r = __syscall_cp_asm(&self->cancel, nr, u, v, w, x, y, z); +- if (r==-EINTR && nr!=SYS_close && self->cancel && +- self->canceldisable != PTHREAD_CANCEL_DISABLE) +- r = __cancel(); +- return r; ++ SYSCALL_CP_C(3, nr, u, v, w); ++} ++ ++long __syscall_cp_c4(syscall_arg_t nr, syscall_arg_t u, syscall_arg_t v, syscall_arg_t w, syscall_arg_t x) ++{ ++ SYSCALL_CP_C(4, nr, u, v, w, x); ++} ++ ++long __syscall_cp_c5(syscall_arg_t nr, syscall_arg_t u, syscall_arg_t v, syscall_arg_t w, syscall_arg_t x, syscall_arg_t y) ++{ ++ SYSCALL_CP_C(5, nr, u, v, w, x, y); ++} ++ ++long __syscall_cp_c6(syscall_arg_t nr, syscall_arg_t u, syscall_arg_t v, syscall_arg_t w, syscall_arg_t x, syscall_arg_t y, syscall_arg_t z) ++{ ++ SYSCALL_CP_C(6, nr, u, v, w, x, y, z); + } + + static void _sigaddset(sigset_t *set, int sig) +@@ -49,7 +105,7 @@ static void cancel_handler(int sig, siginfo_t *si, void *ctx) + { + pthread_t self = __pthread_self(); + ucontext_t *uc = ctx; +- uintptr_t pc = uc->uc_mcontext.MC_PC; ++ uintptr_t pc; + + a_barrier(); + if (!self->cancel || self->canceldisable == PTHREAD_CANCEL_DISABLE) return; +@@ -61,6 +117,8 @@ static void cancel_handler(int sig, siginfo_t *si, void *ctx) + __cancel(); + } + ++#ifdef MC_PC ++ pc = uc->uc_mcontext.MC_PC; + if (pc >= (uintptr_t)__cp_begin && pc < (uintptr_t)__cp_end) { + uc->uc_mcontext.MC_PC = (uintptr_t)__cp_cancel; + #ifdef CANCEL_GOT +@@ -68,6 +126,10 @@ static void cancel_handler(int sig, siginfo_t *si, void *ctx) + #endif + return; + } ++#else ++ /* TODO: handle this. */ ++ abort(); ++#endif + + __syscall(SYS_tkill, self->tid, SIGCANCEL); + } +diff --git a/src/thread/pthread_cond_timedwait.c b/src/thread/pthread_cond_timedwait.c +index 6b76145..fc6793e 100644 +--- a/src/thread/pthread_cond_timedwait.c ++++ b/src/thread/pthread_cond_timedwait.c +@@ -49,8 +49,8 @@ static inline void unlock_requeue(volatile int *l, volatile int *r, int w) + { + a_store(l, 0); + if (w) __wake(l, 1, 1); +- else __syscall(SYS_futex, l, FUTEX_REQUEUE|FUTEX_PRIVATE, 0, 1, r) != -ENOSYS +- || __syscall(SYS_futex, l, FUTEX_REQUEUE, 0, 1, r); ++ else __syscall(SYS_futex, l, FUTEX_REQUEUE|FUTEX_PRIVATE, 0, 1, r, 0) != -ENOSYS ++ || __syscall(SYS_futex, l, FUTEX_REQUEUE, 0, 1, r, 0); + } + + enum { +diff --git a/src/thread/pthread_mutex_timedlock.c b/src/thread/pthread_mutex_timedlock.c +index 9279fc5..9615429 100644 +--- a/src/thread/pthread_mutex_timedlock.c ++++ b/src/thread/pthread_mutex_timedlock.c +@@ -11,11 +11,11 @@ static int __futex4(volatile void *addr, int op, int val, const struct timespec + int r = -ENOSYS; + if (SYS_futex == SYS_futex_time64 || !IS32BIT(s)) + r = __syscall(SYS_futex_time64, addr, op, val, +- to ? ((long long[]){s, ns}) : 0); ++ to ? ((long long[]){s, ns}) : 0, 0, 0); + if (SYS_futex == SYS_futex_time64 || r!=-ENOSYS) return r; + to = to ? (void *)(long[]){CLAMP(s), ns} : 0; + #endif +- return __syscall(SYS_futex, addr, op, val, to); ++ return __syscall(SYS_futex, addr, op, val, to, 0, 0); + } + + static int pthread_mutex_timedlock_pi(pthread_mutex_t *restrict m, const struct timespec *restrict at) +@@ -36,7 +36,7 @@ static int pthread_mutex_timedlock_pi(pthread_mutex_t *restrict m, const struct + /* Catch spurious success for non-robust mutexes. */ + if (!(type&4) && ((m->_m_lock & 0x40000000) || m->_m_waiters)) { + a_store(&m->_m_waiters, -1); +- __syscall(SYS_futex, &m->_m_lock, FUTEX_UNLOCK_PI|priv); ++ __syscall(SYS_futex, &m->_m_lock, FUTEX_UNLOCK_PI|priv, 0, 0, 0, 0); + self->robust_list.pending = 0; + break; + } +diff --git a/src/thread/pthread_mutex_trylock.c b/src/thread/pthread_mutex_trylock.c +index a24e7c5..fc6dc8d 100644 +--- a/src/thread/pthread_mutex_trylock.c ++++ b/src/thread/pthread_mutex_trylock.c +@@ -43,7 +43,7 @@ int __pthread_mutex_trylock_owner(pthread_mutex_t *m) + success: + if ((type&8) && m->_m_waiters) { + int priv = (type & 128) ^ 128; +- __syscall(SYS_futex, &m->_m_lock, FUTEX_UNLOCK_PI|priv); ++ __syscall(SYS_futex, &m->_m_lock, FUTEX_UNLOCK_PI|priv, 0, 0, 0, 0); + self->robust_list.pending = 0; + return (type&4) ? ENOTRECOVERABLE : EBUSY; + } +diff --git a/src/thread/pthread_mutex_unlock.c b/src/thread/pthread_mutex_unlock.c +index b66423e..69f1a9c 100644 +--- a/src/thread/pthread_mutex_unlock.c ++++ b/src/thread/pthread_mutex_unlock.c +@@ -33,7 +33,7 @@ int __pthread_mutex_unlock(pthread_mutex_t *m) + if (type&8) { + if (old<0 || a_cas(&m->_m_lock, old, new)!=old) { + if (new) a_store(&m->_m_waiters, -1); +- __syscall(SYS_futex, &m->_m_lock, FUTEX_UNLOCK_PI|priv); ++ __syscall(SYS_futex, &m->_m_lock, FUTEX_UNLOCK_PI|priv, 0, 0, 0, 0); + } + cont = 0; + waiters = 0; +diff --git a/src/thread/pthread_mutexattr_setprotocol.c b/src/thread/pthread_mutexattr_setprotocol.c +index 8b80c1c..b98cbc2 100644 +--- a/src/thread/pthread_mutexattr_setprotocol.c ++++ b/src/thread/pthread_mutexattr_setprotocol.c +@@ -14,7 +14,7 @@ int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int protocol) + r = check_pi_result; + if (r < 0) { + volatile int lk = 0; +- r = -__syscall(SYS_futex, &lk, FUTEX_LOCK_PI, 0, 0); ++ r = -__syscall(SYS_futex, &lk, FUTEX_LOCK_PI, 0, 0, 0, 0); + a_store(&check_pi_result, r); + } + if (r) return r; +diff --git a/src/thread/wasm/__set_thread_area.c b/src/thread/wasm/__set_thread_area.c +new file mode 100644 +index 0000000..f703c34 +--- /dev/null ++++ b/src/thread/wasm/__set_thread_area.c +@@ -0,0 +1,9 @@ ++#include "pthread_impl.h" ++ ++_Thread_local uintptr_t __musl_tp = 0U; ++ ++int __set_thread_area(void *p) ++{ ++ __musl_tp = (uintptr_t)p; ++ return 0; ++} +diff --git a/src/thread/wasm/clone.s b/src/thread/wasm/clone.s +new file mode 100644 +index 0000000..4aa3a7e +--- /dev/null ++++ b/src/thread/wasm/clone.s +@@ -0,0 +1,149 @@ ++.globaltype __stack_pointer, i32 ++.globaltype __tls_base, i32 ++.functype __wasm_syscall_1(i32, i32, i32, i32) -> (i32) ++.functype __wasm_syscall_5(i32, i32, i32, i32, i32, i32, i32, i32) -> (i32) ++ ++.globl __get_tls_base ++__get_tls_base: ++ .functype __get_tls_base() -> (i32) ++ global.get __tls_base ++ end_function ++ ++# The runtime expects to be able to set __tls_base as part of it's clone setup. ++.globl __set_tls_base ++__set_tls_base: ++ .functype __set_tls_base(i32) -> () ++ local.get 0 ++ global.set __tls_base ++ end_function ++ ++# This function gets called by the host once it detects a clone happening. ++.globl __libc_clone_callback ++__libc_clone_callback: ++ .functype __libc_clone_callback() -> (i32) ++ ++ i32.const 0 # sp (no care) ++ i32.const 0 # tp (no care) ++ i32.const 93 # SYS_exit (will be used at the end of this function) ++ ++ # Extract arg ++ global.get __stack_pointer ++ i32.const 4 ++ i32.add ++ i32.load 0 ++ ++ # Extract fn ++ global.get __stack_pointer ++ i32.load 0 ++ ++ # Deallocate fn+arg+alignemnt ++ global.get __stack_pointer ++ i32.const 16 ++ i32.add ++ global.set __stack_pointer ++ ++ # Call fn(arg) ++ call_indirect (i32) -> (i32) ++ ++ # syscall(SYS_exit, the return value from the callback) ++ call __wasm_syscall_1 ++ end_function ++ ++.globl _Clone ++_Clone: ++ # int _Clone(fn, stack, flags, arg, ptid, tls, ctid); ++ # 0 1 2 3 4 5 6 ++ .functype _Clone(i32, i32, i32, i32, i32, i32, i32) -> (i32) ++ .local i32 /* 7: child_stack */ ++ ++ # We save fn+arg on the stack. There are two distincs cases for stack: ++ # * stack is NULL: the kernel will in this special case copy the value ++ # of __stack_pointer to the new task. This means we should fill in fn ++ # and arg on our stack. This also implies that the caller uses ++ # CLONE_VFORK which pauses the calling task (or it does not care about ++ # the stack being corrupted in the caller, which is a very odd case). ++ # * stack is not NULL: the kernel will in this case set the stack ++ # pointer of the new task to the value of the stack argument. ++ # ++ # In either case we don't really need to "allocate" space on these ++ # stacks, but we do it anyway, should the syscall wrappers change in the ++ # future to call C code. In that case we also need to align the stack. ++ ++ # If stack was NULL, store into __stack_pointer instead. The kernel will ++ # in this special case copy the value of the stack pointer to the new ++ # task in the clone syscall. (Allocating space on these stacks is not ++ # strictly necessary but a nice thing if the syscall function changes ++ # to a C function in the future - in that case we should also align the ++ # stack to 16 bytes.) To be consistent, also allocate on non-NULL stack. ++ block ++ local.get 1 ++ br_if 0 # if (stack != NULL): break to block 0; ++ # else if (stack == NULL): ++ ++ global.get __stack_pointer ++ i32.const 16 ++ i32.sub ++ local.tee 7 ++ global.set __stack_pointer ++ end_block ++ ++ block ++ local.get 1 ++ i32.eqz ++ br_if 0 # if (stack == NULL): break to block 0; ++ # else if (stack != NULL): ++ ++ local.get 1 ++ i32.const 16 ++ i32.sub ++ local.tee 7 ++ local.set 1 ++ end_block ++ ++ /* ++ * At this point: ++ * local 1 contains the decremented stack address or NULL. ++ * local 7 contains the decremented stack address (never NULL). ++ * i.e. we don't pass the decremented __stack_pointer if stack == NULL. ++ */ ++ ++ # Store fn at the lowest address. ++ local.get 7 ++ local.get 0 ++ i32.store 0 ++ ++ # Store arg at the next-lowest address. ++ local.get 7 ++ i32.const 4 ++ i32.add ++ local.get 3 ++ i32.store 0 ++ ++ # syscall(SYS_clone, clone_flags, newsp, parent_tidptr, child_tidptr, tls) ++ global.get __stack_pointer # our sp (used when newsp is NULL) ++ global.get __tls_base # our tp (used when CLONE_SETTLS is not set) ++ i32.const 220 # SYS_clone ++ local.get 2 # clone_flags ++ local.get 1 # newsp (maybe NULL) ++ local.get 4 # ptid ++ local.get 6 # ctid ++ local.get 5 # tls ++ call __wasm_syscall_5 ++ ++ # On Wasm, we don't end up returning twice - instead wasm_clone_callback ++ # is called and we can extract fn and arg from __stack_pointer. This ++ # means that we for sure currently are in the parent when returning. ++ ++ block ++ local.get 1 ++ br_if 0 # if (stack != NULL): break to block 0; ++ # else if(stack == NULL): ++ ++ global.get __stack_pointer ++ i32.const 16 ++ i32.add ++ global.set __stack_pointer ++ end_block ++ ++ # (Pass on return value from the above syscall to the caller.) ++ end_function +diff --git a/src/unistd/access.c b/src/unistd/access.c +index d6eed68..449f9b4 100644 +--- a/src/unistd/access.c ++++ b/src/unistd/access.c +@@ -7,6 +7,6 @@ int access(const char *filename, int amode) + #ifdef SYS_access + return syscall(SYS_access, filename, amode); + #else +- return syscall(SYS_faccessat, AT_FDCWD, filename, amode, 0); ++ return syscall(SYS_faccessat2, AT_FDCWD, filename, amode, 0); + #endif + } +diff --git a/src/unistd/faccessat.c b/src/unistd/faccessat.c +index 43052dd..56d650c 100644 +--- a/src/unistd/faccessat.c ++++ b/src/unistd/faccessat.c +@@ -18,7 +18,7 @@ static int checker(void *p) + if (__syscall(SYS_setregid, __syscall(SYS_getegid), -1) + || __syscall(SYS_setreuid, __syscall(SYS_geteuid), -1)) + __syscall(SYS_exit, 1); +- ret = __syscall(SYS_faccessat, c->fd, c->filename, c->amode, 0); ++ ret = __syscall(SYS_faccessat2, c->fd, c->filename, c->amode, 0); + __syscall(SYS_write, c->p, &ret, sizeof ret); + return 0; + } +-- +2.25.1 +