forked from Minki/linux
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:
commit
f814957b01
@ -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,)
|
||||
|
42
tools/include/nolibc/Makefile
Normal file
42
tools/include/nolibc/Makefile
Normal 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"
|
199
tools/include/nolibc/arch-aarch64.h
Normal file
199
tools/include/nolibc/arch-aarch64.h
Normal 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
|
204
tools/include/nolibc/arch-arm.h
Normal file
204
tools/include/nolibc/arch-arm.h
Normal 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
|
219
tools/include/nolibc/arch-i386.h
Normal file
219
tools/include/nolibc/arch-i386.h
Normal 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
|
215
tools/include/nolibc/arch-mips.h
Normal file
215
tools/include/nolibc/arch-mips.h
Normal 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
|
204
tools/include/nolibc/arch-riscv.h
Normal file
204
tools/include/nolibc/arch-riscv.h
Normal 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
|
215
tools/include/nolibc/arch-x86_64.h
Normal file
215
tools/include/nolibc/arch-x86_64.h
Normal 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
|
32
tools/include/nolibc/arch.h
Normal file
32
tools/include/nolibc/arch.h
Normal 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 */
|
99
tools/include/nolibc/ctype.h
Normal file
99
tools/include/nolibc/ctype.h
Normal 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 */
|
27
tools/include/nolibc/errno.h
Normal file
27
tools/include/nolibc/errno.h
Normal 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
22
tools/include/nolibc/signal.h
Normal file
22
tools/include/nolibc/signal.h
Normal 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 */
|
49
tools/include/nolibc/std.h
Normal file
49
tools/include/nolibc/std.h
Normal 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 */
|
306
tools/include/nolibc/stdio.h
Normal file
306
tools/include/nolibc/stdio.h
Normal 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 */
|
423
tools/include/nolibc/stdlib.h
Normal file
423
tools/include/nolibc/stdlib.h
Normal 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 */
|
285
tools/include/nolibc/string.h
Normal file
285
tools/include/nolibc/string.h
Normal 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
1247
tools/include/nolibc/sys.h
Normal file
File diff suppressed because it is too large
Load Diff
28
tools/include/nolibc/time.h
Normal file
28
tools/include/nolibc/time.h
Normal 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 */
|
205
tools/include/nolibc/types.h
Normal file
205
tools/include/nolibc/types.h
Normal 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 */
|
54
tools/include/nolibc/unistd.h
Normal file
54
tools/include/nolibc/unistd.h
Normal 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 */
|
Loading…
Reference in New Issue
Block a user