Pull request for nolibc library for v5.19

This pull request adds a number of library functions and splits this
 library into multiple files.
 -----BEGIN PGP SIGNATURE-----
 
 iQJHBAABCgAxFiEEbK7UrM+RBIrCoViJnr8S83LZ+4wFAmKH2pQTHHBhdWxtY2tA
 a2VybmVsLm9yZwAKCRCevxLzctn7jEduEACep6ZSvKqdY2TQoepLyQRjJ9ASVxcB
 OU2xKQ4T5qa7235mFcoZo9oQY8wAVYx/iUjzUOTO27FBA7x+bqlg9xJT5ZvwANKC
 YPHqHHIEFLrDjSPCHnh3K9YAfy3szUeFm4sW/xba2U/LiosKhKDZjkPjPrzwBV7b
 1ioaMx/ATSwIo9zofcE3NgMQx/Y975QN2e9gako4JiHTN0IflhTUIzQ/Nmiw6GPW
 6LXB/nKD1zwaqRccg7qHhP9yzqy5dpQ4bPKEdeChQUKoFzDTfzz/14miYm3TCQm4
 uHCAELGGrzFjP0jZayNoLn6XJn21G3x2L0Z5FVt7DhOg+5LbTWlobi3q0O0WH220
 cf6GQ8irLnWVp/SVh/w3FJk73S39Max9kShPzQSLchrfdDBu1r3t09YU6nrqWSiK
 R2fJqF5ljgZcXY8THFtLtQwd7d7BBXKWeNdn0BLe4Zjeuc2XfaZtysAxz/+tWn9/
 ywuYJQAAk59dBmwWRdZ9Q425HSallGRKRw7Wrj5JUS464ys8/aFuRX3vY/hpCZ7R
 IQbmNpJnzGItxQGA0vBLepddm3lD3OTGFsG/S6q4kaOS247vwvKXl+ohICAAOSGo
 EfW0PYDfAiFykyRb4FOeXlEjfS72WddnjhsrUQXSBF8sIIqkm+ewbQo4RqtY5G1o
 N1YVfyLXQ+sgFQ==
 =1Nbj
 -----END PGP SIGNATURE-----

Merge tag 'nolibc.2022.05.20a' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu

Pull nolibc library updates from Paul McKenney:
 "This adds a number of library functions and splits this library into
  multiple files"

* tag 'nolibc.2022.05.20a' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu: (61 commits)
  tools/nolibc/string: Implement `strdup()` and `strndup()`
  tools/nolibc/string: Implement `strnlen()`
  tools/nolibc/stdlib: Implement `malloc()`, `calloc()`, `realloc()` and `free()`
  tools/nolibc/types: Implement `offsetof()` and `container_of()` macro
  tools/nolibc/sys: Implement `mmap()` and `munmap()`
  tools/nolibc: i386: Implement syscall with 6 arguments
  tools/nolibc: Remove .global _start from the entry point code
  tools/nolibc: Replace `asm` with `__asm__`
  tools/nolibc: x86-64: Update System V ABI document link
  tools/nolibc/stdlib: only reference the external environ when inlined
  tools/nolibc/string: do not use __builtin_strlen() at -O0
  tools/nolibc: add the nolibc subdir to the common Makefile
  tools/nolibc: add a makefile to install headers
  tools/nolibc/types: add poll() and waitpid() flag definitions
  tools/nolibc/sys: add syscall definition for getppid()
  tools/nolibc/string: add strcmp() and strncmp()
  tools/nolibc/stdio: add support for '%p' to vfprintf()
  tools/nolibc/stdlib: add a simple getenv() implementation
  tools/nolibc/stdio: make printf(%s) accept NULL
  tools/nolibc/stdlib: implement abort()
  ...
This commit is contained in:
Linus Torvalds 2022-05-23 11:33:25 -07:00
commit f814957b01
21 changed files with 4114 additions and 2505 deletions

View File

@ -24,6 +24,7 @@ help:
@echo ' intel-speed-select - Intel Speed Select tool'
@echo ' kvm_stat - top-like utility for displaying kvm statistics'
@echo ' leds - LEDs tools'
@echo ' nolibc - nolibc headers testing and installation'
@echo ' objtool - an ELF object analysis tool'
@echo ' pci - PCI tools'
@echo ' perf - Linux performance measurement and analysis tool'
@ -74,6 +75,9 @@ bpf/%: FORCE
libapi: FORCE
$(call descend,lib/api)
nolibc_%: FORCE
$(call descend,include/nolibc,$(patsubst nolibc_%,%,$@))
# The perf build does not follow the descend function setup,
# invoking it via it's own make rule.
PERF_O = $(if $(O),$(O)/tools/perf,)

View File

@ -0,0 +1,42 @@
# SPDX-License-Identifier: GPL-2.0
# Makefile for nolibc installation and tests
include ../../scripts/Makefile.include
# we're in ".../tools/include/nolibc"
ifeq ($(srctree),)
srctree := $(patsubst %/tools/include/,%,$(dir $(CURDIR)))
endif
nolibc_arch := $(patsubst arm64,aarch64,$(ARCH))
arch_file := arch-$(nolibc_arch).h
all_files := ctype.h errno.h nolibc.h signal.h std.h stdio.h stdlib.h string.h \
sys.h time.h types.h unistd.h
# install all headers needed to support a bare-metal compiler
all:
# Note: when ARCH is "x86" we concatenate both x86_64 and i386
headers:
$(Q)mkdir -p $(OUTPUT)sysroot
$(Q)mkdir -p $(OUTPUT)sysroot/include
$(Q)cp $(all_files) $(OUTPUT)sysroot/include/
$(Q)if [ "$(ARCH)" = "x86" ]; then \
sed -e \
's,^#ifndef _NOLIBC_ARCH_X86_64_H,#if !defined(_NOLIBC_ARCH_X86_64_H) \&\& defined(__x86_64__),' \
arch-x86_64.h; \
sed -e \
's,^#ifndef _NOLIBC_ARCH_I386_H,#if !defined(_NOLIBC_ARCH_I386_H) \&\& !defined(__x86_64__),' \
arch-i386.h; \
elif [ -e "$(arch_file)" ]; then \
cat $(arch_file); \
else \
echo "Fatal: architecture $(ARCH) not yet supported by nolibc." >&2; \
exit 1; \
fi > $(OUTPUT)sysroot/include/arch.h
headers_standalone: headers
$(Q)$(MAKE) -C $(srctree) headers
$(Q)$(MAKE) -C $(srctree) headers_install INSTALL_HDR_PATH=$(OUTPUT)/sysroot
clean:
$(call QUIET_CLEAN, nolibc) rm -rf "$(OUTPUT)sysroot"

View File

@ -0,0 +1,199 @@
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
* AARCH64 specific definitions for NOLIBC
* Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>
*/
#ifndef _NOLIBC_ARCH_AARCH64_H
#define _NOLIBC_ARCH_AARCH64_H
/* O_* macros for fcntl/open are architecture-specific */
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 0x40
#define O_EXCL 0x80
#define O_NOCTTY 0x100
#define O_TRUNC 0x200
#define O_APPEND 0x400
#define O_NONBLOCK 0x800
#define O_DIRECTORY 0x4000
/* The struct returned by the newfstatat() syscall. Differs slightly from the
* x86_64's stat one by field ordering, so be careful.
*/
struct sys_stat_struct {
unsigned long st_dev;
unsigned long st_ino;
unsigned int st_mode;
unsigned int st_nlink;
unsigned int st_uid;
unsigned int st_gid;
unsigned long st_rdev;
unsigned long __pad1;
long st_size;
int st_blksize;
int __pad2;
long st_blocks;
long st_atime;
unsigned long st_atime_nsec;
long st_mtime;
unsigned long st_mtime_nsec;
long st_ctime;
unsigned long st_ctime_nsec;
unsigned int __unused[2];
};
/* Syscalls for AARCH64 :
* - registers are 64-bit
* - stack is 16-byte aligned
* - syscall number is passed in x8
* - arguments are in x0, x1, x2, x3, x4, x5
* - the system call is performed by calling svc 0
* - syscall return comes in x0.
* - the arguments are cast to long and assigned into the target registers
* which are then simply passed as registers to the asm code, so that we
* don't have to experience issues with register constraints.
*
* On aarch64, select() is not implemented so we have to use pselect6().
*/
#define __ARCH_WANT_SYS_PSELECT6
#define my_syscall0(num) \
({ \
register long _num __asm__ ("x8") = (num); \
register long _arg1 __asm__ ("x0"); \
\
__asm__ volatile ( \
"svc #0\n" \
: "=r"(_arg1) \
: "r"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define my_syscall1(num, arg1) \
({ \
register long _num __asm__ ("x8") = (num); \
register long _arg1 __asm__ ("x0") = (long)(arg1); \
\
__asm__ volatile ( \
"svc #0\n" \
: "=r"(_arg1) \
: "r"(_arg1), \
"r"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define my_syscall2(num, arg1, arg2) \
({ \
register long _num __asm__ ("x8") = (num); \
register long _arg1 __asm__ ("x0") = (long)(arg1); \
register long _arg2 __asm__ ("x1") = (long)(arg2); \
\
__asm__ volatile ( \
"svc #0\n" \
: "=r"(_arg1) \
: "r"(_arg1), "r"(_arg2), \
"r"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define my_syscall3(num, arg1, arg2, arg3) \
({ \
register long _num __asm__ ("x8") = (num); \
register long _arg1 __asm__ ("x0") = (long)(arg1); \
register long _arg2 __asm__ ("x1") = (long)(arg2); \
register long _arg3 __asm__ ("x2") = (long)(arg3); \
\
__asm__ volatile ( \
"svc #0\n" \
: "=r"(_arg1) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), \
"r"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define my_syscall4(num, arg1, arg2, arg3, arg4) \
({ \
register long _num __asm__ ("x8") = (num); \
register long _arg1 __asm__ ("x0") = (long)(arg1); \
register long _arg2 __asm__ ("x1") = (long)(arg2); \
register long _arg3 __asm__ ("x2") = (long)(arg3); \
register long _arg4 __asm__ ("x3") = (long)(arg4); \
\
__asm__ volatile ( \
"svc #0\n" \
: "=r"(_arg1) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \
"r"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \
({ \
register long _num __asm__ ("x8") = (num); \
register long _arg1 __asm__ ("x0") = (long)(arg1); \
register long _arg2 __asm__ ("x1") = (long)(arg2); \
register long _arg3 __asm__ ("x2") = (long)(arg3); \
register long _arg4 __asm__ ("x3") = (long)(arg4); \
register long _arg5 __asm__ ("x4") = (long)(arg5); \
\
__asm__ volatile ( \
"svc #0\n" \
: "=r" (_arg1) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
"r"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \
({ \
register long _num __asm__ ("x8") = (num); \
register long _arg1 __asm__ ("x0") = (long)(arg1); \
register long _arg2 __asm__ ("x1") = (long)(arg2); \
register long _arg3 __asm__ ("x2") = (long)(arg3); \
register long _arg4 __asm__ ("x3") = (long)(arg4); \
register long _arg5 __asm__ ("x4") = (long)(arg5); \
register long _arg6 __asm__ ("x5") = (long)(arg6); \
\
__asm__ volatile ( \
"svc #0\n" \
: "=r" (_arg1) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
"r"(_arg6), "r"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
/* startup code */
__asm__ (".section .text\n"
".weak _start\n"
"_start:\n"
"ldr x0, [sp]\n" // argc (x0) was in the stack
"add x1, sp, 8\n" // argv (x1) = sp
"lsl x2, x0, 3\n" // envp (x2) = 8*argc ...
"add x2, x2, 8\n" // + 8 (skip null)
"add x2, x2, x1\n" // + argv
"and sp, x1, -16\n" // sp must be 16-byte aligned in the callee
"bl main\n" // main() returns the status code, we'll exit with it.
"mov x8, 93\n" // NR_exit == 93
"svc #0\n"
"");
#endif // _NOLIBC_ARCH_AARCH64_H

View File

@ -0,0 +1,204 @@
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
* ARM specific definitions for NOLIBC
* Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>
*/
#ifndef _NOLIBC_ARCH_ARM_H
#define _NOLIBC_ARCH_ARM_H
/* O_* macros for fcntl/open are architecture-specific */
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 0x40
#define O_EXCL 0x80
#define O_NOCTTY 0x100
#define O_TRUNC 0x200
#define O_APPEND 0x400
#define O_NONBLOCK 0x800
#define O_DIRECTORY 0x4000
/* The struct returned by the stat() syscall, 32-bit only, the syscall returns
* exactly 56 bytes (stops before the unused array). In big endian, the format
* differs as devices are returned as short only.
*/
struct sys_stat_struct {
#if defined(__ARMEB__)
unsigned short st_dev;
unsigned short __pad1;
#else
unsigned long st_dev;
#endif
unsigned long st_ino;
unsigned short st_mode;
unsigned short st_nlink;
unsigned short st_uid;
unsigned short st_gid;
#if defined(__ARMEB__)
unsigned short st_rdev;
unsigned short __pad2;
#else
unsigned long st_rdev;
#endif
unsigned long st_size;
unsigned long st_blksize;
unsigned long st_blocks;
unsigned long st_atime;
unsigned long st_atime_nsec;
unsigned long st_mtime;
unsigned long st_mtime_nsec;
unsigned long st_ctime;
unsigned long st_ctime_nsec;
unsigned long __unused[2];
};
/* Syscalls for ARM in ARM or Thumb modes :
* - registers are 32-bit
* - stack is 8-byte aligned
* ( http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka4127.html)
* - syscall number is passed in r7
* - arguments are in r0, r1, r2, r3, r4, r5
* - the system call is performed by calling svc #0
* - syscall return comes in r0.
* - only lr is clobbered.
* - the arguments are cast to long and assigned into the target registers
* which are then simply passed as registers to the asm code, so that we
* don't have to experience issues with register constraints.
* - the syscall number is always specified last in order to allow to force
* some registers before (gcc refuses a %-register at the last position).
*
* Also, ARM supports the old_select syscall if newselect is not available
*/
#define __ARCH_WANT_SYS_OLD_SELECT
#define my_syscall0(num) \
({ \
register long _num __asm__ ("r7") = (num); \
register long _arg1 __asm__ ("r0"); \
\
__asm__ volatile ( \
"svc #0\n" \
: "=r"(_arg1) \
: "r"(_num) \
: "memory", "cc", "lr" \
); \
_arg1; \
})
#define my_syscall1(num, arg1) \
({ \
register long _num __asm__ ("r7") = (num); \
register long _arg1 __asm__ ("r0") = (long)(arg1); \
\
__asm__ volatile ( \
"svc #0\n" \
: "=r"(_arg1) \
: "r"(_arg1), \
"r"(_num) \
: "memory", "cc", "lr" \
); \
_arg1; \
})
#define my_syscall2(num, arg1, arg2) \
({ \
register long _num __asm__ ("r7") = (num); \
register long _arg1 __asm__ ("r0") = (long)(arg1); \
register long _arg2 __asm__ ("r1") = (long)(arg2); \
\
__asm__ volatile ( \
"svc #0\n" \
: "=r"(_arg1) \
: "r"(_arg1), "r"(_arg2), \
"r"(_num) \
: "memory", "cc", "lr" \
); \
_arg1; \
})
#define my_syscall3(num, arg1, arg2, arg3) \
({ \
register long _num __asm__ ("r7") = (num); \
register long _arg1 __asm__ ("r0") = (long)(arg1); \
register long _arg2 __asm__ ("r1") = (long)(arg2); \
register long _arg3 __asm__ ("r2") = (long)(arg3); \
\
__asm__ volatile ( \
"svc #0\n" \
: "=r"(_arg1) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), \
"r"(_num) \
: "memory", "cc", "lr" \
); \
_arg1; \
})
#define my_syscall4(num, arg1, arg2, arg3, arg4) \
({ \
register long _num __asm__ ("r7") = (num); \
register long _arg1 __asm__ ("r0") = (long)(arg1); \
register long _arg2 __asm__ ("r1") = (long)(arg2); \
register long _arg3 __asm__ ("r2") = (long)(arg3); \
register long _arg4 __asm__ ("r3") = (long)(arg4); \
\
__asm__ volatile ( \
"svc #0\n" \
: "=r"(_arg1) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \
"r"(_num) \
: "memory", "cc", "lr" \
); \
_arg1; \
})
#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \
({ \
register long _num __asm__ ("r7") = (num); \
register long _arg1 __asm__ ("r0") = (long)(arg1); \
register long _arg2 __asm__ ("r1") = (long)(arg2); \
register long _arg3 __asm__ ("r2") = (long)(arg3); \
register long _arg4 __asm__ ("r3") = (long)(arg4); \
register long _arg5 __asm__ ("r4") = (long)(arg5); \
\
__asm__ volatile ( \
"svc #0\n" \
: "=r" (_arg1) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
"r"(_num) \
: "memory", "cc", "lr" \
); \
_arg1; \
})
/* startup code */
__asm__ (".section .text\n"
".weak _start\n"
"_start:\n"
#if defined(__THUMBEB__) || defined(__THUMBEL__)
/* We enter here in 32-bit mode but if some previous functions were in
* 16-bit mode, the assembler cannot know, so we need to tell it we're in
* 32-bit now, then switch to 16-bit (is there a better way to do it than
* adding 1 by hand ?) and tell the asm we're now in 16-bit mode so that
* it generates correct instructions. Note that we do not support thumb1.
*/
".code 32\n"
"add r0, pc, #1\n"
"bx r0\n"
".code 16\n"
#endif
"pop {%r0}\n" // argc was in the stack
"mov %r1, %sp\n" // argv = sp
"add %r2, %r1, %r0, lsl #2\n" // envp = argv + 4*argc ...
"add %r2, %r2, $4\n" // ... + 4
"and %r3, %r1, $-8\n" // AAPCS : sp must be 8-byte aligned in the
"mov %sp, %r3\n" // callee, an bl doesn't push (lr=pc)
"bl main\n" // main() returns the status code, we'll exit with it.
"movs r7, $1\n" // NR_exit == 1
"svc $0x00\n"
"");
#endif // _NOLIBC_ARCH_ARM_H

View File

@ -0,0 +1,219 @@
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
* i386 specific definitions for NOLIBC
* Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>
*/
#ifndef _NOLIBC_ARCH_I386_H
#define _NOLIBC_ARCH_I386_H
/* O_* macros for fcntl/open are architecture-specific */
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 0x40
#define O_EXCL 0x80
#define O_NOCTTY 0x100
#define O_TRUNC 0x200
#define O_APPEND 0x400
#define O_NONBLOCK 0x800
#define O_DIRECTORY 0x10000
/* The struct returned by the stat() syscall, 32-bit only, the syscall returns
* exactly 56 bytes (stops before the unused array).
*/
struct sys_stat_struct {
unsigned long st_dev;
unsigned long st_ino;
unsigned short st_mode;
unsigned short st_nlink;
unsigned short st_uid;
unsigned short st_gid;
unsigned long st_rdev;
unsigned long st_size;
unsigned long st_blksize;
unsigned long st_blocks;
unsigned long st_atime;
unsigned long st_atime_nsec;
unsigned long st_mtime;
unsigned long st_mtime_nsec;
unsigned long st_ctime;
unsigned long st_ctime_nsec;
unsigned long __unused[2];
};
/* Syscalls for i386 :
* - mostly similar to x86_64
* - registers are 32-bit
* - syscall number is passed in eax
* - arguments are in ebx, ecx, edx, esi, edi, ebp respectively
* - all registers are preserved (except eax of course)
* - the system call is performed by calling int $0x80
* - syscall return comes in eax
* - the arguments are cast to long and assigned into the target registers
* which are then simply passed as registers to the asm code, so that we
* don't have to experience issues with register constraints.
* - the syscall number is always specified last in order to allow to force
* some registers before (gcc refuses a %-register at the last position).
*
* Also, i386 supports the old_select syscall if newselect is not available
*/
#define __ARCH_WANT_SYS_OLD_SELECT
#define my_syscall0(num) \
({ \
long _ret; \
register long _num __asm__ ("eax") = (num); \
\
__asm__ volatile ( \
"int $0x80\n" \
: "=a" (_ret) \
: "0"(_num) \
: "memory", "cc" \
); \
_ret; \
})
#define my_syscall1(num, arg1) \
({ \
long _ret; \
register long _num __asm__ ("eax") = (num); \
register long _arg1 __asm__ ("ebx") = (long)(arg1); \
\
__asm__ volatile ( \
"int $0x80\n" \
: "=a" (_ret) \
: "r"(_arg1), \
"0"(_num) \
: "memory", "cc" \
); \
_ret; \
})
#define my_syscall2(num, arg1, arg2) \
({ \
long _ret; \
register long _num __asm__ ("eax") = (num); \
register long _arg1 __asm__ ("ebx") = (long)(arg1); \
register long _arg2 __asm__ ("ecx") = (long)(arg2); \
\
__asm__ volatile ( \
"int $0x80\n" \
: "=a" (_ret) \
: "r"(_arg1), "r"(_arg2), \
"0"(_num) \
: "memory", "cc" \
); \
_ret; \
})
#define my_syscall3(num, arg1, arg2, arg3) \
({ \
long _ret; \
register long _num __asm__ ("eax") = (num); \
register long _arg1 __asm__ ("ebx") = (long)(arg1); \
register long _arg2 __asm__ ("ecx") = (long)(arg2); \
register long _arg3 __asm__ ("edx") = (long)(arg3); \
\
__asm__ volatile ( \
"int $0x80\n" \
: "=a" (_ret) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), \
"0"(_num) \
: "memory", "cc" \
); \
_ret; \
})
#define my_syscall4(num, arg1, arg2, arg3, arg4) \
({ \
long _ret; \
register long _num __asm__ ("eax") = (num); \
register long _arg1 __asm__ ("ebx") = (long)(arg1); \
register long _arg2 __asm__ ("ecx") = (long)(arg2); \
register long _arg3 __asm__ ("edx") = (long)(arg3); \
register long _arg4 __asm__ ("esi") = (long)(arg4); \
\
__asm__ volatile ( \
"int $0x80\n" \
: "=a" (_ret) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \
"0"(_num) \
: "memory", "cc" \
); \
_ret; \
})
#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \
({ \
long _ret; \
register long _num __asm__ ("eax") = (num); \
register long _arg1 __asm__ ("ebx") = (long)(arg1); \
register long _arg2 __asm__ ("ecx") = (long)(arg2); \
register long _arg3 __asm__ ("edx") = (long)(arg3); \
register long _arg4 __asm__ ("esi") = (long)(arg4); \
register long _arg5 __asm__ ("edi") = (long)(arg5); \
\
__asm__ volatile ( \
"int $0x80\n" \
: "=a" (_ret) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
"0"(_num) \
: "memory", "cc" \
); \
_ret; \
})
#define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \
({ \
long _eax = (long)(num); \
long _arg6 = (long)(arg6); /* Always in memory */ \
__asm__ volatile ( \
"pushl %[_arg6]\n\t" \
"pushl %%ebp\n\t" \
"movl 4(%%esp),%%ebp\n\t" \
"int $0x80\n\t" \
"popl %%ebp\n\t" \
"addl $4,%%esp\n\t" \
: "+a"(_eax) /* %eax */ \
: "b"(arg1), /* %ebx */ \
"c"(arg2), /* %ecx */ \
"d"(arg3), /* %edx */ \
"S"(arg4), /* %esi */ \
"D"(arg5), /* %edi */ \
[_arg6]"m"(_arg6) /* memory */ \
: "memory", "cc" \
); \
_eax; \
})
/* startup code */
/*
* i386 System V ABI mandates:
* 1) last pushed argument must be 16-byte aligned.
* 2) The deepest stack frame should be set to zero
*
*/
__asm__ (".section .text\n"
".weak _start\n"
"_start:\n"
"pop %eax\n" // argc (first arg, %eax)
"mov %esp, %ebx\n" // argv[] (second arg, %ebx)
"lea 4(%ebx,%eax,4),%ecx\n" // then a NULL then envp (third arg, %ecx)
"xor %ebp, %ebp\n" // zero the stack frame
"and $-16, %esp\n" // x86 ABI : esp must be 16-byte aligned before
"sub $4, %esp\n" // the call instruction (args are aligned)
"push %ecx\n" // push all registers on the stack so that we
"push %ebx\n" // support both regparm and plain stack modes
"push %eax\n"
"call main\n" // main() returns the status code in %eax
"mov %eax, %ebx\n" // retrieve exit code (32-bit int)
"movl $1, %eax\n" // NR_exit == 1
"int $0x80\n" // exit now
"hlt\n" // ensure it does not
"");
#endif // _NOLIBC_ARCH_I386_H

View File

@ -0,0 +1,215 @@
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
* MIPS specific definitions for NOLIBC
* Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>
*/
#ifndef _NOLIBC_ARCH_MIPS_H
#define _NOLIBC_ARCH_MIPS_H
/* O_* macros for fcntl/open are architecture-specific */
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_APPEND 0x0008
#define O_NONBLOCK 0x0080
#define O_CREAT 0x0100
#define O_TRUNC 0x0200
#define O_EXCL 0x0400
#define O_NOCTTY 0x0800
#define O_DIRECTORY 0x10000
/* The struct returned by the stat() syscall. 88 bytes are returned by the
* syscall.
*/
struct sys_stat_struct {
unsigned int st_dev;
long st_pad1[3];
unsigned long st_ino;
unsigned int st_mode;
unsigned int st_nlink;
unsigned int st_uid;
unsigned int st_gid;
unsigned int st_rdev;
long st_pad2[2];
long st_size;
long st_pad3;
long st_atime;
long st_atime_nsec;
long st_mtime;
long st_mtime_nsec;
long st_ctime;
long st_ctime_nsec;
long st_blksize;
long st_blocks;
long st_pad4[14];
};
/* Syscalls for MIPS ABI O32 :
* - WARNING! there's always a delayed slot!
* - WARNING again, the syntax is different, registers take a '$' and numbers
* do not.
* - registers are 32-bit
* - stack is 8-byte aligned
* - syscall number is passed in v0 (starts at 0xfa0).
* - arguments are in a0, a1, a2, a3, then the stack. The caller needs to
* leave some room in the stack for the callee to save a0..a3 if needed.
* - Many registers are clobbered, in fact only a0..a2 and s0..s8 are
* preserved. See: https://www.linux-mips.org/wiki/Syscall as well as
* scall32-o32.S in the kernel sources.
* - the system call is performed by calling "syscall"
* - syscall return comes in v0, and register a3 needs to be checked to know
* if an error occurred, in which case errno is in v0.
* - the arguments are cast to long and assigned into the target registers
* which are then simply passed as registers to the asm code, so that we
* don't have to experience issues with register constraints.
*/
#define my_syscall0(num) \
({ \
register long _num __asm__ ("v0") = (num); \
register long _arg4 __asm__ ("a3"); \
\
__asm__ volatile ( \
"addiu $sp, $sp, -32\n" \
"syscall\n" \
"addiu $sp, $sp, 32\n" \
: "=r"(_num), "=r"(_arg4) \
: "r"(_num) \
: "memory", "cc", "at", "v1", "hi", "lo", \
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" \
); \
_arg4 ? -_num : _num; \
})
#define my_syscall1(num, arg1) \
({ \
register long _num __asm__ ("v0") = (num); \
register long _arg1 __asm__ ("a0") = (long)(arg1); \
register long _arg4 __asm__ ("a3"); \
\
__asm__ volatile ( \
"addiu $sp, $sp, -32\n" \
"syscall\n" \
"addiu $sp, $sp, 32\n" \
: "=r"(_num), "=r"(_arg4) \
: "0"(_num), \
"r"(_arg1) \
: "memory", "cc", "at", "v1", "hi", "lo", \
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" \
); \
_arg4 ? -_num : _num; \
})
#define my_syscall2(num, arg1, arg2) \
({ \
register long _num __asm__ ("v0") = (num); \
register long _arg1 __asm__ ("a0") = (long)(arg1); \
register long _arg2 __asm__ ("a1") = (long)(arg2); \
register long _arg4 __asm__ ("a3"); \
\
__asm__ volatile ( \
"addiu $sp, $sp, -32\n" \
"syscall\n" \
"addiu $sp, $sp, 32\n" \
: "=r"(_num), "=r"(_arg4) \
: "0"(_num), \
"r"(_arg1), "r"(_arg2) \
: "memory", "cc", "at", "v1", "hi", "lo", \
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" \
); \
_arg4 ? -_num : _num; \
})
#define my_syscall3(num, arg1, arg2, arg3) \
({ \
register long _num __asm__ ("v0") = (num); \
register long _arg1 __asm__ ("a0") = (long)(arg1); \
register long _arg2 __asm__ ("a1") = (long)(arg2); \
register long _arg3 __asm__ ("a2") = (long)(arg3); \
register long _arg4 __asm__ ("a3"); \
\
__asm__ volatile ( \
"addiu $sp, $sp, -32\n" \
"syscall\n" \
"addiu $sp, $sp, 32\n" \
: "=r"(_num), "=r"(_arg4) \
: "0"(_num), \
"r"(_arg1), "r"(_arg2), "r"(_arg3) \
: "memory", "cc", "at", "v1", "hi", "lo", \
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" \
); \
_arg4 ? -_num : _num; \
})
#define my_syscall4(num, arg1, arg2, arg3, arg4) \
({ \
register long _num __asm__ ("v0") = (num); \
register long _arg1 __asm__ ("a0") = (long)(arg1); \
register long _arg2 __asm__ ("a1") = (long)(arg2); \
register long _arg3 __asm__ ("a2") = (long)(arg3); \
register long _arg4 __asm__ ("a3") = (long)(arg4); \
\
__asm__ volatile ( \
"addiu $sp, $sp, -32\n" \
"syscall\n" \
"addiu $sp, $sp, 32\n" \
: "=r" (_num), "=r"(_arg4) \
: "0"(_num), \
"r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4) \
: "memory", "cc", "at", "v1", "hi", "lo", \
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" \
); \
_arg4 ? -_num : _num; \
})
#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \
({ \
register long _num __asm__ ("v0") = (num); \
register long _arg1 __asm__ ("a0") = (long)(arg1); \
register long _arg2 __asm__ ("a1") = (long)(arg2); \
register long _arg3 __asm__ ("a2") = (long)(arg3); \
register long _arg4 __asm__ ("a3") = (long)(arg4); \
register long _arg5 = (long)(arg5); \
\
__asm__ volatile ( \
"addiu $sp, $sp, -32\n" \
"sw %7, 16($sp)\n" \
"syscall\n " \
"addiu $sp, $sp, 32\n" \
: "=r" (_num), "=r"(_arg4) \
: "0"(_num), \
"r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5) \
: "memory", "cc", "at", "v1", "hi", "lo", \
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" \
); \
_arg4 ? -_num : _num; \
})
/* startup code, note that it's called __start on MIPS */
__asm__ (".section .text\n"
".weak __start\n"
".set nomips16\n"
".set noreorder\n"
".option pic0\n"
".ent __start\n"
"__start:\n"
"lw $a0,($sp)\n" // argc was in the stack
"addiu $a1, $sp, 4\n" // argv = sp + 4
"sll $a2, $a0, 2\n" // a2 = argc * 4
"add $a2, $a2, $a1\n" // envp = argv + 4*argc ...
"addiu $a2, $a2, 4\n" // ... + 4
"li $t0, -8\n"
"and $sp, $sp, $t0\n" // sp must be 8-byte aligned
"addiu $sp,$sp,-16\n" // the callee expects to save a0..a3 there!
"jal main\n" // main() returns the status code, we'll exit with it.
"nop\n" // delayed slot
"move $a0, $v0\n" // retrieve 32-bit exit code from v0
"li $v0, 4001\n" // NR_exit == 4001
"syscall\n"
".end __start\n"
"");
#endif // _NOLIBC_ARCH_MIPS_H

View File

@ -0,0 +1,204 @@
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
* RISCV (32 and 64) specific definitions for NOLIBC
* Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>
*/
#ifndef _NOLIBC_ARCH_RISCV_H
#define _NOLIBC_ARCH_RISCV_H
/* O_* macros for fcntl/open are architecture-specific */
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 0x100
#define O_EXCL 0x200
#define O_NOCTTY 0x400
#define O_TRUNC 0x1000
#define O_APPEND 0x2000
#define O_NONBLOCK 0x4000
#define O_DIRECTORY 0x200000
struct sys_stat_struct {
unsigned long st_dev; /* Device. */
unsigned long st_ino; /* File serial number. */
unsigned int st_mode; /* File mode. */
unsigned int st_nlink; /* Link count. */
unsigned int st_uid; /* User ID of the file's owner. */
unsigned int st_gid; /* Group ID of the file's group. */
unsigned long st_rdev; /* Device number, if device. */
unsigned long __pad1;
long st_size; /* Size of file, in bytes. */
int st_blksize; /* Optimal block size for I/O. */
int __pad2;
long st_blocks; /* Number 512-byte blocks allocated. */
long st_atime; /* Time of last access. */
unsigned long st_atime_nsec;
long st_mtime; /* Time of last modification. */
unsigned long st_mtime_nsec;
long st_ctime; /* Time of last status change. */
unsigned long st_ctime_nsec;
unsigned int __unused4;
unsigned int __unused5;
};
#if __riscv_xlen == 64
#define PTRLOG "3"
#define SZREG "8"
#elif __riscv_xlen == 32
#define PTRLOG "2"
#define SZREG "4"
#endif
/* Syscalls for RISCV :
* - stack is 16-byte aligned
* - syscall number is passed in a7
* - arguments are in a0, a1, a2, a3, a4, a5
* - the system call is performed by calling ecall
* - syscall return comes in a0
* - the arguments are cast to long and assigned into the target
* registers which are then simply passed as registers to the asm code,
* so that we don't have to experience issues with register constraints.
*
* On riscv, select() is not implemented so we have to use pselect6().
*/
#define __ARCH_WANT_SYS_PSELECT6
#define my_syscall0(num) \
({ \
register long _num __asm__ ("a7") = (num); \
register long _arg1 __asm__ ("a0"); \
\
__asm__ volatile ( \
"ecall\n\t" \
: "=r"(_arg1) \
: "r"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define my_syscall1(num, arg1) \
({ \
register long _num __asm__ ("a7") = (num); \
register long _arg1 __asm__ ("a0") = (long)(arg1); \
\
__asm__ volatile ( \
"ecall\n" \
: "+r"(_arg1) \
: "r"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define my_syscall2(num, arg1, arg2) \
({ \
register long _num __asm__ ("a7") = (num); \
register long _arg1 __asm__ ("a0") = (long)(arg1); \
register long _arg2 __asm__ ("a1") = (long)(arg2); \
\
__asm__ volatile ( \
"ecall\n" \
: "+r"(_arg1) \
: "r"(_arg2), \
"r"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define my_syscall3(num, arg1, arg2, arg3) \
({ \
register long _num __asm__ ("a7") = (num); \
register long _arg1 __asm__ ("a0") = (long)(arg1); \
register long _arg2 __asm__ ("a1") = (long)(arg2); \
register long _arg3 __asm__ ("a2") = (long)(arg3); \
\
__asm__ volatile ( \
"ecall\n\t" \
: "+r"(_arg1) \
: "r"(_arg2), "r"(_arg3), \
"r"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define my_syscall4(num, arg1, arg2, arg3, arg4) \
({ \
register long _num __asm__ ("a7") = (num); \
register long _arg1 __asm__ ("a0") = (long)(arg1); \
register long _arg2 __asm__ ("a1") = (long)(arg2); \
register long _arg3 __asm__ ("a2") = (long)(arg3); \
register long _arg4 __asm__ ("a3") = (long)(arg4); \
\
__asm__ volatile ( \
"ecall\n" \
: "+r"(_arg1) \
: "r"(_arg2), "r"(_arg3), "r"(_arg4), \
"r"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \
({ \
register long _num __asm__ ("a7") = (num); \
register long _arg1 __asm__ ("a0") = (long)(arg1); \
register long _arg2 __asm__ ("a1") = (long)(arg2); \
register long _arg3 __asm__ ("a2") = (long)(arg3); \
register long _arg4 __asm__ ("a3") = (long)(arg4); \
register long _arg5 __asm__ ("a4") = (long)(arg5); \
\
__asm__ volatile ( \
"ecall\n" \
: "+r"(_arg1) \
: "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
"r"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \
({ \
register long _num __asm__ ("a7") = (num); \
register long _arg1 __asm__ ("a0") = (long)(arg1); \
register long _arg2 __asm__ ("a1") = (long)(arg2); \
register long _arg3 __asm__ ("a2") = (long)(arg3); \
register long _arg4 __asm__ ("a3") = (long)(arg4); \
register long _arg5 __asm__ ("a4") = (long)(arg5); \
register long _arg6 __asm__ ("a5") = (long)(arg6); \
\
__asm__ volatile ( \
"ecall\n" \
: "+r"(_arg1) \
: "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), "r"(_arg6), \
"r"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
/* startup code */
__asm__ (".section .text\n"
".weak _start\n"
"_start:\n"
".option push\n"
".option norelax\n"
"lla gp, __global_pointer$\n"
".option pop\n"
"ld a0, 0(sp)\n" // argc (a0) was in the stack
"add a1, sp, "SZREG"\n" // argv (a1) = sp
"slli a2, a0, "PTRLOG"\n" // envp (a2) = SZREG*argc ...
"add a2, a2, "SZREG"\n" // + SZREG (skip null)
"add a2,a2,a1\n" // + argv
"andi sp,a1,-16\n" // sp must be 16-byte aligned
"call main\n" // main() returns the status code, we'll exit with it.
"li a7, 93\n" // NR_exit == 93
"ecall\n"
"");
#endif // _NOLIBC_ARCH_RISCV_H

View File

@ -0,0 +1,215 @@
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
* x86_64 specific definitions for NOLIBC
* Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>
*/
#ifndef _NOLIBC_ARCH_X86_64_H
#define _NOLIBC_ARCH_X86_64_H
/* O_* macros for fcntl/open are architecture-specific */
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 0x40
#define O_EXCL 0x80
#define O_NOCTTY 0x100
#define O_TRUNC 0x200
#define O_APPEND 0x400
#define O_NONBLOCK 0x800
#define O_DIRECTORY 0x10000
/* The struct returned by the stat() syscall, equivalent to stat64(). The
* syscall returns 116 bytes and stops in the middle of __unused.
*/
struct sys_stat_struct {
unsigned long st_dev;
unsigned long st_ino;
unsigned long st_nlink;
unsigned int st_mode;
unsigned int st_uid;
unsigned int st_gid;
unsigned int __pad0;
unsigned long st_rdev;
long st_size;
long st_blksize;
long st_blocks;
unsigned long st_atime;
unsigned long st_atime_nsec;
unsigned long st_mtime;
unsigned long st_mtime_nsec;
unsigned long st_ctime;
unsigned long st_ctime_nsec;
long __unused[3];
};
/* Syscalls for x86_64 :
* - registers are 64-bit
* - syscall number is passed in rax
* - arguments are in rdi, rsi, rdx, r10, r8, r9 respectively
* - the system call is performed by calling the syscall instruction
* - syscall return comes in rax
* - rcx and r11 are clobbered, others are preserved.
* - the arguments are cast to long and assigned into the target registers
* which are then simply passed as registers to the asm code, so that we
* don't have to experience issues with register constraints.
* - the syscall number is always specified last in order to allow to force
* some registers before (gcc refuses a %-register at the last position).
* - see also x86-64 ABI section A.2 AMD64 Linux Kernel Conventions, A.2.1
* Calling Conventions.
*
* Link x86-64 ABI: https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/home
*
*/
#define my_syscall0(num) \
({ \
long _ret; \
register long _num __asm__ ("rax") = (num); \
\
__asm__ volatile ( \
"syscall\n" \
: "=a"(_ret) \
: "0"(_num) \
: "rcx", "r11", "memory", "cc" \
); \
_ret; \
})
#define my_syscall1(num, arg1) \
({ \
long _ret; \
register long _num __asm__ ("rax") = (num); \
register long _arg1 __asm__ ("rdi") = (long)(arg1); \
\
__asm__ volatile ( \
"syscall\n" \
: "=a"(_ret) \
: "r"(_arg1), \
"0"(_num) \
: "rcx", "r11", "memory", "cc" \
); \
_ret; \
})
#define my_syscall2(num, arg1, arg2) \
({ \
long _ret; \
register long _num __asm__ ("rax") = (num); \
register long _arg1 __asm__ ("rdi") = (long)(arg1); \
register long _arg2 __asm__ ("rsi") = (long)(arg2); \
\
__asm__ volatile ( \
"syscall\n" \
: "=a"(_ret) \
: "r"(_arg1), "r"(_arg2), \
"0"(_num) \
: "rcx", "r11", "memory", "cc" \
); \
_ret; \
})
#define my_syscall3(num, arg1, arg2, arg3) \
({ \
long _ret; \
register long _num __asm__ ("rax") = (num); \
register long _arg1 __asm__ ("rdi") = (long)(arg1); \
register long _arg2 __asm__ ("rsi") = (long)(arg2); \
register long _arg3 __asm__ ("rdx") = (long)(arg3); \
\
__asm__ volatile ( \
"syscall\n" \
: "=a"(_ret) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), \
"0"(_num) \
: "rcx", "r11", "memory", "cc" \
); \
_ret; \
})
#define my_syscall4(num, arg1, arg2, arg3, arg4) \
({ \
long _ret; \
register long _num __asm__ ("rax") = (num); \
register long _arg1 __asm__ ("rdi") = (long)(arg1); \
register long _arg2 __asm__ ("rsi") = (long)(arg2); \
register long _arg3 __asm__ ("rdx") = (long)(arg3); \
register long _arg4 __asm__ ("r10") = (long)(arg4); \
\
__asm__ volatile ( \
"syscall\n" \
: "=a"(_ret) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \
"0"(_num) \
: "rcx", "r11", "memory", "cc" \
); \
_ret; \
})
#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \
({ \
long _ret; \
register long _num __asm__ ("rax") = (num); \
register long _arg1 __asm__ ("rdi") = (long)(arg1); \
register long _arg2 __asm__ ("rsi") = (long)(arg2); \
register long _arg3 __asm__ ("rdx") = (long)(arg3); \
register long _arg4 __asm__ ("r10") = (long)(arg4); \
register long _arg5 __asm__ ("r8") = (long)(arg5); \
\
__asm__ volatile ( \
"syscall\n" \
: "=a"(_ret) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
"0"(_num) \
: "rcx", "r11", "memory", "cc" \
); \
_ret; \
})
#define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \
({ \
long _ret; \
register long _num __asm__ ("rax") = (num); \
register long _arg1 __asm__ ("rdi") = (long)(arg1); \
register long _arg2 __asm__ ("rsi") = (long)(arg2); \
register long _arg3 __asm__ ("rdx") = (long)(arg3); \
register long _arg4 __asm__ ("r10") = (long)(arg4); \
register long _arg5 __asm__ ("r8") = (long)(arg5); \
register long _arg6 __asm__ ("r9") = (long)(arg6); \
\
__asm__ volatile ( \
"syscall\n" \
: "=a"(_ret) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
"r"(_arg6), "0"(_num) \
: "rcx", "r11", "memory", "cc" \
); \
_ret; \
})
/* startup code */
/*
* x86-64 System V ABI mandates:
* 1) %rsp must be 16-byte aligned right before the function call.
* 2) The deepest stack frame should be zero (the %rbp).
*
*/
__asm__ (".section .text\n"
".weak _start\n"
"_start:\n"
"pop %rdi\n" // argc (first arg, %rdi)
"mov %rsp, %rsi\n" // argv[] (second arg, %rsi)
"lea 8(%rsi,%rdi,8),%rdx\n" // then a NULL then envp (third arg, %rdx)
"xor %ebp, %ebp\n" // zero the stack frame
"and $-16, %rsp\n" // x86 ABI : esp must be 16-byte aligned before call
"call main\n" // main() returns the status code, we'll exit with it.
"mov %eax, %edi\n" // retrieve exit code (32 bit)
"mov $60, %eax\n" // NR_exit == 60
"syscall\n" // really exit
"hlt\n" // ensure it does not return
"");
#endif // _NOLIBC_ARCH_X86_64_H

View File

@ -0,0 +1,32 @@
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
* Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>
*/
/* Below comes the architecture-specific code. For each architecture, we have
* the syscall declarations and the _start code definition. This is the only
* global part. On all architectures the kernel puts everything in the stack
* before jumping to _start just above us, without any return address (_start
* is not a function but an entry pint). So at the stack pointer we find argc.
* Then argv[] begins, and ends at the first NULL. Then we have envp which
* starts and ends with a NULL as well. So envp=argv+argc+1.
*/
#ifndef _NOLIBC_ARCH_H
#define _NOLIBC_ARCH_H
#if defined(__x86_64__)
#include "arch-x86_64.h"
#elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__)
#include "arch-i386.h"
#elif defined(__ARM_EABI__)
#include "arch-arm.h"
#elif defined(__aarch64__)
#include "arch-aarch64.h"
#elif defined(__mips__) && defined(_ABIO32)
#include "arch-mips.h"
#elif defined(__riscv)
#include "arch-riscv.h"
#endif
#endif /* _NOLIBC_ARCH_H */

View File

@ -0,0 +1,99 @@
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
* ctype function definitions for NOLIBC
* Copyright (C) 2017-2021 Willy Tarreau <w@1wt.eu>
*/
#ifndef _NOLIBC_CTYPE_H
#define _NOLIBC_CTYPE_H
#include "std.h"
/*
* As much as possible, please keep functions alphabetically sorted.
*/
static __attribute__((unused))
int isascii(int c)
{
/* 0x00..0x7f */
return (unsigned int)c <= 0x7f;
}
static __attribute__((unused))
int isblank(int c)
{
return c == '\t' || c == ' ';
}
static __attribute__((unused))
int iscntrl(int c)
{
/* 0x00..0x1f, 0x7f */
return (unsigned int)c < 0x20 || c == 0x7f;
}
static __attribute__((unused))
int isdigit(int c)
{
return (unsigned int)(c - '0') < 10;
}
static __attribute__((unused))
int isgraph(int c)
{
/* 0x21..0x7e */
return (unsigned int)(c - 0x21) < 0x5e;
}
static __attribute__((unused))
int islower(int c)
{
return (unsigned int)(c - 'a') < 26;
}
static __attribute__((unused))
int isprint(int c)
{
/* 0x20..0x7e */
return (unsigned int)(c - 0x20) < 0x5f;
}
static __attribute__((unused))
int isspace(int c)
{
/* \t is 0x9, \n is 0xA, \v is 0xB, \f is 0xC, \r is 0xD */
return ((unsigned int)c == ' ') || (unsigned int)(c - 0x09) < 5;
}
static __attribute__((unused))
int isupper(int c)
{
return (unsigned int)(c - 'A') < 26;
}
static __attribute__((unused))
int isxdigit(int c)
{
return isdigit(c) || (unsigned int)(c - 'A') < 6 || (unsigned int)(c - 'a') < 6;
}
static __attribute__((unused))
int isalpha(int c)
{
return islower(c) || isupper(c);
}
static __attribute__((unused))
int isalnum(int c)
{
return isalpha(c) || isdigit(c);
}
static __attribute__((unused))
int ispunct(int c)
{
return isgraph(c) && !isalnum(c);
}
#endif /* _NOLIBC_CTYPE_H */

View File

@ -0,0 +1,27 @@
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
* Minimal errno definitions for NOLIBC
* Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>
*/
#ifndef _NOLIBC_ERRNO_H
#define _NOLIBC_ERRNO_H
#include <asm/errno.h>
/* this way it will be removed if unused */
static int errno;
#ifndef NOLIBC_IGNORE_ERRNO
#define SET_ERRNO(v) do { errno = (v); } while (0)
#else
#define SET_ERRNO(v) do { } while (0)
#endif
/* errno codes all ensure that they will not conflict with a valid pointer
* because they all correspond to the highest addressable memory page.
*/
#define MAX_ERRNO 4095
#endif /* _NOLIBC_ERRNO_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
* signal function definitions for NOLIBC
* Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>
*/
#ifndef _NOLIBC_SIGNAL_H
#define _NOLIBC_SIGNAL_H
#include "std.h"
#include "arch.h"
#include "types.h"
#include "sys.h"
/* This one is not marked static as it's needed by libgcc for divide by zero */
__attribute__((weak,unused,section(".text.nolibc_raise")))
int raise(int signal)
{
return sys_kill(sys_getpid(), signal);
}
#endif /* _NOLIBC_SIGNAL_H */

View File

@ -0,0 +1,49 @@
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
* Standard definitions and types for NOLIBC
* Copyright (C) 2017-2021 Willy Tarreau <w@1wt.eu>
*/
#ifndef _NOLIBC_STD_H
#define _NOLIBC_STD_H
/* Declare a few quite common macros and types that usually are in stdlib.h,
* stdint.h, ctype.h, unistd.h and a few other common locations. Please place
* integer type definitions and generic macros here, but avoid OS-specific and
* syscall-specific stuff, as this file is expected to be included very early.
*/
/* note: may already be defined */
#ifndef NULL
#define NULL ((void *)0)
#endif
/* stdint types */
typedef unsigned char uint8_t;
typedef signed char int8_t;
typedef unsigned short uint16_t;
typedef signed short int16_t;
typedef unsigned int uint32_t;
typedef signed int int32_t;
typedef unsigned long long uint64_t;
typedef signed long long int64_t;
typedef unsigned long size_t;
typedef signed long ssize_t;
typedef unsigned long uintptr_t;
typedef signed long intptr_t;
typedef signed long ptrdiff_t;
/* those are commonly provided by sys/types.h */
typedef unsigned int dev_t;
typedef unsigned long ino_t;
typedef unsigned int mode_t;
typedef signed int pid_t;
typedef unsigned int uid_t;
typedef unsigned int gid_t;
typedef unsigned long nlink_t;
typedef signed long off_t;
typedef signed long blksize_t;
typedef signed long blkcnt_t;
typedef signed long time_t;
#endif /* _NOLIBC_STD_H */

View File

@ -0,0 +1,306 @@
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
* minimal stdio function definitions for NOLIBC
* Copyright (C) 2017-2021 Willy Tarreau <w@1wt.eu>
*/
#ifndef _NOLIBC_STDIO_H
#define _NOLIBC_STDIO_H
#include <stdarg.h>
#include "std.h"
#include "arch.h"
#include "errno.h"
#include "types.h"
#include "sys.h"
#include "stdlib.h"
#include "string.h"
#ifndef EOF
#define EOF (-1)
#endif
/* just define FILE as a non-empty type */
typedef struct FILE {
char dummy[1];
} FILE;
/* We define the 3 common stdio files as constant invalid pointers that
* are easily recognized.
*/
static __attribute__((unused)) FILE* const stdin = (FILE*)-3;
static __attribute__((unused)) FILE* const stdout = (FILE*)-2;
static __attribute__((unused)) FILE* const stderr = (FILE*)-1;
/* getc(), fgetc(), getchar() */
#define getc(stream) fgetc(stream)
static __attribute__((unused))
int fgetc(FILE* stream)
{
unsigned char ch;
int fd;
if (stream < stdin || stream > stderr)
return EOF;
fd = 3 + (long)stream;
if (read(fd, &ch, 1) <= 0)
return EOF;
return ch;
}
static __attribute__((unused))
int getchar(void)
{
return fgetc(stdin);
}
/* putc(), fputc(), putchar() */
#define putc(c, stream) fputc(c, stream)
static __attribute__((unused))
int fputc(int c, FILE* stream)
{
unsigned char ch = c;
int fd;
if (stream < stdin || stream > stderr)
return EOF;
fd = 3 + (long)stream;
if (write(fd, &ch, 1) <= 0)
return EOF;
return ch;
}
static __attribute__((unused))
int putchar(int c)
{
return fputc(c, stdout);
}
/* fwrite(), puts(), fputs(). Note that puts() emits '\n' but not fputs(). */
/* internal fwrite()-like function which only takes a size and returns 0 on
* success or EOF on error. It automatically retries on short writes.
*/
static __attribute__((unused))
int _fwrite(const void *buf, size_t size, FILE *stream)
{
ssize_t ret;
int fd;
if (stream < stdin || stream > stderr)
return EOF;
fd = 3 + (long)stream;
while (size) {
ret = write(fd, buf, size);
if (ret <= 0)
return EOF;
size -= ret;
buf += ret;
}
return 0;
}
static __attribute__((unused))
size_t fwrite(const void *s, size_t size, size_t nmemb, FILE *stream)
{
size_t written;
for (written = 0; written < nmemb; written++) {
if (_fwrite(s, size, stream) != 0)
break;
s += size;
}
return written;
}
static __attribute__((unused))
int fputs(const char *s, FILE *stream)
{
return _fwrite(s, strlen(s), stream);
}
static __attribute__((unused))
int puts(const char *s)
{
if (fputs(s, stdout) == EOF)
return EOF;
return putchar('\n');
}
/* fgets() */
static __attribute__((unused))
char *fgets(char *s, int size, FILE *stream)
{
int ofs;
int c;
for (ofs = 0; ofs + 1 < size;) {
c = fgetc(stream);
if (c == EOF)
break;
s[ofs++] = c;
if (c == '\n')
break;
}
if (ofs < size)
s[ofs] = 0;
return ofs ? s : NULL;
}
/* minimal vfprintf(). It supports the following formats:
* - %[l*]{d,u,c,x,p}
* - %s
* - unknown modifiers are ignored.
*/
static __attribute__((unused))
int vfprintf(FILE *stream, const char *fmt, va_list args)
{
char escape, lpref, c;
unsigned long long v;
unsigned int written;
size_t len, ofs;
char tmpbuf[21];
const char *outstr;
written = ofs = escape = lpref = 0;
while (1) {
c = fmt[ofs++];
if (escape) {
/* we're in an escape sequence, ofs == 1 */
escape = 0;
if (c == 'c' || c == 'd' || c == 'u' || c == 'x' || c == 'p') {
char *out = tmpbuf;
if (c == 'p')
v = va_arg(args, unsigned long);
else if (lpref) {
if (lpref > 1)
v = va_arg(args, unsigned long long);
else
v = va_arg(args, unsigned long);
} else
v = va_arg(args, unsigned int);
if (c == 'd') {
/* sign-extend the value */
if (lpref == 0)
v = (long long)(int)v;
else if (lpref == 1)
v = (long long)(long)v;
}
switch (c) {
case 'c':
out[0] = v;
out[1] = 0;
break;
case 'd':
i64toa_r(v, out);
break;
case 'u':
u64toa_r(v, out);
break;
case 'p':
*(out++) = '0';
*(out++) = 'x';
/* fall through */
default: /* 'x' and 'p' above */
u64toh_r(v, out);
break;
}
outstr = tmpbuf;
}
else if (c == 's') {
outstr = va_arg(args, char *);
if (!outstr)
outstr="(null)";
}
else if (c == '%') {
/* queue it verbatim */
continue;
}
else {
/* modifiers or final 0 */
if (c == 'l') {
/* long format prefix, maintain the escape */
lpref++;
}
escape = 1;
goto do_escape;
}
len = strlen(outstr);
goto flush_str;
}
/* not an escape sequence */
if (c == 0 || c == '%') {
/* flush pending data on escape or end */
escape = 1;
lpref = 0;
outstr = fmt;
len = ofs - 1;
flush_str:
if (_fwrite(outstr, len, stream) != 0)
break;
written += len;
do_escape:
if (c == 0)
break;
fmt += ofs;
ofs = 0;
continue;
}
/* literal char, just queue it */
}
return written;
}
static __attribute__((unused))
int fprintf(FILE *stream, const char *fmt, ...)
{
va_list args;
int ret;
va_start(args, fmt);
ret = vfprintf(stream, fmt, args);
va_end(args);
return ret;
}
static __attribute__((unused))
int printf(const char *fmt, ...)
{
va_list args;
int ret;
va_start(args, fmt);
ret = vfprintf(stdout, fmt, args);
va_end(args);
return ret;
}
static __attribute__((unused))
void perror(const char *msg)
{
fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno);
}
#endif /* _NOLIBC_STDIO_H */

View File

@ -0,0 +1,423 @@
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
* stdlib function definitions for NOLIBC
* Copyright (C) 2017-2021 Willy Tarreau <w@1wt.eu>
*/
#ifndef _NOLIBC_STDLIB_H
#define _NOLIBC_STDLIB_H
#include "std.h"
#include "arch.h"
#include "types.h"
#include "sys.h"
#include "string.h"
struct nolibc_heap {
size_t len;
char user_p[] __attribute__((__aligned__));
};
/* Buffer used to store int-to-ASCII conversions. Will only be implemented if
* any of the related functions is implemented. The area is large enough to
* store "18446744073709551615" or "-9223372036854775808" and the final zero.
*/
static __attribute__((unused)) char itoa_buffer[21];
/*
* As much as possible, please keep functions alphabetically sorted.
*/
/* must be exported, as it's used by libgcc for various divide functions */
__attribute__((weak,unused,noreturn,section(".text.nolibc_abort")))
void abort(void)
{
sys_kill(sys_getpid(), SIGABRT);
for (;;);
}
static __attribute__((unused))
long atol(const char *s)
{
unsigned long ret = 0;
unsigned long d;
int neg = 0;
if (*s == '-') {
neg = 1;
s++;
}
while (1) {
d = (*s++) - '0';
if (d > 9)
break;
ret *= 10;
ret += d;
}
return neg ? -ret : ret;
}
static __attribute__((unused))
int atoi(const char *s)
{
return atol(s);
}
static __attribute__((unused))
void free(void *ptr)
{
struct nolibc_heap *heap;
if (!ptr)
return;
heap = container_of(ptr, struct nolibc_heap, user_p);
munmap(heap, heap->len);
}
/* getenv() tries to find the environment variable named <name> in the
* environment array pointed to by global variable "environ" which must be
* declared as a char **, and must be terminated by a NULL (it is recommended
* to set this variable to the "envp" argument of main()). If the requested
* environment variable exists its value is returned otherwise NULL is
* returned. getenv() is forcefully inlined so that the reference to "environ"
* will be dropped if unused, even at -O0.
*/
static __attribute__((unused))
char *_getenv(const char *name, char **environ)
{
int idx, i;
if (environ) {
for (idx = 0; environ[idx]; idx++) {
for (i = 0; name[i] && name[i] == environ[idx][i];)
i++;
if (!name[i] && environ[idx][i] == '=')
return &environ[idx][i+1];
}
}
return NULL;
}
static inline __attribute__((unused,always_inline))
char *getenv(const char *name)
{
extern char **environ;
return _getenv(name, environ);
}
static __attribute__((unused))
void *malloc(size_t len)
{
struct nolibc_heap *heap;
/* Always allocate memory with size multiple of 4096. */
len = sizeof(*heap) + len;
len = (len + 4095UL) & -4096UL;
heap = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE,
-1, 0);
if (__builtin_expect(heap == MAP_FAILED, 0))
return NULL;
heap->len = len;
return heap->user_p;
}
static __attribute__((unused))
void *calloc(size_t size, size_t nmemb)
{
void *orig;
size_t res = 0;
if (__builtin_expect(__builtin_mul_overflow(nmemb, size, &res), 0)) {
SET_ERRNO(ENOMEM);
return NULL;
}
/*
* No need to zero the heap, the MAP_ANONYMOUS in malloc()
* already does it.
*/
return malloc(res);
}
static __attribute__((unused))
void *realloc(void *old_ptr, size_t new_size)
{
struct nolibc_heap *heap;
size_t user_p_len;
void *ret;
if (!old_ptr)
return malloc(new_size);
heap = container_of(old_ptr, struct nolibc_heap, user_p);
user_p_len = heap->len - sizeof(*heap);
/*
* Don't realloc() if @user_p_len >= @new_size, this block of
* memory is still enough to handle the @new_size. Just return
* the same pointer.
*/
if (user_p_len >= new_size)
return old_ptr;
ret = malloc(new_size);
if (__builtin_expect(!ret, 0))
return NULL;
memcpy(ret, heap->user_p, heap->len);
munmap(heap, heap->len);
return ret;
}
/* Converts the unsigned long integer <in> to its hex representation into
* buffer <buffer>, which must be long enough to store the number and the
* trailing zero (17 bytes for "ffffffffffffffff" or 9 for "ffffffff"). The
* buffer is filled from the first byte, and the number of characters emitted
* (not counting the trailing zero) is returned. The function is constructed
* in a way to optimize the code size and avoid any divide that could add a
* dependency on large external functions.
*/
static __attribute__((unused))
int utoh_r(unsigned long in, char *buffer)
{
signed char pos = (~0UL > 0xfffffffful) ? 60 : 28;
int digits = 0;
int dig;
do {
dig = in >> pos;
in -= (uint64_t)dig << pos;
pos -= 4;
if (dig || digits || pos < 0) {
if (dig > 9)
dig += 'a' - '0' - 10;
buffer[digits++] = '0' + dig;
}
} while (pos >= 0);
buffer[digits] = 0;
return digits;
}
/* converts unsigned long <in> to an hex string using the static itoa_buffer
* and returns the pointer to that string.
*/
static inline __attribute__((unused))
char *utoh(unsigned long in)
{
utoh_r(in, itoa_buffer);
return itoa_buffer;
}
/* Converts the unsigned long integer <in> to its string representation into
* buffer <buffer>, which must be long enough to store the number and the
* trailing zero (21 bytes for 18446744073709551615 in 64-bit, 11 for
* 4294967295 in 32-bit). The buffer is filled from the first byte, and the
* number of characters emitted (not counting the trailing zero) is returned.
* The function is constructed in a way to optimize the code size and avoid
* any divide that could add a dependency on large external functions.
*/
static __attribute__((unused))
int utoa_r(unsigned long in, char *buffer)
{
unsigned long lim;
int digits = 0;
int pos = (~0UL > 0xfffffffful) ? 19 : 9;
int dig;
do {
for (dig = 0, lim = 1; dig < pos; dig++)
lim *= 10;
if (digits || in >= lim || !pos) {
for (dig = 0; in >= lim; dig++)
in -= lim;
buffer[digits++] = '0' + dig;
}
} while (pos--);
buffer[digits] = 0;
return digits;
}
/* Converts the signed long integer <in> to its string representation into
* buffer <buffer>, which must be long enough to store the number and the
* trailing zero (21 bytes for -9223372036854775808 in 64-bit, 12 for
* -2147483648 in 32-bit). The buffer is filled from the first byte, and the
* number of characters emitted (not counting the trailing zero) is returned.
*/
static __attribute__((unused))
int itoa_r(long in, char *buffer)
{
char *ptr = buffer;
int len = 0;
if (in < 0) {
in = -in;
*(ptr++) = '-';
len++;
}
len += utoa_r(in, ptr);
return len;
}
/* for historical compatibility, same as above but returns the pointer to the
* buffer.
*/
static inline __attribute__((unused))
char *ltoa_r(long in, char *buffer)
{
itoa_r(in, buffer);
return buffer;
}
/* converts long integer <in> to a string using the static itoa_buffer and
* returns the pointer to that string.
*/
static inline __attribute__((unused))
char *itoa(long in)
{
itoa_r(in, itoa_buffer);
return itoa_buffer;
}
/* converts long integer <in> to a string using the static itoa_buffer and
* returns the pointer to that string. Same as above, for compatibility.
*/
static inline __attribute__((unused))
char *ltoa(long in)
{
itoa_r(in, itoa_buffer);
return itoa_buffer;
}
/* converts unsigned long integer <in> to a string using the static itoa_buffer
* and returns the pointer to that string.
*/
static inline __attribute__((unused))
char *utoa(unsigned long in)
{
utoa_r(in, itoa_buffer);
return itoa_buffer;
}
/* Converts the unsigned 64-bit integer <in> to its hex representation into
* buffer <buffer>, which must be long enough to store the number and the
* trailing zero (17 bytes for "ffffffffffffffff"). The buffer is filled from
* the first byte, and the number of characters emitted (not counting the
* trailing zero) is returned. The function is constructed in a way to optimize
* the code size and avoid any divide that could add a dependency on large
* external functions.
*/
static __attribute__((unused))
int u64toh_r(uint64_t in, char *buffer)
{
signed char pos = 60;
int digits = 0;
int dig;
do {
if (sizeof(long) >= 8) {
dig = (in >> pos) & 0xF;
} else {
/* 32-bit platforms: avoid a 64-bit shift */
uint32_t d = (pos >= 32) ? (in >> 32) : in;
dig = (d >> (pos & 31)) & 0xF;
}
if (dig > 9)
dig += 'a' - '0' - 10;
pos -= 4;
if (dig || digits || pos < 0)
buffer[digits++] = '0' + dig;
} while (pos >= 0);
buffer[digits] = 0;
return digits;
}
/* converts uint64_t <in> to an hex string using the static itoa_buffer and
* returns the pointer to that string.
*/
static inline __attribute__((unused))
char *u64toh(uint64_t in)
{
u64toh_r(in, itoa_buffer);
return itoa_buffer;
}
/* Converts the unsigned 64-bit integer <in> to its string representation into
* buffer <buffer>, which must be long enough to store the number and the
* trailing zero (21 bytes for 18446744073709551615). The buffer is filled from
* the first byte, and the number of characters emitted (not counting the
* trailing zero) is returned. The function is constructed in a way to optimize
* the code size and avoid any divide that could add a dependency on large
* external functions.
*/
static __attribute__((unused))
int u64toa_r(uint64_t in, char *buffer)
{
unsigned long long lim;
int digits = 0;
int pos = 19; /* start with the highest possible digit */
int dig;
do {
for (dig = 0, lim = 1; dig < pos; dig++)
lim *= 10;
if (digits || in >= lim || !pos) {
for (dig = 0; in >= lim; dig++)
in -= lim;
buffer[digits++] = '0' + dig;
}
} while (pos--);
buffer[digits] = 0;
return digits;
}
/* Converts the signed 64-bit integer <in> to its string representation into
* buffer <buffer>, which must be long enough to store the number and the
* trailing zero (21 bytes for -9223372036854775808). The buffer is filled from
* the first byte, and the number of characters emitted (not counting the
* trailing zero) is returned.
*/
static __attribute__((unused))
int i64toa_r(int64_t in, char *buffer)
{
char *ptr = buffer;
int len = 0;
if (in < 0) {
in = -in;
*(ptr++) = '-';
len++;
}
len += u64toa_r(in, ptr);
return len;
}
/* converts int64_t <in> to a string using the static itoa_buffer and returns
* the pointer to that string.
*/
static inline __attribute__((unused))
char *i64toa(int64_t in)
{
i64toa_r(in, itoa_buffer);
return itoa_buffer;
}
/* converts uint64_t <in> to a string using the static itoa_buffer and returns
* the pointer to that string.
*/
static inline __attribute__((unused))
char *u64toa(uint64_t in)
{
u64toa_r(in, itoa_buffer);
return itoa_buffer;
}
#endif /* _NOLIBC_STDLIB_H */

View File

@ -0,0 +1,285 @@
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
* string function definitions for NOLIBC
* Copyright (C) 2017-2021 Willy Tarreau <w@1wt.eu>
*/
#ifndef _NOLIBC_STRING_H
#define _NOLIBC_STRING_H
#include "std.h"
static void *malloc(size_t len);
/*
* As much as possible, please keep functions alphabetically sorted.
*/
static __attribute__((unused))
int memcmp(const void *s1, const void *s2, size_t n)
{
size_t ofs = 0;
char c1 = 0;
while (ofs < n && !(c1 = ((char *)s1)[ofs] - ((char *)s2)[ofs])) {
ofs++;
}
return c1;
}
static __attribute__((unused))
void *_nolibc_memcpy_up(void *dst, const void *src, size_t len)
{
size_t pos = 0;
while (pos < len) {
((char *)dst)[pos] = ((const char *)src)[pos];
pos++;
}
return dst;
}
static __attribute__((unused))
void *_nolibc_memcpy_down(void *dst, const void *src, size_t len)
{
while (len) {
len--;
((char *)dst)[len] = ((const char *)src)[len];
}
return dst;
}
/* might be ignored by the compiler without -ffreestanding, then found as
* missing.
*/
__attribute__((weak,unused,section(".text.nolibc_memmove")))
void *memmove(void *dst, const void *src, size_t len)
{
size_t dir, pos;
pos = len;
dir = -1;
if (dst < src) {
pos = -1;
dir = 1;
}
while (len) {
pos += dir;
((char *)dst)[pos] = ((const char *)src)[pos];
len--;
}
return dst;
}
/* must be exported, as it's used by libgcc on ARM */
__attribute__((weak,unused,section(".text.nolibc_memcpy")))
void *memcpy(void *dst, const void *src, size_t len)
{
return _nolibc_memcpy_up(dst, src, len);
}
/* might be ignored by the compiler without -ffreestanding, then found as
* missing.
*/
__attribute__((weak,unused,section(".text.nolibc_memset")))
void *memset(void *dst, int b, size_t len)
{
char *p = dst;
while (len--)
*(p++) = b;
return dst;
}
static __attribute__((unused))
char *strchr(const char *s, int c)
{
while (*s) {
if (*s == (char)c)
return (char *)s;
s++;
}
return NULL;
}
static __attribute__((unused))
int strcmp(const char *a, const char *b)
{
unsigned int c;
int diff;
while (!(diff = (unsigned char)*a++ - (c = (unsigned char)*b++)) && c)
;
return diff;
}
static __attribute__((unused))
char *strcpy(char *dst, const char *src)
{
char *ret = dst;
while ((*dst++ = *src++));
return ret;
}
/* this function is only used with arguments that are not constants or when
* it's not known because optimizations are disabled.
*/
static __attribute__((unused))
size_t nolibc_strlen(const char *str)
{
size_t len;
for (len = 0; str[len]; len++);
return len;
}
/* do not trust __builtin_constant_p() at -O0, as clang will emit a test and
* the two branches, then will rely on an external definition of strlen().
*/
#if defined(__OPTIMIZE__)
#define strlen(str) ({ \
__builtin_constant_p((str)) ? \
__builtin_strlen((str)) : \
nolibc_strlen((str)); \
})
#else
#define strlen(str) nolibc_strlen((str))
#endif
static __attribute__((unused))
size_t strnlen(const char *str, size_t maxlen)
{
size_t len;
for (len = 0; (len < maxlen) && str[len]; len++);
return len;
}
static __attribute__((unused))
char *strdup(const char *str)
{
size_t len;
char *ret;
len = strlen(str);
ret = malloc(len + 1);
if (__builtin_expect(ret != NULL, 1))
memcpy(ret, str, len + 1);
return ret;
}
static __attribute__((unused))
char *strndup(const char *str, size_t maxlen)
{
size_t len;
char *ret;
len = strnlen(str, maxlen);
ret = malloc(len + 1);
if (__builtin_expect(ret != NULL, 1)) {
memcpy(ret, str, len);
ret[len] = '\0';
}
return ret;
}
static __attribute__((unused))
size_t strlcat(char *dst, const char *src, size_t size)
{
size_t len;
char c;
for (len = 0; dst[len]; len++)
;
for (;;) {
c = *src;
if (len < size)
dst[len] = c;
if (!c)
break;
len++;
src++;
}
return len;
}
static __attribute__((unused))
size_t strlcpy(char *dst, const char *src, size_t size)
{
size_t len;
char c;
for (len = 0;;) {
c = src[len];
if (len < size)
dst[len] = c;
if (!c)
break;
len++;
}
return len;
}
static __attribute__((unused))
char *strncat(char *dst, const char *src, size_t size)
{
char *orig = dst;
while (*dst)
dst++;
while (size && (*dst = *src)) {
src++;
dst++;
size--;
}
*dst = 0;
return orig;
}
static __attribute__((unused))
int strncmp(const char *a, const char *b, size_t size)
{
unsigned int c;
int diff = 0;
while (size-- &&
!(diff = (unsigned char)*a++ - (c = (unsigned char)*b++)) && c)
;
return diff;
}
static __attribute__((unused))
char *strncpy(char *dst, const char *src, size_t size)
{
size_t len;
for (len = 0; len < size; len++)
if ((dst[len] = *src))
src++;
return dst;
}
static __attribute__((unused))
char *strrchr(const char *s, int c)
{
const char *ret = NULL;
while (*s) {
if (*s == (char)c)
ret = s;
s++;
}
return (char *)ret;
}
#endif /* _NOLIBC_STRING_H */

1247
tools/include/nolibc/sys.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
* time function definitions for NOLIBC
* Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>
*/
#ifndef _NOLIBC_TIME_H
#define _NOLIBC_TIME_H
#include "std.h"
#include "arch.h"
#include "types.h"
#include "sys.h"
static __attribute__((unused))
time_t time(time_t *tptr)
{
struct timeval tv;
/* note, cannot fail here */
sys_gettimeofday(&tv, NULL);
if (tptr)
*tptr = tv.tv_sec;
return tv.tv_sec;
}
#endif /* _NOLIBC_TIME_H */

View File

@ -0,0 +1,205 @@
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
* Special types used by various syscalls for NOLIBC
* Copyright (C) 2017-2021 Willy Tarreau <w@1wt.eu>
*/
#ifndef _NOLIBC_TYPES_H
#define _NOLIBC_TYPES_H
#include "std.h"
#include <linux/time.h>
/* Only the generic macros and types may be defined here. The arch-specific
* ones such as the O_RDONLY and related macros used by fcntl() and open(), or
* the layout of sys_stat_struct must not be defined here.
*/
/* stat flags (WARNING, octal here) */
#define S_IFDIR 0040000
#define S_IFCHR 0020000
#define S_IFBLK 0060000
#define S_IFREG 0100000
#define S_IFIFO 0010000
#define S_IFLNK 0120000
#define S_IFSOCK 0140000
#define S_IFMT 0170000
#define S_ISDIR(mode) (((mode) & S_IFDIR) == S_IFDIR)
#define S_ISCHR(mode) (((mode) & S_IFCHR) == S_IFCHR)
#define S_ISBLK(mode) (((mode) & S_IFBLK) == S_IFBLK)
#define S_ISREG(mode) (((mode) & S_IFREG) == S_IFREG)
#define S_ISFIFO(mode) (((mode) & S_IFIFO) == S_IFIFO)
#define S_ISLNK(mode) (((mode) & S_IFLNK) == S_IFLNK)
#define S_ISSOCK(mode) (((mode) & S_IFSOCK) == S_IFSOCK)
/* dirent types */
#define DT_UNKNOWN 0x0
#define DT_FIFO 0x1
#define DT_CHR 0x2
#define DT_DIR 0x4
#define DT_BLK 0x6
#define DT_REG 0x8
#define DT_LNK 0xa
#define DT_SOCK 0xc
/* commonly an fd_set represents 256 FDs */
#ifndef FD_SETSIZE
#define FD_SETSIZE 256
#endif
/* PATH_MAX and MAXPATHLEN are often used and found with plenty of different
* values.
*/
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
#ifndef MAXPATHLEN
#define MAXPATHLEN (PATH_MAX)
#endif
/* Special FD used by all the *at functions */
#ifndef AT_FDCWD
#define AT_FDCWD (-100)
#endif
/* whence values for lseek() */
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
/* cmd for reboot() */
#define LINUX_REBOOT_MAGIC1 0xfee1dead
#define LINUX_REBOOT_MAGIC2 0x28121969
#define LINUX_REBOOT_CMD_HALT 0xcdef0123
#define LINUX_REBOOT_CMD_POWER_OFF 0x4321fedc
#define LINUX_REBOOT_CMD_RESTART 0x01234567
#define LINUX_REBOOT_CMD_SW_SUSPEND 0xd000fce2
/* Macros used on waitpid()'s return status */
#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
#define WIFEXITED(status) (((status) & 0x7f) == 0)
/* waitpid() flags */
#define WNOHANG 1
/* standard exit() codes */
#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1
/* for select() */
typedef struct {
uint32_t fd32[(FD_SETSIZE + 31) / 32];
} fd_set;
#define FD_CLR(fd, set) do { \
fd_set *__set = (set); \
int __fd = (fd); \
if (__fd >= 0) \
__set->fd32[__fd / 32] &= ~(1U << (__fd & 31)); \
} while (0)
#define FD_SET(fd, set) do { \
fd_set *__set = (set); \
int __fd = (fd); \
if (__fd >= 0) \
__set->fd32[__fd / 32] |= 1U << (__fd & 31); \
} while (0)
#define FD_ISSET(fd, set) ({ \
fd_set *__set = (set); \
int __fd = (fd); \
int __r = 0; \
if (__fd >= 0) \
__r = !!(__set->fd32[__fd / 32] & 1U << (__fd & 31)); \
__r; \
})
#define FD_ZERO(set) do { \
fd_set *__set = (set); \
int __idx; \
for (__idx = 0; __idx < (FD_SETSIZE+31) / 32; __idx ++) \
__set->fd32[__idx] = 0; \
} while (0)
/* for poll() */
#define POLLIN 0x0001
#define POLLPRI 0x0002
#define POLLOUT 0x0004
#define POLLERR 0x0008
#define POLLHUP 0x0010
#define POLLNVAL 0x0020
struct pollfd {
int fd;
short int events;
short int revents;
};
/* for getdents64() */
struct linux_dirent64 {
uint64_t d_ino;
int64_t d_off;
unsigned short d_reclen;
unsigned char d_type;
char d_name[];
};
/* needed by wait4() */
struct rusage {
struct timeval ru_utime;
struct timeval ru_stime;
long ru_maxrss;
long ru_ixrss;
long ru_idrss;
long ru_isrss;
long ru_minflt;
long ru_majflt;
long ru_nswap;
long ru_inblock;
long ru_oublock;
long ru_msgsnd;
long ru_msgrcv;
long ru_nsignals;
long ru_nvcsw;
long ru_nivcsw;
};
/* The format of the struct as returned by the libc to the application, which
* significantly differs from the format returned by the stat() syscall flavours.
*/
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for file system I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};
/* WARNING, it only deals with the 4096 first majors and 256 first minors */
#define makedev(major, minor) ((dev_t)((((major) & 0xfff) << 8) | ((minor) & 0xff)))
#define major(dev) ((unsigned int)(((dev) >> 8) & 0xfff))
#define minor(dev) ((unsigned int)(((dev) & 0xff))
#ifndef offsetof
#define offsetof(TYPE, FIELD) ((size_t) &((TYPE *)0)->FIELD)
#endif
#ifndef container_of
#define container_of(PTR, TYPE, FIELD) ({ \
__typeof__(((TYPE *)0)->FIELD) *__FIELD_PTR = (PTR); \
(TYPE *)((char *) __FIELD_PTR - offsetof(TYPE, FIELD)); \
})
#endif
#endif /* _NOLIBC_TYPES_H */

View File

@ -0,0 +1,54 @@
/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
/*
* unistd function definitions for NOLIBC
* Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>
*/
#ifndef _NOLIBC_UNISTD_H
#define _NOLIBC_UNISTD_H
#include "std.h"
#include "arch.h"
#include "types.h"
#include "sys.h"
static __attribute__((unused))
int msleep(unsigned int msecs)
{
struct timeval my_timeval = { msecs / 1000, (msecs % 1000) * 1000 };
if (sys_select(0, 0, 0, 0, &my_timeval) < 0)
return (my_timeval.tv_sec * 1000) +
(my_timeval.tv_usec / 1000) +
!!(my_timeval.tv_usec % 1000);
else
return 0;
}
static __attribute__((unused))
unsigned int sleep(unsigned int seconds)
{
struct timeval my_timeval = { seconds, 0 };
if (sys_select(0, 0, 0, 0, &my_timeval) < 0)
return my_timeval.tv_sec + !!my_timeval.tv_usec;
else
return 0;
}
static __attribute__((unused))
int usleep(unsigned int usecs)
{
struct timeval my_timeval = { usecs / 1000000, usecs % 1000000 };
return sys_select(0, 0, 0, 0, &my_timeval);
}
static __attribute__((unused))
int tcsetpgrp(int fd, pid_t pid)
{
return ioctl(fd, TIOCSPGRP, &pid);
}
#endif /* _NOLIBC_UNISTD_H */