First commit

This commit is contained in:
Calascibetta Romain 2024-12-20 23:45:12 +01:00
commit e7ccf2e72a
567 changed files with 68348 additions and 0 deletions

120
GNUmakefile Normal file
View file

@ -0,0 +1,120 @@
RM= rm
CC= $(CONFIG_TARGET_CC)
AS= $(CONFIG_TARGET_CC)
# NOTE(dinosaure): we must use [aarch64-none-elf-gcc] to compile assembly code
# to be able to include some files and define some macros.
LD= $(CONFIG_TARGET_LD)
AR= aarch64-none-elf-ar # TODO
OBJDUMP= aarch64-none-elf-objdump # TODO
OBJCOPY= $(CONFIG_TARGET_OBJCOPY)
export TOPDIR := $(abspath .)
$(TOPDIR)/Makeconf:
$(error Makeconf not found, please run ./configure.sh)
include $(TOPDIR)/Makeconf
# armstub8.bin: armstub8.S
# @echo "CC -DGIC=1 $<"
# @$(CC) -DGIC=1 -o ${<:S=o} -c $<
# @echo "LD --section-start=.text=0 ${<:S=o}"
# @$(LD) --section-start=.text=0 -o ${<:S=elf} ${<:S=o}
# @echo "DUMP ${<:S=elf}"
# @$(OBJDUMP) -D ${<:S=elf} > ${<:S=lst}
# @echo "COPY ${<:S=elf}"
# @$(OBJCOPY) ${<:S=elf} -O binary $@
include/$(CONFIG_TARGET_TRIPLE):
@echo "GEN $@"
@./gen-headers.sh $@
CPU ?= -mcpu=cortex-a76 -mlittle-endian
ARCH += -DAARCH=64 $(CPU)
TARGET ?= kernel_2712
DEFINE += -DSSP_GUARD_SYMBOL=__stack_chk_guard \
-DSSP_FAIL_SYMBOL=__stack_chk_fail
INCLUDE += -I $(TOPDIR)/include -I $(TOPDIR)/include/$(CONFIG_TARGET_TRIPLE) \
-I $(TOPDIR)/nolibc/include
AFLAGS += $(ARCH) $(DEFINE) $(INCLUDE)
CFLAGS += -std=c11 -ffreestanding -fstack-protector-strong -nostdlib \
-nostartfiles -mstrict-align -Wall \
$(ARCH) $(DEFINE) $(INCLUDE) $(CONFIG_TARGET_CC_CFLAGS)
LDFLAGS += -nostdlib -static -Wl,--no-warn-rwx-segments \
-Wl,--section-start=.init=0x80000 -Wl,--build-id=none \
-Wl,--start-group -L . -lgilbraltar -L nolibc -lnolibc -L openlibm -lopenlibm -lgcc -Wl,--end-group
SRCS= kernel.c timer.c led.c interrupt_handler.c exception_handler.c \
mbox.c clock.c crt.c serial.c log.c
ASMS= startup.S exception_stub.S
OBJS= $(SRCS:c=o) $(ASMS:S=o)
%.o: %.S
@echo "AS $@"
@$(AS) $(AFLAGS) -c -o $@ $<
%.o: %.c include/$(CONFIG_TARGET_TRIPLE)
@echo "CC $@"
@$(CC) $(CFLAGS) -c -o $@ $<
libgilbraltar.a: $(OBJS)
@echo "AR $@"
@rm -f $@
@$(AR) cr $@ $^
.PHONY: phony-openlibm
phony-openlibm: include/$(CONFIG_TARGET_TRIPLE)
@$(MAKE) -C openlibm \
"CC=$(CC)" "CPPFLAGS=$(CFLAGS)" libopenlibm.a
openlibm/libopenlibm.a: phony-openlibm
NOLIBC_CFLAGS= $(CFLAGS) -I $(TOPDIR)/nolibc/include -I $(TOPDIR)/openlibm/src -I $(TOPDIR)/openlibm/include
.PHONY: phony-nolibc
phony-nolibc: include/$(CONFIG_TARGET_TRIPLE)
$(MAKE) -C nolibc libnolibc.a \
"CC=$(CC)" "FREESTANDING_CFLAGS=$(NOLIBC_CFLAGS)"
nolibc/libnolibc.a: phony-nolibc
.PHONY: all
all: libgilbraltar.a
.PHONY: clean
clean:
$(RM) -f *.o *.a *.elf *.bin *.lst *.img
$(RM) -rf include/$(CONFIG_TARGET_TRIPLE)
$(RM) -f test/*.o
$(MAKE) -C openlibm clean
$(MAKE) -C nolibc clean FREESTANDING_CFLAGS=_
.PHONY: distclean
distclean: clean
$(RM) -f Makeconf
test/%.o: test/%.c
@echo "CC $@"
@$(CC) $(CFLAGS) -c -o $@ $<
test01.img: libgilbraltar.a nolibc/libnolibc.a openlibm/libopenlibm.a test/test01.o
@echo "LD ${@:img=elf}"
@$(LD) test/${@:img=o} -o ${@:img=elf} $(LDFLAGS) -Wl,-T gilbraltar.ld
@$(OBJCOPY) ${@:img=elf} -O binary $@
test02.img: libgilbraltar.a nolibc/libnolibc.a openlibm/libopenlibm.a test/test02.o
@echo "LD ${@:img=elf}"
@$(LD) test/${@:img=o} -o ${@:img=elf} $(LDFLAGS) -Wl,-T gilbraltar.ld
@$(OBJCOPY) ${@:img=elf} -O binary $@
test03.img: libgilbraltar.a nolibc/libnolibc.a openlibm/libopenlibm.a test/test03.o
@echo "LD ${@:img=elf}"
@$(LD) test/${@:img=o} -o ${@:img=elf} $(LDFLAGS) -Wl,-T gilbraltar.ld
@$(OBJCOPY) ${@:img=elf} -O binary $@

213
armstub8.S Normal file
View file

@ -0,0 +1,213 @@
/*
* Copyright (c) 2024 Romain Calascibetta <romain.calascibetta@gmail.com>
* Copyright (c) 2016-2019 Raspberry Pi (Trading) Ltd.
* Copyright (c) 2016 Stephen Warren <swarren@wwwdotorg.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#define BIT(x) (1 << (x))
#define LOCAL_CONTROL 0xff800000
#define LOCAL_PRESCALER 0xff800008
#define GIC_DISTB 0xff841000
#define GIC_CPUB 0xff842000
#define OSC_FREQ 54000000
#define SCR_RW BIT(10)
#define SCR_HCE BIT(8)
#define SCR_SMD BIT(7)
#define SCR_RES1_5 BIT(5)
#define SCR_RES1_4 BIT(4)
#define SCR_NS BIT(0)
#define SCR_VAL \
(SCR_RW | SCR_HCE | SCR_SMD | SCR_RES1_5 | SCR_RES1_4 | SCR_NS)
#define ACTLR_VAL \
(BIT(0) | BIT(1) | BIT(4) | BIT(5) | BIT(6))
#define CPUECTLR_EL1 S3_1_C15_C2_1
#define CPUECTLR_EL1_SMPEN BIT(6)
#define SPSR_EL3_D BIT(9)
#define SPSR_EL3_A BIT(8)
#define SPSR_EL3_I BIT(7)
#define SPSR_EL3_F BIT(6)
#define SPSR_EL3_MODE_EL2H 9
#define SPSR_EL3_VAL \
(SPSR_EL3_D | SPSR_EL3_A | SPSR_EL3_I | SPSR_EL3_F | SPSR_EL3_MODE_EL2H)
#define L2CTLR_EL1 S3_1_C11_C0_2
#define GICC_CTRLR 0x0
#define GICC_PMR 0x4
#define IT_NR 0x8 // Number of interrupt enable registers (256 total irqs)
#define GICD_CTRLR 0x0
#define GICD_IGROUPR 0x80
.globl _start
_start:
// LOCAL_CONTROL:
// Bit 9 clear: Increment by 1 (vs. 2).
// Bit 8 clear: Timer source is 19.2MHz crystal (vs. APB).
ldr x0, =LOCAL_CONTROL
str wzr, [x0]
// LOCAL_PRESCALER; divide-by (0x80000000 / register_val) == 1
mov w1, 0x80000000
str w1, [x0, #(LOCAL_PRESCALER - LOCAL_CONTROL)]
// Set L2 read/write cache latency to 3
mrs x0, L2CTLR_EL1
mov x1, #0x22
orr x0, x0, x1
msr L2CTLR_EL1, x0
// Set up CNTFRQ_EL0
ldr x0, =OSC_FREQ
msr CNTFRQ_EL0, x0
// Set up CNTVOFF_EL2
msr CNTVOFF_EL2, xzr
// Enable FP/SIMD
// Bit 10 (TFP) is set to 0
msr CPTR_EL3, xzr
// Set up SCR
mov x0, #SCR_VAL
msr SCR_EL3, x0
// Set up ACTLR
mov x0, #ACTLR_VAL
msr ACTLR_EL3, x0
// Set SMPEN
mov x0, #CPUECTLR_EL1_SMPEN
msr CPUECTLR_EL1, x0
#ifdef GIC
bl setup_gic
#endif
// Set up SCTLR_EL2
// All set bits below are res1. LE, no WXN/I/SA/C/A/M
ldr x0, =0x30c50830
msr SCTLR_EL2, x0
// Switch to EL2
mov x0, #SPSR_EL3_VAL
msr spsr_el3, x0
adr x0, in_el2
msr elr_el3, x0
eret
in_el2:
mrs x6, MPIDR_EL1
and x6, x6, #0x3
cbz x6, primary_cpu
adr x5, spin_cpu0
secondary_spin:
wfe
ldr x4, [x5, x6, lsl #3]
cbz x4, secondary_spin
mov x0, #0
b boot_kernel
primary_cpu:
ldr w4, kernel_entry32
ldr w0, dtb_ptr32
boot_kernel:
mov x1, #0
mov x2, #0
mov x3, #0
br x4
.ltorg
.org 0xd8
.globl spin_cpu0
spin_cpu0:
.quad 0
.org 0xe0
.globl spin_cpu1
spin_cpu1:
.quad 0
.org 0xe8
.globl spin_cpu2
spin_cpu2:
.quad 0
.org 0xf0
.globl spin_cpu3
spin_cpu3:
# Shared with next two symbols/.word
# FW clears the next 8 bytes after reading the initial value, leaving
# the location suitable for use as spin_cpu3
.org 0xf0
.globl stub_magic
stub_magic:
.word 0x5afe570b
.org 0xf4
.globl stub_version
stub_version:
.word 0
.org 0xf8
.globl dtb_ptr32
dtb_ptr32:
.word 0x0
.org 0xfc
.globl kernel_entry32
kernel_entry32:
.word 0x0
.org 0x100
#ifdef GIC
setup_gic: // Called from secure mode - set all interrupts to group 1 and enable.
mrs x0, MPIDR_EL1
ldr x2, =GIC_DISTB
tst x0, #0x3
b.eq 2f // primary core
mov w0, #3 // Enable group 0 and 1 IRQs from distributor
str w0, [x2, #GICD_CTRLR]
2:
add x1, x2, #(GIC_CPUB - GIC_DISTB)
mov w0, #0x1e7
str w0, [x1, #GICC_CTRLR] // Enable group 1 IRQs from CPU interface
mov w0, #0xff
str w0, [x1, #GICC_PMR] // priority mask
add x2, x2, #GICD_IGROUPR
mov x0, #(IT_NR * 4)
mov w1, #~0 // group 1 all the things
3:
subs x0, x0, #4
str w1, [x2, x0]
b.ne 3b
ret
#endif
.globl dtb_space
dtb_space:

41
clock.c Normal file
View file

@ -0,0 +1,41 @@
#include <mem.h>
#include <mbox.h>
#include <tag.h>
#define PROPTAG_GET_CLOCK_RATE 0x00030002
#define PROPTAG_GET_CLOCK_RATE_MEASURED 0x00030047
uint32_t gilbraltar_get_clock(uint32_t cid) {
uint32_t proptag0[] __attribute__ ((aligned(16))) =
{
8.4,
CODE_REQUEST,
PROPTAG_GET_CLOCK_RATE,
4*4,
1*4,
cid,
0,
PROPTAG_END
};
gilbraltar_mbox_write_read((uintptr_t) &proptag0);
if (proptag0[6] != 0)
return proptag0[6];
uint32_t proptag1[] __attribute__ ((aligned(16))) =
{
8*4,
CODE_REQUEST,
PROPTAG_GET_CLOCK_RATE_MEASURED,
4*4,
1*4,
cid,
0,
PROPTAG_END
};
gilbraltar_mbox_write_read((uintptr_t) &proptag1);
return proptag1[6];
}

456
configure.sh Executable file
View file

@ -0,0 +1,456 @@
#!/bin/sh
# Copyright (c) 2015-2020 Solo5 Contributors
# Copyright (c) 2024 Romain Calascibetta
#
# This file is part of Gilbraltar, a bare-metal OS for Raspberry Pi.
#
# Permission to use, copy, modify, and/or distribute this software
# for any purpose with or without fee is hereby granted, provided
# that the above copyright notice and this permission notice appear
# in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
TARGET_CC="${TARGET_CC:-aarch64-none-elf-gcc}"
TARGET_LD="${TARGET_LD:-aarch64-none-elf-gcc}"
TARGET_OBJCOPY="${TARGET_OBJCOPY:-aarch64-none-elf-objcopy}"
prog_NAME="$(basename $0)"
cleanup()
{
rm -f conftmp.c conftmp.d conftmp*.o
}
err()
{
echo "${prog_NAME}: ERROR: $@" 1>&2
}
die()
{
echo "${prog_NAME}: ERROR: $@" 1>&2
cleanup
exit 1
}
warn()
{
echo "${prog_NAME}: WARNING: $@" 1>&2
}
usage()
{
cat <<EOM 1>&2
usage: ${prog_NAME} [ OPTIONS ]
Configures the Gilbraltar build system.
Options:
--prefix=DIR
Installation prefix (default: /usr/local).
Environment variables affecting the build system configuration:
HOST_CC
C compiler used for host tools.
TARGET_CC (= $TARGET_CC)
TARGET_LD (= $TARGET_LD)
TARGET_OBJCOPY (= $TARGET_OBJCOPY)
C compiler, linker and objcopy used for target toolchain.
Experimental: If a cross-toolchain is specified here, the resulting
target toolchain and bindings will be cross-compiled to its
architecture.
EOM
exit 1
}
cc_maybe_gcc()
{
${CC} -dM -E - </dev/null | \
grep -Eq '^#define __GNUC__ ([4-9]$|[1-9][0-9]+$)'
}
cc_is_clang()
{
${CC} -dM -E - </dev/null | grep -Eq '^#define __clang__ 1$'
}
cc_has_pie()
{
${CC} -dM -E - </dev/null | grep -Eq '^#define __PIE__ [1-9]$'
}
cc_is_gcc()
{
cc_maybe_gcc && ! cc_is_clang
}
cc_check_option()
{
${CC} "$@" -x c -c -o /dev/null - <<EOM >/dev/null 2>&1
int main(int argc, char *argv[])
{
return 0;
}
EOM
}
cc_check_header()
{
${CC} ${PKG_CFLAGS} -x c -o /dev/null - <<EOM >/dev/null 2>&1
#include <$@>
int main(int argc, char *argv[])
{
return 0;
}
EOM
}
cc_check_lib()
{
${CC} -x c -o /dev/null - "$@" ${PKG_LIBS} <<EOM >/dev/null 2>&1
int main(int argc, char *argv[])
{
return 0;
}
EOM
}
ld_is_lld()
{
${LD} --version 2>&1 | grep -q '^LLD'
}
# Check that the linker ${LD} is available and suitable for our purposes.
check_ld()
{
echo -n "${prog_NAME}: Checking if ${LD} is available: "
if [ -x "$(command -v ${LD})" ]; then
echo "yes"
else
echo "no"
return 1
fi
echo -n "${prog_NAME}: Checking if ${LD} is LLD: "
if ld_is_lld; then
echo "yes"
# LLD < 8 chokes on the Xen ldscript, so refuse to use it.
echo -n "${prog_NAME}: Checking if LLD ${LD} is LLVM 8 or newer: "
if ${LD} --version 2>&1 | grep -q '^LLD [1-7]\.'; then
echo "no"
return 1
else
echo "yes"
fi
else
echo "no"
fi
cat >conftmp.c <<EOM
int foo(void)
{
return 1;
}
EOM
${CC} -c conftmp.c -o conftmp.o || return 1
echo -n "${prog_NAME}: Checking if ${LD} understands ${TARGET_ARCH}: "
if ! ${LD} -r -o conftmp1.o conftmp.o >/dev/null 2>&1; then
echo "no"
return 1
else
echo "yes"
fi
return 0
}
# Check that the objcopy ${OBJCOPY} is available and suitable for our purposes.
check_objcopy()
{
echo -n "${prog_NAME}: Checking if ${OBJCOPY} is available: "
if [ -x "$(command -v ${OBJCOPY})" ]; then
echo "yes"
else
echo "no"
return 1
fi
cat >conftmp.c <<EOM
int KEEP_ME(void)
{
return 1;
}
int local(void)
{
return 1;
}
EOM
${CC} -c conftmp.c -o conftmp.o || return 1
# [Clang] A LLVM objcopy will understand any LLVM-supported architecture.
# [GCC] A GNU objcopy will only understand the architecture it was
# targetted for.
echo -n "${prog_NAME}: Checking if ${OBJCOPY} understands ${TARGET_ARCH}: "
if ! ${OBJCOPY} conftmp.o conftmp.o >/dev/null 2>&1; then
echo "no"
return 1
else
echo "yes"
fi
# [Clang] For LLVM objcopy, -w was introduced in
# https://reviews.llvm.org/D66613 (release/12.x) and -G was introduced in
# https://reviews.llvm.org/D50589 (release/8.x).
echo -n "${prog_NAME}: Checking if ${OBJCOPY} understands -w -G: "
if ! ${OBJCOPY} -w -G KEEP\* conftmp.o conftmp.o >/dev/null 2>&1; then
echo "no"
return 1
else
echo "yes"
fi
return 0
}
OPT_PREFIX=/usr/local
while [ $# -gt 0 ]; do
OPT="$1"
case "${OPT}" in
--prefix=*)
OPT_PREFIX="${OPT##*=}"
;;
--help)
usage
;;
*)
err "Unknown option: '${OPT}'"
usage
;;
esac
shift
done
#
# Configure host tools based on HOST_CC.
#
HOST_CC=${HOST_CC:-cc}
HOST_CC_MACHINE=$(${HOST_CC} -dumpmachine)
[ $? -ne 0 ] &&
die "Could not run '${HOST_CC} -dumpmachine', is your compiler working?"
echo "${prog_NAME}: Using ${HOST_CC} for host compiler (${HOST_CC_MACHINE})"
case ${HOST_CC_MACHINE} in
x86_64-*linux*)
CONFIG_HOST_ARCH=x86_64 CONFIG_HOST=Linux
;;
aarch64-*linux*)
CONFIG_HOST_ARCH=aarch64 CONFIG_HOST=Linux
;;
powerpc64le-*linux*|ppc64le-*linux*)
CONFIG_HOST_ARCH=ppc64le CONFIG_HOST=Linux
;;
x86_64-*freebsd*)
CONFIG_HOST_ARCH=x86_64 CONFIG_HOST=FreeBSD
;;
amd64-*openbsd*)
CONFIG_HOST_ARCH=x86_64 CONFIG_HOST=OpenBSD
;;
*)
die "Unsupported host toolchain: ${HOST_CC_MACHINE}"
;;
esac
# If no toolchain was requested, stop here.
# XXX De-duplicate the generation of Makeconf*?
if [ -n "${OPT_DISABLE_TOOLCHAIN}" ]; then
cat <<EOM >Makeconf
# Generated by configure.sh
CONFIG_PREFIX=${OPT_PREFIX}
CONFIG_HOST_ARCH=${CONFIG_HOST_ARCH}
CONFIG_HOST=${CONFIG_HOST}
CONFIG_HOST_CC=${HOST_CC}
EOM
sed -Ee 's/^([A-Z_]+)=(.*)$/\1="\2"/' Makeconf >Makeconf.sh
cleanup
exit 0
fi
#
# Configure target toolchain and bindings based on TARGET_{CC,LD,OBJCOPY}.
#
TARGET_CC="${TARGET_CC:-cc}"
echo -n "${prog_NAME}: Checking that ${TARGET_CC} works: "
cat >conftmp.c <<EOM
int foo(void)
{
return 1;
}
EOM
if ! ${TARGET_CC} -c conftmp.c -o conftmp.o >/dev/null 2>&1; then
echo "no"
die "Could not find a working compiler for target toolchain"
else
echo "yes"
fi
TARGET_CC_MACHINE=$(${TARGET_CC} -dumpmachine)
CONFIG_HVT= CONFIG_SPT= CONFIG_VIRTIO= CONFIG_MUEN= CONFIG_XEN=
case ${TARGET_CC_MACHINE} in
x86_64-*|amd64-*)
TARGET_ARCH=x86_64
TARGET_LD_MAX_PAGE_SIZE=0x1000
;;
aarch64-*)
TARGET_ARCH=aarch64
TARGET_LD_MAX_PAGE_SIZE=0x1000
;;
powerpc64le-*|ppc64le-*)
TARGET_ARCH=ppc64le
TARGET_LD_MAX_PAGE_SIZE=0x10000
;;
*)
die "Unsupported target toolchain: ${TARGET_CC_MACHINE}"
;;
esac
TARGET_CC_CFLAGS=
TARGET_CC_IS_OPENBSD=
if CC="${TARGET_CC}" cc_is_clang; then
TARGET_CC_CFLAGS=-nostdlibinc
# XXX Clang warns for no good reason if -nostdlibinc is used and no
# compliation is performed. We could work around this by using --config
# which "claims" all command line arguments as "used", but this is easier
# for now.
TARGET_CC_CFLAGS="${TARGET_CC_CFLAGS} -Wno-unused-command-line-argument"
else
TARGET_CC_CFLAGS=-nostdinc
fi
case ${TARGET_CC_MACHINE} in
x86_64-*linux*|powerpc64le-*linux*|ppc64le-*linux*)
# On x86_64 and PPC we need to ensure that TLS is not used for the
# stack protector:
# [GCC] -mstack-protector-guard=global is available from GCC 4.9.0.
# [Clang] -mstack-protector-guard=global was introduced in
# https://reviews.llvm.org/D88631 (release/12.x).
CC="${TARGET_CC}" cc_check_option -mstack-protector-guard=global || \
die "${TARGET_CC} does not support -mstack-protector-guard="
TARGET_CC_CFLAGS="${TARGET_CC_CFLAGS} -mstack-protector-guard=global"
;;
*openbsd*)
# [Clang] OpenBSD's Clang needs to be told to switch off mitigations.
TARGET_CC_CFLAGS="${TARGET_CC_CFLAGS} -mno-retpoline -fno-ret-protector"
# [Clang] OpenBSD's LLVM/Clang uses a global stack protector guard, but
# different symbols; see bindings/GNUmakefile.
TARGET_CC_IS_OPENBSD=1
;;
esac
echo "${prog_NAME}:" \
"Using ${TARGET_CC} for target compiler (${TARGET_CC_MACHINE})"
# TARGET_CC_LDFLAGS are used by "cc as linker"; TARGET_LD_LDFLAGS are used by
# the plain "ld" wrapper.
# TODO: Are there Linux distributions with toolchains configured to force PIE
# even if -static is used? If yes, these will need treatment similar to OpenBSD
# below, and/or disabling PIE in "cc".
TARGET_CC_LDFLAGS=
TARGET_LD_LDFLAGS=
case ${CONFIG_HOST} in
Linux)
# The "--build-id=none" nonsense is needed for "cc as a linker" to suppress any
# generation of a .note.gnu.build-id, since our linker scripts don't
# support it and Linux "cc" toolchains insist on adding it by default
# which leads to linkers warning if it is enabled but the input section
# is subsequently discarded.
TARGET_LD="${TARGET_LD:-ld}"
TARGET_OBJCOPY="${TARGET_OBJCOPY:-objcopy}"
TARGET_CC_LDFLAGS="-Wl,--build-id=none"
;;
FreeBSD)
TARGET_LD="${TARGET_LD:-ld.lld}"
TARGET_OBJCOPY="${TARGET_OBJCOPY:-objcopy}"
# FreeBSD's GNU ld is old and broken, refuse to use it.
if ! LD="${TARGET_LD}" ld_is_lld; then
err "${TARGET_LD} is not LLVM LLD"
die "Using GNU LD is not supported on FreeBSD"
fi
;;
OpenBSD)
TARGET_LD="${TARGET_LD:-ld.lld}"
TARGET_OBJCOPY="${TARGET_OBJCOPY:-objcopy}"
# OpenBSD's GNU ld is old and broken, refuse to use it.
if ! LD="${TARGET_LD}" ld_is_lld; then
err "${TARGET_LD} is not LLVM LLD"
die "Using GNU LD is not supported on OpenBSD"
fi
# [LLD] OpenBSD's LLD needs to be explicitly told not to produce PIE
# executables.
TARGET_CC_LDFLAGS="-Wl,-nopie"
TARGET_LD_LDFLAGS="-nopie"
;;
*)
die "Unsupported host system: ${CONFIG_HOST}"
;;
esac
# [Clang] Force Clang to use the right TARGET_LD when run as "cc as linker".
# -fuse-ld= is braindead and accepts either a "flavour" (e.g. "lld") or an
# absolute path; we try with the latter.
# TODO XXX: This is fragile, but works for the default cases for now.
# TODO document: This won't work with clang "bare metal" targets.
if CC="${TARGET_CC}" cc_is_clang; then
REAL_TARGET_LD="$(command -v ${TARGET_LD})"
[ ! -x "${REAL_TARGET_LD}" ] && \
die "Cannot determine absolute path for ${TARGET_LD}"
TARGET_CC_LDFLAGS="${TARGET_CC_LDFLAGS} -fuse-ld=${REAL_TARGET_LD}"
fi
if ! CC="${TARGET_CC}" LD="${TARGET_LD}" check_ld; then
die "Could not find a working target linker"
fi
if ! CC="${TARGET_CC}" OBJCOPY="${TARGET_OBJCOPY}" check_objcopy; then
die "Could not find a working target objcopy"
fi
echo "${prog_NAME}: Using ${TARGET_LD} for target linker"
echo "${prog_NAME}: Using ${TARGET_OBJCOPY} for target objcopy"
TARGET_TRIPLE="${TARGET_ARCH}-gilbraltar-none-static"
echo "${prog_NAME}: Target toolchain triple is ${TARGET_TRIPLE}"
#
# Generate Makeconf, to be included by Makefiles.
#
cat <<EOM >Makeconf
# Generated by configure.sh
CONFIG_PREFIX=${OPT_PREFIX}
CONFIG_HOST_ARCH=${CONFIG_HOST_ARCH}
CONFIG_HOST=${CONFIG_HOST}
CONFIG_HOST_CC=${HOST_CC}
CONFIG_TARGET_ARCH=${TARGET_ARCH}
CONFIG_TARGET_TRIPLE=${TARGET_TRIPLE}
CONFIG_TARGET_CC=${TARGET_CC}
CONFIG_TARGET_CC_CFLAGS=${TARGET_CC_CFLAGS}
CONFIG_TARGET_CC_LDFLAGS=${TARGET_CC_LDFLAGS}
CONFIG_TARGET_CC_IS_OPENBSD=${TARGET_CC_IS_OPENBSD}
CONFIG_TARGET_LD=${TARGET_LD}
CONFIG_TARGET_LD_LDFLAGS=${TARGET_LD_LDFLAGS}
CONFIG_TARGET_LD_MAX_PAGE_SIZE=${TARGET_LD_MAX_PAGE_SIZE}
CONFIG_TARGET_OBJCOPY=${TARGET_OBJCOPY}
EOM
#
# Generate Makeconf.sh, to be included by shell scripts.
#
sed -Ee 's/^([A-Z_]+)=(.*)$/\1="\2"/' Makeconf >Makeconf.sh
cleanup

44
crt.c Normal file
View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2015-2019 Contributors as noted in the AUTHORS file
*
* This file is part of Gilbraltar, a bare-metal OS for RPi4.
*
* Permission to use, copy, modify, and/or distribute this software
* for any purpose with or without fee is hereby granted, provided
* that the above copyright notice and this permission notice appear
* in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stddef.h>
#include <stdint.h>
/*
* The stack canary value will be initialised to a pseudo-random value by
* crt_init_early(), keep an easily recognisable "terminator" value here to
* flag if that did not happen as expected.
*/
uintptr_t SSP_GUARD_SYMBOL = 0x00deadbeef0d0a00;
/*
* Called by compiler-generated code when corruption of the canary value is
* detected. There is very little we can do here safely, so just print a
* message and abort, taking care to make minimal use of the stack.
*/
static const char stack_chk_fail_message[] =
"ABORT: Stack corruption detected\n";
extern void uart_puts(const char *str);
__attribute__((noreturn)) void SSP_FAIL_SYMBOL(void) {
for (;;)
;
}

58
doc.txt Normal file
View file

@ -0,0 +1,58 @@
# How to make a simple unikernel on RBPi
File required on Raspberry Pi 5:
- bcm2712-rpi-5-b.dtb
- overlays/bcm2712d0.dtdo
- config.txt
CPU used by Raspberry Pi 5: Cortex-A76
BCM2712 is the Broadcom chip used by Raspberry Pi 5
# Memory layout
| Base | Size | Contents | Remarks |
|------------|----------------------|----------------------------|---------|
| 00000000 | 32 KByte | ARM Trusted Firmware | BL31 |
| 0006F000 | 4 KByte | EL3 Stack |
| 00070000 | 2 KByte | Exception vector table EL3 |
| ...
| 00080000 | max. 6 MByte | Kernel image |
| | .init | startup code |
| | .text | |
| | .rodata | |
| | .data | |
| | .bss | |
| ...
| | Kernel stacks | |
| 00280000 | 128 KByte | for Core 0 |
| 002A0000 | 128 KByte | for Core 1 |
| 002C0000 | 128 KByte | for Core 2 |
| 002E0000 | 128 KByte | for Core 3 |
| 00300000 | | End of stacks |
| | Exception stacks | |
| 00300000 | 32 KByte | for Core 0 |
| 00308000 | 32 KByte | for Core 1 |
| 00310000 | 32 KByte | for Core 2 |
| 00318000 | 32 KByte | for Core 3 |
| 00320000 | | End of exception stacks |
| | IRQ stack | |
| 00320000 | 32 KByte | for Core 0 |
| 00328000 | 32 KByte | for Core 1 |
| 00330000 | 32 KByte | for Core 2 |
| 00338000 | 32 KByte | for Core 3 |
| 00340000 | | End of IRQ stacks |
| | FIQ stack | |
| 00340000 | 32 KByte | for Core 0 |
| 00348000 | 32 KByte | for Core 1 |
| 00350000 | 32 KByte | for Core 2 |
| 00358000 | 32 KByte | for Core 3 |
| 00360000 | | End of FIQ stacks |
| | Page Table | |
| 00360000 | 16 KByte | |
| 00364000 | | End of page table |
| 00500000 | 4 MByte | Coherent region |
| 00900000 | | |
| 40000000 | | Heap |
| ...
| 1FFFFFFFF | 8192 MB | Heap |
| 1000000000 | 16 MByte | AXI peripherals |

6
exception_handler.c Normal file
View file

@ -0,0 +1,6 @@
#include <exception_handler.h>
void gilbraltar_exception_handler(uint64_t exn, struct frame *frame) {
while (1)
__asm__ __volatile("wfi");
}

298
exception_stub.S Normal file
View file

@ -0,0 +1,298 @@
/*
* exceptionstub.S
*
* Copyright (C) 2014-2020 R. Stange <rsta2@o2online.de>
* Copyright (C) 2024 Romain Calascibetta <romain.calascibetta@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "exception_stub.h"
#include "bcm.h"
.macro vector handler
.align 7
b \handler
.endm
.macro stub name, exception
.globl \name
\name:
mrs x0, esr_el1
mrs x1, spsr_el1
mov x2, x30 // lr
mrs x3, elr_el1
mrs x4, sp_el0
mov x5, sp
mrs x6, far_el1
str x6, [sp, #-16]!
stp x4, x5, [sp, #-16]!
stp x2, x3, [sp, #-16]!
stp x0, x1, [sp, #-16]!
mov x0, #\exception
mov x1, sp
b gilbraltar_exception_handler // never returns
.endm
.text
.align 11
.globl VectorTable
VectorTable:
// from current EL with sp_el0
vector synchronous_stub
vector IRQ_stub
vector FIQ_stub
vector system_error_stub
// from current EL with sp_elx, x != 0
vector synchronous_stub
vector IRQ_stub
vector FIQ_stub
vector system_error_stub
// from lower EL, target EL minus 1 is AArch64
vector HVC_stub
vector unexpected_stub
vector unexpected_stub
vector unexpected_stub
// from lower EL, target EL minus 1 is AArch32
vector unexpected_stub
vector unexpected_stub
vector unexpected_stub
vector unexpected_stub
// Abort stubs
stub unexpected_stub, EXCEPTION_UNEXPECTED
stub synchronous_stub, EXCEPTION_SYNCHRONOUS
stub system_error_stub,EXCEPTION_SYSTEM_ERROR
// IRQ stub
.globl IRQ_stub
IRQ_stub:
stp x29, x30, [sp, #-16]! // save x29, x30 onto stack
mrs x29, elr_el1 // save elr_el1, spsr_el1 onto stack
mrs x30, spsr_el1
stp x29, x30, [sp, #-16]!
msr DAIFClr, #1 // enable FIQ
#ifdef SAVE_VFP_REGS_ON_IRQ
stp q30, q31, [sp, #-32]! // save q0-q31 onto stack
stp q28, q29, [sp, #-32]!
stp q26, q27, [sp, #-32]!
stp q24, q25, [sp, #-32]!
stp q22, q23, [sp, #-32]!
stp q20, q21, [sp, #-32]!
stp q18, q19, [sp, #-32]!
stp q16, q17, [sp, #-32]!
stp q14, q15, [sp, #-32]!
stp q12, q13, [sp, #-32]!
stp q10, q11, [sp, #-32]!
stp q8, q9, [sp, #-32]!
stp q6, q7, [sp, #-32]!
stp q4, q5, [sp, #-32]!
stp q2, q3, [sp, #-32]!
stp q0, q1, [sp, #-32]!
#endif
stp x27, x28, [sp, #-16]! // save x0-x28 onto stack
stp x25, x26, [sp, #-16]!
stp x23, x24, [sp, #-16]!
stp x21, x22, [sp, #-16]!
stp x19, x20, [sp, #-16]!
stp x17, x18, [sp, #-16]!
stp x15, x16, [sp, #-16]!
stp x13, x14, [sp, #-16]!
stp x11, x12, [sp, #-16]!
stp x9, x10, [sp, #-16]!
stp x7, x8, [sp, #-16]!
stp x5, x6, [sp, #-16]!
stp x3, x4, [sp, #-16]!
stp x1, x2, [sp, #-16]!
str x0, [sp, #-16]!
ldr x0, =IRQ_return_address // store return address for profiling
str x29, [x0]
bl gilbraltar_interrupt_handler
ldr x0, [sp], #16 // restore x0-x28 from stack
ldp x1, x2, [sp], #16
ldp x3, x4, [sp], #16
ldp x5, x6, [sp], #16
ldp x7, x8, [sp], #16
ldp x9, x10, [sp], #16
ldp x11, x12, [sp], #16
ldp x13, x14, [sp], #16
ldp x15, x16, [sp], #16
ldp x17, x18, [sp], #16
ldp x19, x20, [sp], #16
ldp x21, x22, [sp], #16
ldp x23, x24, [sp], #16
ldp x25, x26, [sp], #16
ldp x27, x28, [sp], #16
#ifdef SAVE_VFP_REGS_ON_IRQ
ldp q0, q1, [sp], #32 // restore q0-q31 from stack
ldp q2, q3, [sp], #32
ldp q4, q5, [sp], #32
ldp q6, q7, [sp], #32
ldp q8, q9, [sp], #32
ldp q10, q11, [sp], #32
ldp q12, q13, [sp], #32
ldp q14, q15, [sp], #32
ldp q16, q17, [sp], #32
ldp q18, q19, [sp], #32
ldp q20, q21, [sp], #32
ldp q22, q23, [sp], #32
ldp q24, q25, [sp], #32
ldp q26, q27, [sp], #32
ldp q28, q29, [sp], #32
ldp q30, q31, [sp], #32
#endif
msr DAIFSet, #1 // disable FIQ
ldp x29, x30, [sp], #16 // restore elr_el1, spsr_el1 from stack
msr elr_el1, x29
msr spsr_el1, x30
ldp x29, x30, [sp], #16 // restore x29, x30 from stack
eret
// FIQ stub
.globl FIQ_stub
FIQ_stub:
#ifdef SAVE_VFP_REGS_ON_FIQ
stp q30, q31, [sp, #-32]!
stp q28, q29, [sp, #-32]!
stp q26, q27, [sp, #-32]!
stp q24, q25, [sp, #-32]!
stp q22, q23, [sp, #-32]!
stp q20, q21, [sp, #-32]!
stp q18, q19, [sp, #-32]!
stp q16, q17, [sp, #-32]!
stp q14, q15, [sp, #-32]!
stp q12, q13, [sp, #-32]!
stp q10, q11, [sp, #-32]!
stp q8, q9, [sp, #-32]!
stp q6, q7, [sp, #-32]!
stp q4, q5, [sp, #-32]!
stp q2, q3, [sp, #-32]!
stp q0, q1, [sp, #-32]!
#endif
stp x29, x30, [sp, #-16]!
stp x27, x28, [sp, #-16]!
stp x25, x26, [sp, #-16]!
stp x23, x24, [sp, #-16]!
stp x21, x22, [sp, #-16]!
stp x19, x20, [sp, #-16]!
stp x17, x18, [sp, #-16]!
stp x15, x16, [sp, #-16]!
stp x13, x14, [sp, #-16]!
stp x11, x12, [sp, #-16]!
stp x9, x10, [sp, #-16]!
stp x7, x8, [sp, #-16]!
stp x5, x6, [sp, #-16]!
stp x3, x4, [sp, #-16]!
stp x1, x2, [sp, #-16]!
str x0, [sp, #-16]!
ldr x2, =FIQ_data
ldr x1, [x2] // get FIQ_data.pHandler
cmp x1, #0 // is handler set?
b.eq 2f
ldr x0, [x2, #8] // get FIQ_data.pParam
blr x1 // call handler
1:
ldr x0, [sp], #16
ldp x1, x2, [sp], #16
ldp x3, x4, [sp], #16
ldp x5, x6, [sp], #16
ldp x7, x8, [sp], #16
ldp x9, x10, [sp], #16
ldp x11, x12, [sp], #16
ldp x13, x14, [sp], #16
ldp x15, x16, [sp], #16
ldp x17, x18, [sp], #16
ldp x19, x20, [sp], #16
ldp x21, x22, [sp], #16
ldp x23, x24, [sp], #16
ldp x25, x26, [sp], #16
ldp x27, x28, [sp], #16
ldp x29, x30, [sp], #16
#ifdef SAVE_VFP_REGS_ON_FIQ
ldp q0, q1, [sp], #32
ldp q2, q3, [sp], #32
ldp q4, q5, [sp], #32
ldp q6, q7, [sp], #32
ldp q8, q9, [sp], #32
ldp q10, q11, [sp], #32
ldp q12, q13, [sp], #32
ldp q14, q15, [sp], #32
ldp q16, q17, [sp], #32
ldp q18, q19, [sp], #32
ldp q20, q21, [sp], #32
ldp q22, q23, [sp], #32
ldp q24, q25, [sp], #32
ldp q26, q27, [sp], #32
ldp q28, q29, [sp], #32
ldp q30, q31, [sp], #32
#endif
eret
2:
ldr x1, =ARM_IC_FIQ_CONTROL // disable FIQ (if handler is not set)
mov w0, #0
str w0, [x1]
b 1b
// SMC stub
.globl SMC_stub
SMC_stub:
ldr x2, =SMC_stack
mov sp, x2
str x30, [sp, #-16]!
bl gilbraltar_secure_monitor_handler
ldr x30, [sp], #16
eret
// HVC stub
HVC_stub: // return to EL2h mode
mrs x0, spsr_el2
bic x0, x0, #0xF
mov x1, #9
orr x0, x0, x1
msr spsr_el2, x0
eret
.data
.align 3
.globl FIQ_data
FIQ_data:
.quad 0 // handler
.quad 0 // param
.word 0 // FIQ_number (unused)
.align 3
.globl IRQ_return_address
IRQ_return_address:
.quad 0
.bss
.align 4
.space 128
SMC_stack:

104
gen-headers.sh Executable file
View file

@ -0,0 +1,104 @@
#!/bin/sh
# Copyright (c) 2015-2021 Contributors as noted in the AUTHORS file
#
# This file is part of Gilbraltar, a bare-metal OS for RPi4.
#
# Permission to use, copy, modify, and/or distribute this software
# for any purpose with or without fee is hereby granted, provided
# that the above copyright notice and this permission notice appear
# in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# gen-headers.sh: Appropriate internal "C runtime" header files needed for the
# target toolchain from the origin compiler.
prog_NAME="$(basename $0)"
cleanup()
{
rm -f conftmp.c conftmp.d conftmp*.o
}
die()
{
echo "${prog_NAME}: ERROR: $@" 1>&2
cleanup
exit 1
}
cc_is_clang()
{
${CC} -dM -E - </dev/null | grep -Eq '^#define __clang__ 1$'
}
# Arguments: PATH, FILES...
# For the header FILES..., all of which must be relative to PATH, resolve their
# dependencies using the C preprocessor and output a list of FILES... plus all
# their unique dependencies, also relative to PATH.
cc_get_header_deps()
{
temp="$PWD/conftmp.d"
local path="$1"
shift
(
cd ${path} || return 1
${CC} -M "$@" >${temp} || return 1
sed -e 's!.*\.o:!!g' -e "s!${path}/!!g" ${temp} \
| tr ' \\' '\n' \
| sort \
| uniq
rm ${temp}
)
}
[ "$#" -ne 1 ] && die "Missing DESTDIR"
DESTDIR=$1
. ./Makeconf.sh || die "Can't find . ./Makeconf.sh"
mkdir -p ${DESTDIR} || die "mkdir failed"
if CC=${CONFIG_TARGET_CC} cc_is_clang; then
case ${CONFIG_HOST} in
# The BSDs don't ship some standard headers that we need in Clang's
# resource directory. Appropriate these from the host system.
FreeBSD|OpenBSD)
SRCDIR=/usr/include
SRCS="float.h stddef.h stdint.h stdbool.h stdarg.h"
DEPS="$(mktemp)"
CC=${CONFIG_TARGET_CC} cc_get_header_deps ${SRCDIR} ${SRCS} \
>${DEPS} || \
die "Failure getting dependencies of host headers"
# cpio will fail if CRT_INCDIR is below a symlink, so squash that
DESTDIR="$(readlink -f ${DESTDIR})"
Q=
[ "${CONFIG_HOST}" = "FreeBSD" ] && Q="--quiet"
(cd ${SRCDIR} && cpio ${Q} -Lpdm ${DESTDIR} <${DEPS}) || \
die "Failure copying host headers"
rm ${DEPS}
;;
# Other known Clang toolchains don't require anything special here as
# -nostdlibinc will pick up all we need from the compiler's resource
# directory.
*)
;;
esac
else
# For GCC there isn't an equivalent of -nostdlibinc, so we need to
# appropriate all of its internal headers.
SRCDIR="$(${CONFIG_TARGET_CC} -print-file-name=include)"
[ -d "${SRCDIR}" ] || die "Cannot determine gcc include directory"
cp -R "${SRCDIR}/." ${DESTDIR} || \
die "Failure copying host headers"
fi
echo "uint64_t tscclock_monotonic(void);" > ${DESTDIR}/rpi4.h
cleanup

52
gilbraltar.ld Normal file
View file

@ -0,0 +1,52 @@
ENTRY(_start)
PHDRS {
tdata PT_LOAD FLAGS(4);
}
SECTIONS
{
.init : {
*(.init)
}
.text : {
*(.text*)
_etext = .;
}
.rodata : {
*(.rodata*)
}
.ARM.exidx : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
}
.eh_frame : {
*(.eh_frame*)
}
.data : {
*(.data*)
}
.tdata : {
_stdata = .;
*(.tdata)
} :tdata
_edata = .;
.tbss : {
*(.tbss)
} : tbss
.bss : {
__bss_start = .;
*(.bss*)
*(COMMON)
_end = .;
}
}

23
include/bcm.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef __GILBRALTAR_BCM__
#define __GILBRALTAR_BCM__
#define ARM_IO_BASE 0x107c000000UL
// System timers
#define ARM_SYSTIMER_BASE (ARM_IO_BASE + 0x3000)
#define ARM_SYSTIMER_CS (ARM_SYSTIMER_BASE + 0x00)
#define ARM_SYSTIMER_CLO (ARM_SYSTIMER_BASE + 0x04)
#define ARM_SYSTIMER_CHI (ARM_SYSTIMER_BASE + 0x08)
// Interrupt Controller
#define ARM_IC_BASE (ARM_IO_BASE + 0xb000)
#define ARM_IC_FIQ_CONTROL (ARM_IO_BASE + 0x20c)
// General Purpose I/O #2
#define ARM_GPIO2_BASE (ARM_IO_BASE + 0x1517c00)
#define ARM_GPIO2_DATA0 (ARM_GPIO2_BASE + 0x04)
#define ARM_GPIO2_IODIR0 (ARM_GPIO2_BASE + 0x08)
#endif

6
include/clock.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef __GILBRALTAR_CLOCK__
#define __GILBRALTAR_CLOCK__
uint32_t gilbraltar_get_clock(uint32_t);
#endif

View file

@ -0,0 +1,32 @@
#ifndef __GILBRALTAR_EXCEPTION_HANDLER__
#define __GILBRALTAR_EXCEPTION_HANDLER__
#include <stdint.h>
struct frame {
uint32_t sp_irq;
uint32_t lr_irq;
uint32_t sp_fiq;
uint32_t lr_fiq;
uint32_t r0;
uint32_t r1;
uint32_t r2;
uint32_t r3;
uint32_t r4;
uint32_t r5;
uint32_t r6;
uint32_t r7;
uint32_t r8;
uint32_t r9;
uint32_t r10;
uint32_t r11;
uint32_t r12;
uint32_t sp;
uint32_t lr;
uint32_t spsr;
uint32_t pc;
};
void gilbraltar_exception_handler(uint64_t, struct frame *);
#endif

8
include/exception_stub.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef __GILBRALTAR_EXCEPTION_STUB__
#define __GILBRALTAR_EXCEPTION_STUB__
#define EXCEPTION_UNEXPECTED 0
#define EXCEPTION_SYNCHRONOUS 1
#define EXCEPTION_SYSTEM_ERROR 2
#endif

6
include/gpio.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef __GILBRALTAR_GPIO__
#define __GILBRALTAR_GPIO__
#include <bcm.h>
#endif

8
include/led.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef __GILBRALTAR_LED__
#define __GILBRALTAR_LED__
#include <stdbool.h>
void gilbraltar_led(bool);
#endif

19
include/log.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef __GILBRALTAR_LOG__
#define __GILBRALTAR_LOG__
#include <stdint.h>
#include <stdarg.h>
#include <stddef.h>
enum log_level {
ERROR= 0,
WARN,
INFO,
DEBUG
};
size_t gilbraltar_log(enum log_level, const char *fmt, ...)
__attribute__((format (printf, 2, 3)));
void gilbraltar_log_set(enum log_level);
#endif

21
include/mbox.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef __GILBRALTAR_MBOX__
#define __GILBRALTAR_MBOX__
#include <bcm.h>
#include <stdint.h>
#define MAILBOX_BASE (ARM_IO_BASE + 0x13880)
#define MAILBOX0_READ (MAILBOX_BASE + 0x00)
#define MAILBOX0_STATUS (MAILBOX_BASE + 0x18)
#define MAILBOX1_WRITE (MAILBOX_BASE + 0x20)
#define MAILBOX1_STATUS (MAILBOX_BASE + 0x38)
#define MAILBOX_STATUS_EMPTY 0x40000000
#define MAILBOX_STATUS_FULL 0x80000000
#define BCM_MAILBOX_PROP_OUT 8
uint32_t gilbraltar_mbox_write_read(uintptr_t data);
#endif

23
include/mem.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef __GILBRALTAR_MEM__
#define __GILBRALTAR_MEM__
#include <stddef.h>
#include <stdint.h>
static inline uint8_t read8(uintptr_t addr) {
return *(uint8_t volatile *)addr;
}
static inline void write8(uintptr_t addr, uint8_t v) {
*(uint8_t volatile *)addr = v;
}
static inline uint32_t read32(uintptr_t addr) {
return *(uint32_t volatile *)addr;
}
static inline void write32(uintptr_t addr, uint32_t v) {
*(uint32_t volatile *)addr = v;
}
#endif

57
include/memory_map.h Normal file
View file

@ -0,0 +1,57 @@
#ifndef __GILBRALTAR_MEMORY_MAP__
#define __GILBRALTAR_MEMORY_MAP__
#ifndef __GILBRALTAR_MEMORY_MAP__
#error Do not include memory_map.h file directly!
#endif
#ifndef MEGABYTE
#define MEGABYTE 0x100000
#endif
#ifndef GIGABYTE
#define GIGABYTE 0x40000000UL
#endif
#define CORES 4 // must be a power of 2
#define MEM_SIZE (512 * MEGABYTE) // default size
#define GPU_MEM_SIZE (64 * MEGABYTE) // set in config.txt
#define ARM_MEM_SIZE (MEM_SIZE - GPU_MEM_SIZE) // normally overwritten
#define PAGE_SIZE 0x10000 // page size used by us
#define PAGE_SHIFT 16
#define PAGE_MASK ~(0xffff)
#define EXCEPTION_STACK_SIZE 0x8000
#define PAGE_RESERVE (16 * MEGABYTE)
// Kernel
#define MEM_KERNEL_START 0x80000 // main code starts here
#define MEM_KERNEL_END (MEM_KERNEL_START + KERNEL_MAX_SIZE)
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE) // expands down
#define MEM_EXCEPTION_STACK \
(MEM_KERNEL_STACK + KERNEL_STACK_SIZE * (CORES - 1) + EXCEPTION_STACK_SIZE)
#define MEM_EXCEPTION_STACK_END \
(MEM_EXCEPTION_STACK + EXCEPTION_STACK_SIZE * (CORES - 1))
// Coherent Memory Region (4 MB)
#define MEM_COHERENT_REGION \
((MEM_EXCEPTION_STACK_END + 2 * MEGABYTE) & ~(MEGABYTE - 1))
// Heap
#define MEM_HEAP_START GIGABYTE
#define MEM_HEAP_END (8 * GIGABYTE - 1)
// IRQ & FIC stacks
#define MEM_ABORT_STACK (MEM_KERNEL_STACK + KERNEL_STACK_SIZE * (CORES - 1))
#define MEM_IRQ_STACK (MEM_EXCEPTION_STACK_END + EXCEPTION_STACK_SIZE)
#define MEM_FIQ_STACK \
(MEM_IRQ_STACK + EXCEPTION_STACK_SIZE * (CORES - 1) + EXCEPTION_STACK_SIZE)
// Page table
#define MEM_PAGE_TABLE1 \
(MEM_FIQ_STACK + EXCEPTION_STACK_SIZE * (CORES - 1))
#define PAGE_TABLE1_SIZE 0x4000
#define MEM_PAGE_TABLE1_END (MEM_PAGE_TABLE1 + PAGE_TABLE1_SIZE)
#endif

29
include/serial.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef __GILBRALTAR_SERIAL__
#define __GILBRALTAR_SERIAL__
#include <bcm.h>
#include <stdint.h>
#include <stddef.h>
#define ARM_UART0_BASE (ARM_IO_BASE + 0x1001000)
#define ARM_UART0_DR (ARM_UART0_BASE + 0x00)
#define ARM_UART0_FR (ARM_UART0_BASE + 0x18)
#define ARM_UART0_IBRD (ARM_UART0_BASE + 0x24)
#define ARM_UART0_FBRD (ARM_UART0_BASE + 0x28)
#define ARM_UART0_LCRH (ARM_UART0_BASE + 0x2c)
#define ARM_UART0_CR (ARM_UART0_BASE + 0x30)
#define ARM_UART0_IFLS (ARM_UART0_BASE + 0x34)
#define ARM_UART0_IMSC (ARM_UART0_BASE + 0x38)
#define ARM_UART0_RIS (ARM_UART0_BASE + 0x3c)
#define ARM_UART0_MIS (ARM_UART0_BASE + 0x40)
#define ARM_UART0_ICR (ARM_UART0_BASE + 0x44)
void gilbraltar_serial_init(void);
void gilbraltar_serial_send(uint8_t);
uint8_t gilbraltar_serial_recv(void);
void gilbraltar_serial_puts(const char *);
void gilbraltar_serial_putchar(int);
void gilbraltar_serial_write(const char *, size_t);
#endif

44
include/sysconfig.h Normal file
View file

@ -0,0 +1,44 @@
#ifndef __GILBRALTAR_SYSCONFIG__
#define __GILBRALTAR_SYSCONFIG__
#define MEGABYTE 0x100000
/* KERNEL_MAX_SIZE is the maximum allowed size of a built kernel image. If your
* kernel image contains big data areas it may be required to increase this
* value. This value must be a multiple of 16 KByte.
*/
#ifndef KERNEL_MAX_SIZE
#define KERNEL_MAX_SIZE (6 * MEGABYTE)
#endif
/* KERNEL_STACK_SIZE is the size of the stack set on startup for the main
* thread. This must be a multiple of 16 KByte.
*/
#ifndef KERNEL_STACK_SIZE
#define KERNEL_STACK_SIZE 0x20000
#endif
#define USE_PHYSICAL_COUNTER
#define SERIAL_GPIO_SELECT 14 // and 15
#if __GNUC__ >= 12
// GNU-C 12.x uses floating point registers for optimizations. This may occur
// anywhere in the code, even in IRQ and FIQ handlers.
#ifndef SAVE_VFP_REGS_ON_IRQ
#define SAVE_VFP_REGS_ON_IRQ
#endif
#ifndef SAVE_VFP_REGS_ON_FIQ
#define SAVE_VFP_REGS_ON_FIQ
#endif
#ifndef __FAST_MATH__
#define __FAST_MATH__
#endif
#endif
#include "memory_map.h"
#endif

10
include/tag.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef __GILBRALTAR_TAG__
#define __GILBRALTAR_TAG__
#define CODE_REQUEST 0x00000000
#define CODE_RESPONSE_SUCCESS 0x80000000
#define CODE_RESPONSE_FAILURE 0x80000001
#define PROPTAG_END 0x00000000
#endif

13
include/timer.h Normal file
View file

@ -0,0 +1,13 @@
#ifndef __GILBRALTAR_TIMER__
#define __GILBRALTAR_TIMER__
#include <bcm.h>
#include <stdint.h>
#define CLOCKHZ 1000000
void gilbraltar_delay_hot_loop(uint32_t);
void gilbraltar_delay_us(uint32_t);
void gilbraltar_delay_ms(uint32_t);
#endif

9
interrupt_handler.c Normal file
View file

@ -0,0 +1,9 @@
void gilbraltar_interrupt_handler(void) {
while (1)
__asm__ __volatile("wfi");
}
void gilbraltar_secure_monitor_handler(void) {
while (1)
__asm__ __volatile("wfi");
}

3
kernel.c Normal file
View file

@ -0,0 +1,3 @@
extern void main(void);
void gilbraltar_sysinit(void) { main(); }

14
led.c Normal file
View file

@ -0,0 +1,14 @@
#include <bcm.h>
#include <mem.h>
#include <stdbool.h>
void gilbraltar_led(bool s) {
uint32_t reg = read32(ARM_GPIO2_DATA0);
if (s)
reg |= 0x200; // set bit 9 to 1
else
reg &= ~0x200; // set bit 9 to 0
write32(ARM_GPIO2_DATA0, reg);
}

32
log.c Normal file
View file

@ -0,0 +1,32 @@
#include <serial.h>
#include <stdio.h>
#include <log.h>
static enum log_level actual_level = INFO;
size_t gilbraltar_log(enum log_level level, const char *fmt, ...) {
if (actual_level < level)
return 0;
va_list args;
size_t size;
char buffer[320];
va_start(args, fmt);
size = vsnprintf(buffer, sizeof buffer, fmt, args);
va_end(args);
if (size >= sizeof buffer) {
const char trunc[] = "(truncated)\n";
gilbraltar_serial_write(buffer, sizeof buffer - 1);
gilbraltar_serial_write(trunc, sizeof buffer - 1);
return (sizeof buffer - 1);
} else {
gilbraltar_serial_write(buffer, size);
return size;
}
}
void gilbraltar_log_set(enum log_level level) {
actual_level = level;
}

18
mbox.c Normal file
View file

@ -0,0 +1,18 @@
#include <mem.h>
#include <mbox.h>
uint32_t gilbraltar_mbox_write_read(uintptr_t dma) {
dma |= 0xc0000000U;
while (read32(MAILBOX1_STATUS) & MAILBOX_STATUS_FULL);
write32(MAILBOX1_WRITE, BCM_MAILBOX_PROP_OUT | dma);
uint32_t result;
do {
while (read32(MAILBOX0_STATUS) & MAILBOX_STATUS_EMPTY);
result = read32(MAILBOX0_READ);
} while ((result & 0xf) != BCM_MAILBOX_PROP_OUT);
return (result & ~0xf);
}

58
nolibc/GNUmakefile Normal file
View file

@ -0,0 +1,58 @@
ifeq ($(FREESTANDING_CFLAGS),)
$(error FREESTANDING_CFLAGS not set)
endif
all: libnolibc.a test-headers
.PHONY: all test-headers clean
clean:
$(RM) libnolibc.a *.o test-include/*.[co] test-include/sys/*.[co]
CC=cc
CFLAGS=-O2 -std=c99 -Wall -Wno-parentheses -Werror
CFLAGS+=$(FREESTANDING_CFLAGS)
OBJS=assert.o \
ctype.o \
dtoa.o \
errlist.o strerror_r.o \
memchr.o memcmp.o memcpy.o memmove.o memset.o \
strcmp.o strlen.o strnlen.o strtol.o strchr.o strchrnul.o strncpy.o stpncpy.o \
strstr.o strncmp.o puts.o \
stubs.o \
vfprintf.o vsnprintf.o snprintf.o fprintf.o printf.o \
sysconf.o \
mmap.o
SYSOBJS=sysdeps.o
dtoa.o: CFLAGS+=-fno-strict-aliasing
libnolibc.a: $(OBJS) $(SYSOBJS)
$(AR) rcs $@ $(OBJS) $(SYSOBJS)
# The following test ensures that each header file provided by nolibc is both
# self-contained and compile-tested. Note that headers in include/_freestanding
# are not intended to be included directly, thus are exempt from this check.
HEADERS=$(wildcard include/*.h include/sys/*.h)
# For each HEADER we want to test, produce test-include/HEADER.o. Note that
# HEADER will include subdirectories, if matched.
TEST_H_OBJS=$(patsubst %.h,test-%.o,$(HEADERS))
# For each HEADER we want to test, generate a C source file including only
# that HEADER. As above, HEADER may include subdirectories.
test-include/%.c: include/%.h | test-include/sys/
echo "#include \"../$<\"" >$@
.PRECIOUS: test-include/%.c
test-include/:
mkdir -p $@
test-include/sys/: test-include/
mkdir -p $@
test-headers: $(TEST_H_OBJS) | test-include/sys/

18
nolibc/assert.c Normal file
View file

@ -0,0 +1,18 @@
#include <stdlib.h>
#include <stdio.h>
/*
* These functions deliberately do not call printf() or malloc() in order to
* abort as quickly as possible without triggering further errors.
*/
void _assert_fail(const char *file, const char *line, const char *e)
{
puts(file);
puts(":");
puts(line);
puts(": Assertion `");
puts(e);
puts("' failed\n");
abort();
}

26
nolibc/ctype.c Normal file
View file

@ -0,0 +1,26 @@
#include <ctype.h>
int isalpha(int c)
{
return ((unsigned)c|32)-'a' < 26;
}
int isdigit(int c)
{
return (unsigned)c-'0' < 10;
}
int isprint(int c)
{
return (unsigned)c-0x20 < 0x5f;
}
int isspace(int c)
{
return c == ' ' || (unsigned)c-'\t' < 5;
}
int isupper(int c)
{
return (unsigned)c-'A' < 26;
}

6339
nolibc/dlmalloc.i Normal file

File diff suppressed because it is too large Load diff

6234
nolibc/dtoa.c Normal file

File diff suppressed because it is too large Load diff

64
nolibc/errlist.c Normal file
View file

@ -0,0 +1,64 @@
/* This code is mostly an extract from OpenBSD sources, keeping only the needed
* errno values
*
* NOTE: the list of errors must be kept in sync with errno.h */
/*
* Copyright (c) 1982, 1985, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <errno.h>
#include <assert.h>
const char *const sys_errlist[] = {
"Undefined error: 0", /* ENOERROR */
"No such file or directory", /* ENOENT */
"Interrupted system call", /* EINTR */
"Bad file descriptor", /* EBADF */
"Cannot allocate memory", /* ENOMEM */
"Device busy", /* EBUSY */
"Invalid argument", /* EINVAL */
"Too many open files", /* EMFILE */
"Broken pipe", /* EPIPE */
/* math software */
"Result too large", /* ERANGE */
/* non-blocking and interrupt i/o */
"Resource temporarily unavailable", /* EAGAIN */
/* ipc/network software -- operational errors */
"Connection reset by peer", /* ECONNRESET */
"Function not implemented", /* ENOSYS */
/* EOVERFLOW */
"Value too large to be stored in data type",
};
static_assert((sizeof sys_errlist/sizeof sys_errlist[0]) == NB_ERRORS,
"errlist.c and errno.h are out of sync");

12
nolibc/fprintf.c Normal file
View file

@ -0,0 +1,12 @@
#include <stdio.h>
#include <stdarg.h>
int fprintf(FILE *restrict f, const char *restrict fmt, ...)
{
int ret;
va_list ap;
va_start(ap, fmt);
ret = vfprintf(f, fmt, ap);
va_end(ap);
return ret;
}

View file

@ -0,0 +1,16 @@
#ifndef __FREESTANDING_BYTEORDER_H
#define __FREESTANDING_BYTEORDER_H
#if !defined(__BYTE_ORDER__)
#error C compiler does not define __BYTE_ORDER__
#endif
#if defined(BYTE_ORDER) || defined(LITTLE_ENDIAN) || defined(BIG_ENDIAN)
#error BYTE_ORDER, LITTLE_ENDIAN or BIG_ENDIAN must not be defined here
#endif
#define BYTE_ORDER __BYTE_ORDER__
#define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
#define BIG_ENDIAN __ORDER_BIG_ENDIAN__
#endif /* __FREESTANDING_BYTEORDER_H */

View file

@ -0,0 +1,13 @@
#ifndef __FREESTANDING_OVERRIDES_H
#define __FREESTANDING_OVERRIDES_H
#undef __FreeBSD__
#undef __OpenBSD__
#undef __gnu_linux__
#undef __linux
#undef __linux__
#undef linux
#define __ocaml_solo5__
#endif /* __FREESTANDING_OVERRIDES_H */

20
nolibc/include/assert.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef _ASSERT_H
#define _ASSERT_H
extern void _assert_fail(const char *, const char *, const char *)
__attribute__((noreturn));
#define _ASSERT_STR_EXPAND(y) #y
#define _ASSERT_STR(x) _ASSERT_STR_EXPAND(x)
#define assert(e) \
do { \
if (!(e)) \
_assert_fail(__FILE__, _ASSERT_STR(__LINE__), #e); \
} while (0)
#endif
#ifndef static_assert
# define static_assert _Static_assert
#endif

12
nolibc/include/ctype.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef _CTYPE_H
#define _CTYPE_H
int isalpha(int);
int isdigit(int);
int isprint(int);
int isspace(int);
int isupper(int);
int isalnum(int);
int tolower(int);
#endif

12
nolibc/include/dirent.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef _DIRENT_H
#define _DIRENT_H
typedef int DIR;
int closedir(DIR *);
DIR *opendir(const char *);
struct dirent {
char *d_name;
};
struct dirent *readdir(DIR *);
#endif

45
nolibc/include/endian.h Normal file
View file

@ -0,0 +1,45 @@
#ifndef _ENDIAN_H
#define _ENDIAN_H
#include <_gilbraltar/byteorder.h>
#include <stdint.h>
#define bswap16(x) __builtin_bswap16(x)
#define bswap32(x) __builtin_bswap32(x)
#define bswap64(x) __builtin_bswap64(x)
#if BYTE_ORDER == LITTLE_ENDIAN
#define htobe16(x) bswap16(x)
#define htobe32(x) bswap32(x)
#define htobe64(x) bswap64(x)
#define htole16(x) (uint16_t)(x)
#define htole32(x) (uint32_t)(x)
#define htole64(x) (uint64_t)(x)
#define be16toh(x) bswap16(x)
#define be32toh(x) bswap32(x)
#define be64toh(x) bswap64(x)
#define le16toh(x) (uint16_t)(x)
#define le32toh(x) (uint32_t)(x)
#define le64toh(x) (uint64_t)(x)
#else /* BYTE_ORDER != LITTLE_ENDIAN */
#define htobe16(x) (uint16_t)(x)
#define htobe32(x) (uint32_t)(x)
#define htobe64(x) (uint64_t)(x)
#define htole16(x) bswap16(x)
#define htole32(x) bswap32(x)
#define htole64(x) bswap64(x)
#define be16toh(x) (uint16_t)(x)
#define be32toh(x) (uint32_t)(x)
#define be64toh(x) (uint64_t)(x)
#define le16toh(x) bswap16(x)
#define le32toh(x) bswap32(x)
#define le64toh(x) bswap64(x)
#endif /* BYTE_ORDER == LITTLE_ENDIAN */
#endif /* _ENDIAN_H */

26
nolibc/include/errno.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef _ERRNO_H
#define _ERRNO_H
extern int errno;
/* This list of errors must be kept in sync with errlist.c */
#define ENOERROR 0 /* Actual error codes should be > 0 */
#define ENOENT 1
#define EINTR 2
#define EBADF 3
#define ENOMEM 4
#define EBUSY 5
#define EINVAL 6
#define EMFILE 7
#define EPIPE 8
#define ERANGE 9
#define EAGAIN 10
#define ECONNRESET 11
#define ENOSYS 12
#define EOVERFLOW 13
#define NB_ERRORS 14
#endif

18
nolibc/include/fcntl.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef _FCNTL_H
#define _FCNTL_H
int fcntl(int, int, ...);
int open(const char *, int, ...);
/* The following values are taken from:
* https://github.com/openbsd/src/blob/master/sys/sys/fcntl.h
*/
#define O_RDONLY 0x0000 /* open for reading only */
#define O_WRONLY 0x0001 /* open for writing only */
#define O_RDWR 0x0002 /* open for reading and writing */
#define O_APPEND 0x0008 /* set append mode */
#define O_CREAT 0x0200 /* create if nonexistent */
#define O_TRUNC 0x0400 /* truncate to zero length */
#define O_EXCL 0x0800 /* error if already exists */
#endif

View file

@ -0,0 +1,6 @@
#ifndef _FEATURES_H
#define _FEATURES_H
/* No features, isn't that nice? */
#endif

122
nolibc/include/inttypes.h Normal file
View file

@ -0,0 +1,122 @@
#ifndef _INTTYPES_H
#define _INTTYPES_H
#include <stdint.h>
#if !defined(UINTPTR_MAX) || !defined(UINT64_MAX)
#error UINTPTR_MAX and UINT64_MAX must be defined here
#endif
#if UINTPTR_MAX == UINT64_MAX
#define __PRI64 "l"
#define __PRIPTR "l"
#else
#define __PRI64 "ll"
#define __PRIPTR ""
#endif
#define PRId8 "d"
#define PRId16 "d"
#define PRId32 "d"
#define PRId64 __PRI64 "d"
#define PRIdLEAST8 "d"
#define PRIdLEAST16 "d"
#define PRIdLEAST32 "d"
#define PRIdLEAST64 __PRI64 "d"
#define PRIdFAST8 "d"
#define PRIdFAST16 "d"
#define PRIdFAST32 "d"
#define PRIdFAST64 __PRI64 "d"
#define PRIi8 "i"
#define PRIi16 "i"
#define PRIi32 "i"
#define PRIi64 __PRI64 "i"
#define PRIiLEAST8 "i"
#define PRIiLEAST16 "i"
#define PRIiLEAST32 "i"
#define PRIiLEAST64 __PRI64 "i"
#define PRIiFAST8 "i"
#define PRIiFAST16 "i"
#define PRIiFAST32 "i"
#define PRIiFAST64 __PRI64 "i"
#define PRIo8 "o"
#define PRIo16 "o"
#define PRIo32 "o"
#define PRIo64 __PRI64 "o"
#define PRIoLEAST8 "o"
#define PRIoLEAST16 "o"
#define PRIoLEAST32 "o"
#define PRIoLEAST64 __PRI64 "o"
#define PRIoFAST8 "o"
#define PRIoFAST16 "o"
#define PRIoFAST32 "o"
#define PRIoFAST64 __PRI64 "o"
#define PRIu8 "u"
#define PRIu16 "u"
#define PRIu32 "u"
#define PRIu64 __PRI64 "u"
#define PRIuLEAST8 "u"
#define PRIuLEAST16 "u"
#define PRIuLEAST32 "u"
#define PRIuLEAST64 __PRI64 "u"
#define PRIuFAST8 "u"
#define PRIuFAST16 "u"
#define PRIuFAST32 "u"
#define PRIuFAST64 __PRI64 "u"
#define PRIx8 "x"
#define PRIx16 "x"
#define PRIx32 "x"
#define PRIx64 __PRI64 "x"
#define PRIxLEAST8 "x"
#define PRIxLEAST16 "x"
#define PRIxLEAST32 "x"
#define PRIxLEAST64 __PRI64 "x"
#define PRIxFAST8 "x"
#define PRIxFAST16 "x"
#define PRIxFAST32 "x"
#define PRIxFAST64 __PRI64 "x"
#define PRIX8 "X"
#define PRIX16 "X"
#define PRIX32 "X"
#define PRIX64 __PRI64 "X"
#define PRIXLEAST8 "X"
#define PRIXLEAST16 "X"
#define PRIXLEAST32 "X"
#define PRIXLEAST64 __PRI64 "X"
#define PRIXFAST8 "X"
#define PRIXFAST16 "X"
#define PRIXFAST32 "X"
#define PRIXFAST64 __PRI64 "X"
#define PRIdMAX __PRI64 "d"
#define PRIiMAX __PRI64 "i"
#define PRIoMAX __PRI64 "o"
#define PRIuMAX __PRI64 "u"
#define PRIxMAX __PRI64 "x"
#define PRIXMAX __PRI64 "X"
#define PRIdPTR __PRIPTR "d"
#define PRIiPTR __PRIPTR "i"
#define PRIoPTR __PRIPTR "o"
#define PRIuPTR __PRIPTR "u"
#define PRIxPTR __PRIPTR "x"
#define PRIXPTR __PRIPTR "X"
#endif

23
nolibc/include/limits.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef _LIMITS_H
#define _LIMITS_H
#define INT_MAX 0x7fffffff
#define INT_MIN (-1-0x7fffffff)
#define UINT_MAX 0xffffffffU
#define SHRT_MAX 0x7fff
#define SHRT_MIN (-1-0x7fff)
#define USHRT_MAX 0xffff
#if defined(__x86_64__) || defined(__aarch64__)
#define LONG_MAX 0x7fffffffffffffffL
#define LLONG_MAX 0x7fffffffffffffffLL
#else
#error Unsupported architecture
#endif
#define LONG_MIN (-LONG_MAX-1)
#define LLONG_MIN (-LLONG_MAX-1)
#define ULONG_MAX (2UL*LONG_MAX+1)
#define NL_ARGMAX 9
#define UCHAR_MAX 255
#define CHAR_BIT 8
#endif

1
nolibc/include/math.h Normal file
View file

@ -0,0 +1 @@
#include <openlibm.h>

57
nolibc/include/pthread.h Normal file
View file

@ -0,0 +1,57 @@
#ifndef _PTHREAD_H
#define _PTHREAD_H
#include <stddef.h>
#include <signal.h>
typedef unsigned long int pthread_t;
typedef int cpu_set_t;
int pthread_getaffinity_np(pthread_t, size_t, cpu_set_t *);
pthread_t pthread_self(void);
typedef int pthread_attr_t;
int pthread_create(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *);
int pthread_join(pthread_t, void **);
int pthread_attr_init(pthread_attr_t *);
void pthread_cleanup_push(void (*)(void *), void *);
void pthread_cleanup_pop(int);
typedef int pthread_mutex_t;
typedef int pthread_cond_t;
int pthread_mutex_lock(pthread_mutex_t *);
int pthread_mutex_trylock(pthread_mutex_t *);
int pthread_mutex_unlock(pthread_mutex_t *);
#define PTHREAD_MUTEX_INITIALIZER 0
#define PTHREAD_COND_INITIALIZER 0
int pthread_sigmask(int, const sigset_t *, sigset_t *);
int pthread_detach(pthread_t);
int pthread_equal(pthread_t, pthread_t);
typedef int pthread_mutexattr_t;
int pthread_mutexattr_init(pthread_mutexattr_t *);
int pthread_mutexattr_settype(pthread_mutexattr_t *, int);
#define PTHREAD_MUTEX_ERRORCHECK 0
int pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *);
int pthread_mutexattr_destroy(pthread_mutexattr_t *);
int pthread_mutex_destroy(pthread_mutex_t *);
typedef int pthread_condattr_t;
int pthread_condattr_init(pthread_condattr_t *);
int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *);
int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *);
int pthread_cond_broadcast(pthread_cond_t *);
int pthread_cond_signal(pthread_cond_t *);
int pthread_cond_destroy(pthread_cond_t *);
#endif

8
nolibc/include/sched.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef _SCHED_H
#define _SCHED_H
typedef int cpu_set_t;
#define CPU_ZERO (x) 0
#endif

5
nolibc/include/setjmp.h Normal file
View file

@ -0,0 +1,5 @@
#include <signal.h>
void longjmp(int, int) __attribute__ ((__noreturn__));
#define setjmp(buf) 0

27
nolibc/include/signal.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef _SIGNAL_H
#define _SIGNAL_H
/*
* The following definitions are required not only to build the OCaml runtime
* but also the freestanding version of GMP used by Mirage.
* Note though that Solo5 does not implement signals, so we should not trigger a
* situation where these values are really used.
*/
typedef int jmp_buf;
int setjmp(jmp_buf);
void (*signal(int sig, void (*func)(int)))(int);
#define SIG_DFL 0
#define SIG_IGN 0
#define SIG_ERR 0
#define SIG_BLOCK 0
#define SIG_SETMASK 0
#define SIGFPE 1
int raise(int);
typedef int sigset_t;
int sigfillset(sigset_t *);
int sigwait(const sigset_t *restrict set, int *restrict sig);
#endif

42
nolibc/include/stdio.h Normal file
View file

@ -0,0 +1,42 @@
#ifndef _STDIO_H
#define _STDIO_H
#include <stdarg.h>
#include <stddef.h>
struct _FILE;
typedef struct _FILE {
size_t (*write)(struct _FILE *f, const char *, size_t);
char *wpos;
char *wend;
} FILE;
int fflush(FILE *);
int fprintf(FILE *, const char *, ...);
int printf(const char *, ...);
int rename(const char *, const char *);
extern FILE *stdout;
extern FILE *stderr;
int sscanf(const char *, const char *, ...);
int snprintf(char *, size_t, const char *, ...);
int vfprintf(FILE *, const char *, va_list);
int vsnprintf(char *, size_t, const char *, va_list);
/*
* The following definitions are not required by the OCaml runtime, but are
* needed to build the freestanding version of GMP used by Mirage.
*/
#define EOF (-1)
extern FILE *stdin;
size_t fread(void *, size_t, size_t, FILE *);
int getc(FILE *);
int ungetc(int, FILE *);
size_t fwrite(const void *, size_t, size_t, FILE *);
int fputc(int, FILE *);
int putc(int, FILE *);
int ferror(FILE *);
int fputs(const char *, FILE *);
FILE *fopen(const char *, const char *);
int fclose(FILE *);
int puts(const char *);
int putchar(int);
#endif

39
nolibc/include/stdlib.h Normal file
View file

@ -0,0 +1,39 @@
#ifndef _STDLIB_H
#define _STDLIB_H
#include <stddef.h>
void abort(void) __attribute__((noreturn));
void exit(int) __attribute__((noreturn));
void *malloc(size_t);
void free(void *);
void *calloc(size_t, size_t);
void *realloc(void *, size_t);
int posix_memalign(void **, size_t, size_t);
struct mallinfo {
size_t arena; /* non-mmapped space allocated from system */
size_t ordblks; /* number of free chunks */
size_t smblks; /* always 0 */
size_t hblks; /* always 0 */
size_t hblkhd; /* space in mmapped regions */
size_t usmblks; /* maximum total allocated space */
size_t fsmblks; /* always 0 */
size_t uordblks; /* total allocated space */
size_t fordblks; /* total free space */
size_t keepcost; /* releasable (via malloc_trim) space */
};
struct mallinfo mallinfo(void);
char *getenv(const char *);
char *secure_getenv(const char *);
int system(const char *);
double strtod(const char *, char **);
long strtol(const char *, char **, int);
void qsort(void *base, size_t nmemb, size_t size,
int (*compare)(const void *, const void *));
char *mktemp(char *);
#endif

29
nolibc/include/string.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef _STRING_H
#define _STRING_H
#include <stddef.h>
int memcmp(const void *, const void *, size_t);
void *memchr(const void *, int, size_t);
void *memcpy(void *, const void *, size_t);
void *memmove(void *, const void *, size_t);
void *memset(void *, int, size_t);
int strcmp(const char *, const char *);
size_t strlen(const char *);
size_t strnlen(const char *, size_t);
char *strerror(int);
int strerror_r(int errnum, char *buf, size_t buflen);
/*
* The following definitions are not required by the OCaml runtime, but are
* needed to build the freestanding version of GMP used by Mirage.
*/
char *strncpy(char *, const char *, size_t);
char *strcpy(char *, const char *);
char *strchr(const char *, int);
char *strstr(const char *, const char *);
/*
* The following definitions are required for the OCaml bytecode runtime.
*/
int strncmp(const char*, const char*, size_t);
#endif

View file

@ -0,0 +1,4 @@
#ifndef _SYS_IOCTL_H
#define _SYS_IOCTL_H
#endif

26
nolibc/include/sys/mman.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef _MMAP_H
#define _MMAP_H
#include <stddef.h>
#include <sys/types.h>
void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
#define PROT_NONE 0
#define PROT_READ 1
#define PROT_WRITE 2
#define MAP_SHARED 0x01
#define MAP_PRIVATE 0x02
#define MAP_FIXED 0x10
#define MAP_ANONYMOUS 0x20
#define MAP_ANON MAP_ANONYMOUS
// the MAP_FAILED comply with the mmap manual page
#define MAP_FAILED (void*)-1
#define OCAML_SOLO5_PAGESIZE (1 << 12)
int munmap(void *addr, size_t len);
#endif

View file

@ -0,0 +1,6 @@
#ifndef _SYS_PARAM_H
#define _SYS_PARAM_H
#include <_solo5/byteorder.h>
#endif /* _SYS_PARAM_H */

20
nolibc/include/sys/stat.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef _SYS_STAT_H
#define _SYS_STAT_H
#include <sys/types.h>
struct stat {
int st_mode;
int st_size;
};
#define S_IFDIR 0
#define S_IFMT 0
#define S_IFREG 0
#define S_ISREG(x) (0)
#define S_IRUSR 0
#define S_IWUSR 0
int stat(const char *, struct stat *);
int mkdir(const char *, mode_t);
int fstat(int, struct stat *);
#endif

16
nolibc/include/sys/time.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef _SYS_TIME_H
#define _SYS_TIME_H
typedef long time_t;
typedef long suseconds_t;
struct timeval {
time_t tv_sec;
suseconds_t tv_usec;
};
struct timezone {
int tz_minuteswest;
int tz_dsttime;
};
int gettimeofday(struct timeval *tv, struct timezone *tz);
#endif

View file

@ -0,0 +1,13 @@
#ifndef _SYS_TIMES_H
#define _SYS_TIMES_H
typedef int clock_t;
struct tms {
clock_t tms_utime;
clock_t tms_stime;
clock_t tms_cutime;
clock_t tms_cstime;
};
clock_t times(struct tms *buf);
#endif

View file

@ -0,0 +1,13 @@
#ifndef _SYS_TYPES_H
#define _SYS_TYPES_H
#include <stddef.h>
#include <stdint.h>
typedef int pid_t;
typedef int off_t;
typedef int ssize_t;
typedef int mode_t;
typedef int useconds_t;
#endif

View file

@ -0,0 +1,4 @@
#ifndef _SYS_WAIT_H
#define _SYS_WAIT_H
#endif

1
nolibc/include/time.h Symbolic link
View file

@ -0,0 +1 @@
sys/time.h

26
nolibc/include/unistd.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef _UNISTD_H
#define _UNISTD_H
#include <sys/types.h>
int chdir(const char *);
int close(int);
char *getcwd(char *, size_t);
pid_t getpid(void);
pid_t getppid(void);
int isatty(int);
off_t lseek(int, off_t, int); /* SEEK_ */
ssize_t read(int, void *, size_t);
ssize_t write(int, const void *, size_t);
ssize_t readlink(const char *, char *, size_t);
int unlink(const char *);
int rmdir(const char *);
int usleep(useconds_t);
int ftruncate(int, off_t);
long sysconf(int);
int execv(const char *, char *const []);
#define _SC_PAGESIZE 1
#define _SC_PAGE_SIZE _SC_PAGESIZE
#endif

23
nolibc/memchr.c Normal file
View file

@ -0,0 +1,23 @@
#include <string.h>
#include <stdint.h>
#include <limits.h>
#define SS (sizeof(size_t))
#define ALIGN (sizeof(size_t)-1)
#define ONES ((size_t)-1/UCHAR_MAX)
#define HIGHS (ONES * (UCHAR_MAX/2+1))
#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
void *memchr(const void *src, int c, size_t n)
{
const unsigned char *s = src;
c = (unsigned char)c;
for (; ((uintptr_t)s & ALIGN) && n && *s != c; s++, n--);
if (n && *s != c) {
const size_t *w;
size_t k = ONES * c;
for (w = (const void *)s; n>=SS && !HASZERO(*w^k); w++, n-=SS);
for (s = (const void *)w; n && *s != c; s++, n--);
}
return n ? (void *)s : 0;
}

8
nolibc/memcmp.c Normal file
View file

@ -0,0 +1,8 @@
#include <string.h>
int memcmp(const void *vl, const void *vr, size_t n)
{
const unsigned char *l=vl, *r=vr;
for (; n && *l == *r; n--, l++, r++);
return n ? *l-*r : 0;
}

124
nolibc/memcpy.c Normal file
View file

@ -0,0 +1,124 @@
#include <string.h>
#include <stdint.h>
#include <endian.h>
void *memcpy(void *restrict dest, const void *restrict src, size_t n)
{
unsigned char *d = dest;
const unsigned char *s = src;
#ifdef __GNUC__
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define LS >>
#define RS <<
#else
#define LS <<
#define RS >>
#endif
typedef uint32_t __attribute__((__may_alias__)) u32;
uint32_t w, x;
for (; (uintptr_t)s % 4 && n; n--) *d++ = *s++;
if ((uintptr_t)d % 4 == 0) {
for (; n>=16; s+=16, d+=16, n-=16) {
*(u32 *)(d+0) = *(u32 *)(s+0);
*(u32 *)(d+4) = *(u32 *)(s+4);
*(u32 *)(d+8) = *(u32 *)(s+8);
*(u32 *)(d+12) = *(u32 *)(s+12);
}
if (n&8) {
*(u32 *)(d+0) = *(u32 *)(s+0);
*(u32 *)(d+4) = *(u32 *)(s+4);
d += 8; s += 8;
}
if (n&4) {
*(u32 *)(d+0) = *(u32 *)(s+0);
d += 4; s += 4;
}
if (n&2) {
*d++ = *s++; *d++ = *s++;
}
if (n&1) {
*d = *s;
}
return dest;
}
if (n >= 32) switch ((uintptr_t)d % 4) {
case 1:
w = *(u32 *)s;
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
n -= 3;
for (; n>=17; s+=16, d+=16, n-=16) {
x = *(u32 *)(s+1);
*(u32 *)(d+0) = (w LS 24) | (x RS 8);
w = *(u32 *)(s+5);
*(u32 *)(d+4) = (x LS 24) | (w RS 8);
x = *(u32 *)(s+9);
*(u32 *)(d+8) = (w LS 24) | (x RS 8);
w = *(u32 *)(s+13);
*(u32 *)(d+12) = (x LS 24) | (w RS 8);
}
break;
case 2:
w = *(u32 *)s;
*d++ = *s++;
*d++ = *s++;
n -= 2;
for (; n>=18; s+=16, d+=16, n-=16) {
x = *(u32 *)(s+2);
*(u32 *)(d+0) = (w LS 16) | (x RS 16);
w = *(u32 *)(s+6);
*(u32 *)(d+4) = (x LS 16) | (w RS 16);
x = *(u32 *)(s+10);
*(u32 *)(d+8) = (w LS 16) | (x RS 16);
w = *(u32 *)(s+14);
*(u32 *)(d+12) = (x LS 16) | (w RS 16);
}
break;
case 3:
w = *(u32 *)s;
*d++ = *s++;
n -= 1;
for (; n>=19; s+=16, d+=16, n-=16) {
x = *(u32 *)(s+3);
*(u32 *)(d+0) = (w LS 8) | (x RS 24);
w = *(u32 *)(s+7);
*(u32 *)(d+4) = (x LS 8) | (w RS 24);
x = *(u32 *)(s+11);
*(u32 *)(d+8) = (w LS 8) | (x RS 24);
w = *(u32 *)(s+15);
*(u32 *)(d+12) = (x LS 8) | (w RS 24);
}
break;
}
if (n&16) {
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
}
if (n&8) {
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
}
if (n&4) {
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
}
if (n&2) {
*d++ = *s++; *d++ = *s++;
}
if (n&1) {
*d = *s;
}
return dest;
#endif
for (; n; n--) *d++ = *s++;
return dest;
}

36
nolibc/memmove.c Normal file
View file

@ -0,0 +1,36 @@
#include <string.h>
#include <stdint.h>
#define WT size_t
#define WS (sizeof(WT))
void *memmove(void *dest, const void *src, size_t n)
{
char *d = dest;
const char *s = src;
if (d==s) return d;
if (s+n <= d || d+n <= s) return memcpy(d, s, n);
if (d<s) {
if ((uintptr_t)s % WS == (uintptr_t)d % WS) {
while ((uintptr_t)d % WS) {
if (!n--) return dest;
*d++ = *s++;
}
for (; n>=WS; n-=WS, d+=WS, s+=WS) *(WT *)d = *(WT *)s;
}
for (; n; n--) *d++ = *s++;
} else {
if ((uintptr_t)s % WS == (uintptr_t)d % WS) {
while ((uintptr_t)(d+n) % WS) {
if (!n--) return dest;
d[n] = s[n];
}
while (n>=WS) n-=WS, *(WT *)(d+n) = *(WT *)(s+n);
}
while (n) n--, d[n] = s[n];
}
return dest;
}

86
nolibc/memset.c Normal file
View file

@ -0,0 +1,86 @@
#include <string.h>
#include <stdint.h>
void *memset(void *dest, int c, size_t n)
{
unsigned char *s = dest;
size_t k;
/* Fill head and tail with minimal branching. Each
* conditional ensures that all the subsequently used
* offsets are well-defined and in the dest region. */
if (!n) return dest;
s[0] = s[n-1] = c;
if (n <= 2) return dest;
s[1] = s[n-2] = c;
s[2] = s[n-3] = c;
if (n <= 6) return dest;
s[3] = s[n-4] = c;
if (n <= 8) return dest;
/* Advance pointer to align it at a 4-byte boundary,
* and truncate n to a multiple of 4. The previous code
* already took care of any head/tail that get cut off
* by the alignment. */
k = -(uintptr_t)s & 3;
s += k;
n -= k;
n &= -4;
#ifdef __GNUC__
typedef uint32_t __attribute__((__may_alias__)) u32;
typedef uint64_t __attribute__((__may_alias__)) u64;
u32 c32 = ((u32)-1)/255 * (unsigned char)c;
/* In preparation to copy 32 bytes at a time, aligned on
* an 8-byte bounary, fill head/tail up to 28 bytes each.
* As in the initial byte-based head/tail fill, each
* conditional below ensures that the subsequent offsets
* are valid (e.g. !(n<=24) implies n>=28). */
*(u32 *)(s+0) = c32;
*(u32 *)(s+n-4) = c32;
if (n <= 8) return dest;
*(u32 *)(s+4) = c32;
*(u32 *)(s+8) = c32;
*(u32 *)(s+n-12) = c32;
*(u32 *)(s+n-8) = c32;
if (n <= 24) return dest;
*(u32 *)(s+12) = c32;
*(u32 *)(s+16) = c32;
*(u32 *)(s+20) = c32;
*(u32 *)(s+24) = c32;
*(u32 *)(s+n-28) = c32;
*(u32 *)(s+n-24) = c32;
*(u32 *)(s+n-20) = c32;
*(u32 *)(s+n-16) = c32;
/* Align to a multiple of 8 so we can fill 64 bits at a time,
* and avoid writing the same bytes twice as much as is
* practical without introducing additional branching. */
k = 24 + ((uintptr_t)s & 4);
s += k;
n -= k;
/* If this loop is reached, 28 tail bytes have already been
* filled, so any remainder when n drops below 32 can be
* safely ignored. */
u64 c64 = c32 | ((u64)c32 << 32);
for (; n >= 32; n-=32, s+=32) {
*(u64 *)(s+0) = c64;
*(u64 *)(s+8) = c64;
*(u64 *)(s+16) = c64;
*(u64 *)(s+24) = c64;
}
#else
/* Pure C fallback with no aliasing violations. */
for (; n; n--, s++) *s = c;
#endif
return dest;
}

77
nolibc/mmap.c Normal file
View file

@ -0,0 +1,77 @@
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/mman.h>
void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off) {
/* man page for mmap says:
* If addr is NULL, then the kernel chooses the (page-aligned) address at
* which to create the mapping; this is the most portable method of creating a
* new mapping.
*
* For our purpose (Solo5 & OCaml), OCaml might use a NULL addr and force us to
* use posix_memalign.
* OCaml calls mmap with a non-null address and with the MAP_FIXED flag only
* on already reserved memory to commit or decommit that memory block, ie to
* set its protection to PROT_READ|PROT_WRITE or to PROT_NONE, in the
* caml_mem_commit and caml_mem_decommit functions.
* So we accept this particular case without allocating memory that would leak
* since the OCaml code base simply ignores the returned value (as MAP_FIXED
* enforces the returned value to be either addr or MAP_FAILED).
*
* The OCaml runtime uses [mmap()] only to allocate memory, so only
* [fildes == 1] is handled.
*/
(void)prot; // unused argument
if (fildes != -1) {
printf("mmap: file descriptor is unsupported.\n");
abort();
}
if (!(flags & MAP_ANONYMOUS) || off != 0) {
printf("mmap: only MAP_ANONYMOUS (and offset is 0) is supported.\n");
abort();
}
void *ptr = NULL;
if (addr == NULL) {
/* posix_memalign doesn't modify ptr on error: ptr will still be NULL and
* so we will return MAP_FAILED with no need to check explicitly the value
* returned by posix_memalign */
posix_memalign(&ptr, OCAML_SOLO5_PAGESIZE, len);
} else {
if ((flags & MAP_FIXED) != 0) {
/* Case where mmap is called to commit or decommit already reserved
* memory. Since we ignore prot, we can simply let it go through */
return addr;
} else {
/* We cannot handle this case */
errno = EINVAL;
}
}
if (ptr == NULL) {
return MAP_FAILED;
} else {
return ptr;
}
}
int munmap(void *addr, size_t length)
{
(void)length; // unused argument
/* man page for munmap says:
* The address addr must be a multiple of the page size (but length need not be).
*/
if ((uintptr_t)addr & OCAML_SOLO5_PAGESIZE != 0) {
errno = EINVAL;
return -1;
}
free(addr);
return 0;
}

12
nolibc/printf.c Normal file
View file

@ -0,0 +1,12 @@
#include <stdio.h>
#include <stdarg.h>
int printf(const char *restrict fmt, ...)
{
int ret;
va_list ap;
va_start(ap, fmt);
ret = vfprintf(stdout, fmt, ap);
va_end(ap);
return ret;
}

17
nolibc/puts.c Normal file
View file

@ -0,0 +1,17 @@
#include <string.h>
#include <unistd.h>
extern void solo5_console_write(const char *, size_t);
int puts(const char *s)
{
size_t len = strlen(s);
solo5_console_write(s, len);
return (int)(len); // We should never have a string length above MAX_INT, do we?
}
int putchar(int chr)
{
solo5_console_write((char *) &chr, 1);
return (1);
}

13
nolibc/snprintf.c Normal file
View file

@ -0,0 +1,13 @@
#include <stdio.h>
#include <stdarg.h>
int snprintf(char *restrict s, size_t n, const char *restrict fmt, ...)
{
int ret;
va_list ap;
va_start(ap, fmt);
ret = vsnprintf(s, n, fmt, ap);
va_end(ap);
return ret;
}

27
nolibc/stpncpy.c Normal file
View file

@ -0,0 +1,27 @@
#include <string.h>
#include <stdint.h>
#include <limits.h>
#define ALIGN (sizeof(size_t)-1)
#define ONES ((size_t)-1/UCHAR_MAX)
#define HIGHS (ONES * (UCHAR_MAX/2+1))
#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
char *__stpncpy(char *restrict d, const char *restrict s, size_t n)
{
size_t *wd;
const size_t *ws;
if (((uintptr_t)s & ALIGN) == ((uintptr_t)d & ALIGN)) {
for (; ((uintptr_t)s & ALIGN) && n && (*d=*s); n--, s++, d++);
if (!n || !*s) goto tail;
wd=(void *)d; ws=(const void *)s;
for (; n>=sizeof(size_t) && !HASZERO(*ws);
n-=sizeof(size_t), ws++, wd++) *wd = *ws;
d=(void *)wd; s=(const void *)ws;
}
for (; n && (*d=*s); n--, s++, d++);
tail:
memset(d, 0, n);
return d;
}

9
nolibc/strchr.c Normal file
View file

@ -0,0 +1,9 @@
#include <string.h>
char *__strchrnul(const char *, int);
char *strchr(const char *s, int c)
{
char *r = __strchrnul(s, c);
return *(unsigned char *)r == (unsigned char)c ? r : 0;
}

23
nolibc/strchrnul.c Normal file
View file

@ -0,0 +1,23 @@
#include <string.h>
#include <stdint.h>
#include <limits.h>
#define ALIGN (sizeof(size_t))
#define ONES ((size_t)-1/UCHAR_MAX)
#define HIGHS (ONES * (UCHAR_MAX/2+1))
#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
char *__strchrnul(const char *s, int c)
{
size_t *w, k;
c = (unsigned char)c;
if (!c) return (char *)s + strlen(s);
for (; (uintptr_t)s % ALIGN; s++)
if (!*s || *(unsigned char *)s == c) return (char *)s;
k = ONES * c;
for (w = (void *)s; !HASZERO(*w) && !HASZERO(*w^k); w++);
for (s = (void *)w; *s && *(unsigned char *)s != c; s++);
return (char *)s;
}

7
nolibc/strcmp.c Normal file
View file

@ -0,0 +1,7 @@
#include <string.h>
int strcmp(const char *l, const char *r)
{
for (; *l==*r && *l; l++, r++);
return *(unsigned char *)l - *(unsigned char *)r;
}

19
nolibc/strerror_r.c Normal file
View file

@ -0,0 +1,19 @@
#include <errno.h>
#include <stdio.h>
#include <string.h>
extern const char *const sys_errlist[];
int strerror_r(int num, char *buf, size_t buflen)
{
if (num < 0 || num >= NB_ERRORS) {
errno = EINVAL;
return EINVAL;
}
if (snprintf(buf, buflen, "%s", sys_errlist[num]) >= (int)buflen) {
errno = ERANGE;
return ERANGE;
}
return 0;
}

18
nolibc/strlen.c Normal file
View file

@ -0,0 +1,18 @@
#include <string.h>
#include <stdint.h>
#include <limits.h>
#define ALIGN (sizeof(size_t))
#define ONES ((size_t)-1/UCHAR_MAX)
#define HIGHS (ONES * (UCHAR_MAX/2+1))
#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
size_t strlen(const char *s)
{
const char *a = s;
const size_t *w;
for (; (uintptr_t)s % ALIGN; s++) if (!*s) return s-a;
for (w = (const void *)s; !HASZERO(*w); w++);
for (s = (const void *)w; *s; s++);
return s-a;
}

9
nolibc/strncmp.c Normal file
View file

@ -0,0 +1,9 @@
#include <string.h>
int strncmp(const char *_l, const char *_r, size_t n)
{
const unsigned char *l=(void *)_l, *r=(void *)_r;
if (!n--) return 0;
for (; *l && *r && n && *l == *r ; l++, r++, n--);
return *l - *r;
}

9
nolibc/strncpy.c Normal file
View file

@ -0,0 +1,9 @@
#include <string.h>
char *__stpncpy(char *, const char *, size_t);
char *strncpy(char *restrict d, const char *restrict s, size_t n)
{
__stpncpy(d, s, n);
return d;
}

20
nolibc/strnlen.c Normal file
View file

@ -0,0 +1,20 @@
#include <string.h>
#include <stdint.h>
#include <limits.h>
#define ALIGN (sizeof(size_t))
#define ONES ((size_t)-1/UCHAR_MAX)
#define HIGHS (ONES * (UCHAR_MAX/2+1))
#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
size_t strnlen(const char *s, size_t maxlen)
{
const char *a = s;
const size_t *w;
size_t over = maxlen;
for (; ((uintptr_t)s % ALIGN) && over; s++, over--) if (!*s) return s-a;
for (w = (const void *)s; over>ALIGN && !HASZERO(*w); w++,over-=ALIGN);
for (s = (const void *)w; *s && over; s++, over--);
if (over) return s-a;
else return maxlen;
}

155
nolibc/strstr.c Normal file
View file

@ -0,0 +1,155 @@
#include <string.h>
#include <stdint.h>
static char *twobyte_strstr(const unsigned char *h, const unsigned char *n)
{
uint16_t nw = n[0]<<8 | n[1], hw = h[0]<<8 | h[1];
for (h++; *h && hw != nw; hw = hw<<8 | *++h);
return *h ? (char *)h-1 : 0;
}
static char *threebyte_strstr(const unsigned char *h, const unsigned char *n)
{
uint32_t nw = n[0]<<24 | n[1]<<16 | n[2]<<8;
uint32_t hw = h[0]<<24 | h[1]<<16 | h[2]<<8;
for (h+=2; *h && hw != nw; hw = (hw|*++h)<<8);
return *h ? (char *)h-2 : 0;
}
static char *fourbyte_strstr(const unsigned char *h, const unsigned char *n)
{
uint32_t nw = n[0]<<24 | n[1]<<16 | n[2]<<8 | n[3];
uint32_t hw = h[0]<<24 | h[1]<<16 | h[2]<<8 | h[3];
for (h+=3; *h && hw != nw; hw = hw<<8 | *++h);
return *h ? (char *)h-3 : 0;
}
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)<(b)?(a):(b))
#define BITOP(a,b,op) \
((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a))))
static char *twoway_strstr(const unsigned char *h, const unsigned char *n)
{
const unsigned char *z;
size_t l, ip, jp, k, p, ms, p0, mem, mem0;
size_t byteset[32 / sizeof(size_t)] = { 0 };
size_t shift[256];
/* Computing length of needle and fill shift table */
for (l=0; n[l] && h[l]; l++)
BITOP(byteset, n[l], |=), shift[n[l]] = l+1;
if (n[l]) return 0; /* hit the end of h */
/* Compute maximal suffix */
ip = -1; jp = 0; k = p = 1;
while (jp+k<l) {
if (n[ip+k] == n[jp+k]) {
if (k == p) {
jp += p;
k = 1;
} else k++;
} else if (n[ip+k] > n[jp+k]) {
jp += k;
k = 1;
p = jp - ip;
} else {
ip = jp++;
k = p = 1;
}
}
ms = ip;
p0 = p;
/* And with the opposite comparison */
ip = -1; jp = 0; k = p = 1;
while (jp+k<l) {
if (n[ip+k] == n[jp+k]) {
if (k == p) {
jp += p;
k = 1;
} else k++;
} else if (n[ip+k] < n[jp+k]) {
jp += k;
k = 1;
p = jp - ip;
} else {
ip = jp++;
k = p = 1;
}
}
if (ip+1 > ms+1) ms = ip;
else p = p0;
/* Periodic needle? */
if (memcmp(n, n+p, ms+1)) {
mem0 = 0;
p = MAX(ms, l-ms-1) + 1;
} else mem0 = l-p;
mem = 0;
/* Initialize incremental end-of-haystack pointer */
z = h;
/* Search loop */
for (;;) {
/* Update incremental end-of-haystack pointer */
if (z-h < l) {
/* Fast estimate for MIN(l,63) */
size_t grow = l | 63;
const unsigned char *z2 = memchr(z, 0, grow);
if (z2) {
z = z2;
if (z-h < l) return 0;
} else z += grow;
}
/* Check last byte first; advance by shift on mismatch */
if (BITOP(byteset, h[l-1], &)) {
k = l-shift[h[l-1]];
//printf("adv by %zu (on %c) at [%s] (%zu;l=%zu)\n", k, h[l-1], h, shift[h[l-1]], l);
if (k) {
if (mem0 && mem && k < p) k = l-p;
h += k;
mem = 0;
continue;
}
} else {
h += l;
mem = 0;
continue;
}
/* Compare right half */
for (k=MAX(ms+1,mem); n[k] && n[k] == h[k]; k++);
if (n[k]) {
h += k-ms;
mem = 0;
continue;
}
/* Compare left half */
for (k=ms+1; k>mem && n[k-1] == h[k-1]; k--);
if (k <= mem) return (char *)h;
h += p;
mem = mem0;
}
}
char *strstr(const char *h, const char *n)
{
/* Return immediately on empty needle */
if (!n[0]) return (char *)h;
/* Use faster algorithms for short needles */
h = strchr(h, *n);
if (!h || !n[1]) return (char *)h;
if (!h[1]) return 0;
if (!n[2]) return twobyte_strstr((void *)h, (void *)n);
if (!h[2]) return 0;
if (!n[3]) return threebyte_strstr((void *)h, (void *)n);
if (!h[3]) return 0;
if (!n[4]) return fourbyte_strstr((void *)h, (void *)n);
return twoway_strstr((void *)h, (void *)n);
}

150
nolibc/strtol.c Normal file
View file

@ -0,0 +1,150 @@
/* $OpenBSD: strtol.c,v 1.11 2015/09/13 08:31:48 guenther Exp $ */
/*
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
/*
* Convert a string to a long integer.
*
* Ignores `locale' stuff. Assumes that the upper and lower case
* alphabets and digits are each contiguous.
*/
long
strtol(const char *nptr, char **endptr, int base)
{
const char *s;
long acc, cutoff;
int c;
int neg, any, cutlim;
/*
* Ensure that base is between 2 and 36 inclusive, or the special
* value of 0.
*/
if (base < 0 || base == 1 || base > 36) {
if (endptr != 0)
*endptr = (char *)nptr;
errno = EINVAL;
return 0;
}
/*
* Skip white space and pick up leading +/- sign if any.
* If base is 0, allow 0x for hex and 0 for octal, else
* assume decimal; if base is already 16, allow 0x.
*/
s = nptr;
do {
c = (unsigned char) *s++;
} while (isspace(c));
if (c == '-') {
neg = 1;
c = *s++;
} else {
neg = 0;
if (c == '+')
c = *s++;
}
if ((base == 0 || base == 16) &&
c == '0' && (*s == 'x' || *s == 'X')) {
c = s[1];
s += 2;
base = 16;
}
if (base == 0)
base = c == '0' ? 8 : 10;
/*
* Compute the cutoff value between legal numbers and illegal
* numbers. That is the largest legal value, divided by the
* base. An input number that is greater than this value, if
* followed by a legal input character, is too big. One that
* is equal to this value may be valid or not; the limit
* between valid and invalid numbers is then based on the last
* digit. For instance, if the range for longs is
* [-2147483648..2147483647] and the input base is 10,
* cutoff will be set to 214748364 and cutlim to either
* 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
* a value > 214748364, or equal but the next digit is > 7 (or 8),
* the number is too big, and we will return a range error.
*
* Set any if any `digits' consumed; make it negative to indicate
* overflow.
*/
cutoff = neg ? LONG_MIN : LONG_MAX;
cutlim = cutoff % base;
cutoff /= base;
if (neg) {
if (cutlim > 0) {
cutlim -= base;
cutoff += 1;
}
cutlim = -cutlim;
}
for (acc = 0, any = 0;; c = (unsigned char) *s++) {
if (isdigit(c))
c -= '0';
else if (isalpha(c))
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
else
break;
if (c >= base)
break;
if (any < 0)
continue;
if (neg) {
if (acc < cutoff || (acc == cutoff && c > cutlim)) {
any = -1;
acc = LONG_MIN;
errno = ERANGE;
} else {
any = 1;
acc *= base;
acc -= c;
}
} else {
if (acc > cutoff || (acc == cutoff && c > cutlim)) {
any = -1;
acc = LONG_MAX;
errno = ERANGE;
} else {
any = 1;
acc *= base;
acc += c;
}
}
}
if (endptr != 0)
*endptr = (char *) (any ? s - 1 : nptr);
return (acc);
}

152
nolibc/stubs.c Normal file
View file

@ -0,0 +1,152 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#define STUB_ABORT(function) \
int __unsup_##function(void) __asm__(#function) __attribute__((noreturn)); \
int __unsup_##function(void) \
{ \
printf("STUB: abort: %s() called\n", #function); \
abort(); \
}
/*
* Warnings are deliberately disabled here to reduce unnecessary verbosity under
* normal operation. To enable, replace "called = 1" with "called = 0" and
* rebuild.
*/
#define STUB_WARN_ONCE(type, function, ret) \
type __unsup_##function(void) __asm__(#function); \
type __unsup_##function(void) \
{ \
static int called = 1; \
if (!called) {\
printf("STUB: %s() called\n", #function); \
called = 1; \
} \
errno = ENOSYS; \
return ret; \
}
#define STUB_IGNORE(type, function, ret) \
type __unsup_##function(void) __asm__(#function); \
type __unsup_##function(void) \
{ \
errno = ENOSYS; \
return ret; \
}
/* stdio.h */
STUB_WARN_ONCE(int, fflush, 0);
STUB_ABORT(rename);
STUB_ABORT(sscanf); /* Used only for parsing OCAMLRUNPARAM, never called */
/*
* The following stubs are not required by the OCaml runtime, but are
* needed to build the freestanding version of GMP used by Mirage.
*/
STUB_WARN_ONCE(int, fread, 0);
STUB_WARN_ONCE(int, getc, EOF);
STUB_WARN_ONCE(int, ungetc, EOF);
STUB_WARN_ONCE(int, fwrite, 0);
STUB_WARN_ONCE(int, fputc, EOF);
STUB_WARN_ONCE(int, fputs, EOF);
STUB_WARN_ONCE(int, putc, EOF);
STUB_WARN_ONCE(int, ferror, 1);
STUB_WARN_ONCE(int, fopen, 1);
STUB_WARN_ONCE(int, fclose, 1);
/* stdlib.h */
STUB_WARN_ONCE(char *, getenv, NULL);
STUB_WARN_ONCE(char *, secure_getenv, NULL);
STUB_ABORT(system);
/* unistd.h */
STUB_WARN_ONCE(int, chdir, -1);
STUB_ABORT(close);
STUB_ABORT(getcwd);
STUB_WARN_ONCE(pid_t, getpid, 2);
STUB_WARN_ONCE(pid_t, getppid, 1);
STUB_IGNORE(int, isatty, 0);
STUB_IGNORE(off_t, lseek, -1);
STUB_ABORT(read);
STUB_IGNORE(int, readlink, -1);
STUB_ABORT(unlink);
STUB_ABORT(rmdir);
STUB_ABORT(ftruncate);
STUB_ABORT(execv);
/* dirent.h */
STUB_WARN_ONCE(int, closedir, -1);
STUB_WARN_ONCE(void *, opendir, NULL);
STUB_WARN_ONCE(struct dirent *, readdir, NULL);
/* fcntl.h */
STUB_ABORT(fcntl);
STUB_WARN_ONCE(int, open, -1);
/* signal.h */
STUB_IGNORE(int, setjmp, 0);
STUB_ABORT(signal);
/*
* The following stubs are not required by the OCaml runtime, but are
* needed to build the freestanding version of GMP used by Mirage.
*/
STUB_ABORT(raise);
/* string.h */
STUB_ABORT(strerror);
/* sys/stat.h */
STUB_WARN_ONCE(int, stat, -1);
STUB_ABORT(mkdir);
/* pthread.h */
STUB_IGNORE(int, pthread_join, 0);
STUB_IGNORE(int, pthread_create, EAGAIN);
STUB_IGNORE(int, pthread_attr_init, 0);
STUB_ABORT(pthread_cleanup_push);
STUB_ABORT(pthread_cleanup_pop);
/* above that line, for OCaml 5, those are only required (i guess) for the configure step */
STUB_IGNORE(int, pthread_mutex_lock, 0);
STUB_IGNORE(int, pthread_mutex_trylock, 0);
STUB_IGNORE(int, pthread_mutex_unlock, 0);
STUB_IGNORE(int, pthread_mutex_destroy, 0);
STUB_IGNORE(int, pthread_mutex_init, 0);
STUB_IGNORE(int, pthread_mutexattr_init, 0);
STUB_IGNORE(int, pthread_mutexattr_destroy, 0);
STUB_IGNORE(int, pthread_mutexattr_settype, 0);
STUB_IGNORE(int, pthread_sigmask, 0);
STUB_IGNORE(int, pthread_equal, 1);
STUB_IGNORE(int, pthread_condattr_init, 0);
/* pthread_condattr_destroy: not used by Ocaml 5 (pthread_condattr_init is only used in
ocaml/runtime/platform.c with a function local variable as argument)
*/
STUB_IGNORE(int, pthread_cond_init, 0);
STUB_IGNORE(int, pthread_cond_destroy, 0);
/* it's possible to create a [Stdlib.Condition.t] but an execution path exists
* where we don't really use it. However, OCaml will try to destroy this
* [Stdlib.Condition.t] and we must ignore such execution path which is still
* safe for unikernels (with one core). The real issue with [pthread_cond_*] is
* when we would like to suspend the execution with [pthread_cond_wait] but
* [pthread_cond_init] and [pthread_cond_destroy] are safe to just ignore. */
STUB_ABORT(pthread_cond_wait);
STUB_ABORT(pthread_cond_signal);
STUB_IGNORE(int, pthread_cond_broadcast, 0);
STUB_ABORT(pthread_self);
STUB_ABORT(pthread_detach);
STUB_IGNORE(int, sigfillset, 0);
STUB_ABORT(sigwait);
STUB_ABORT(usleep);

11
nolibc/sysconf.c Normal file
View file

@ -0,0 +1,11 @@
#include <unistd.h>
#include <sys/mman.h>
long sysconf(int x) {
switch (x) {
case _SC_PAGESIZE: /* _SC_PAGE_SIZE */
return OCAML_SOLO5_PAGESIZE;
default:
return -1;
}
}

47
nolibc/sysdeps.c Normal file
View file

@ -0,0 +1,47 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
int errno;
extern void gilbraltar_serial_write(const char *, size_t);
extern void gilbraltar_serial_puts(const char *);
static size_t console_write(FILE *f __attribute__((unused)), const char *str, size_t len) {
gilbraltar_serial_write(str, len);
return (len);
}
static FILE console = { .write = console_write };
FILE *stderr = &console;
FILE *stdout = &console;
ssize_t write(int fd, const void *buf, size_t count) {
if (fd == 1 || fd == 2) {
gilbraltar_serial_write(buf, count);
return (count);
}
errno = ENOSYS;
return (-1);
}
void exit(int status) {
while (1)
__asm__ __volatile("wfi");
}
void abort(void) {
gilbraltar_serial_puts("Aborted\r\n");
while (1)
__asm__ __volatile("wfi");
}
#if defined(__aarch64__)
int __getauxval(int unused) {
errno = ENOENT;
return (0);
}
#endif

650
nolibc/vfprintf.c Normal file
View file

@ -0,0 +1,650 @@
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <limits.h>
#include <string.h>
#include <stdarg.h>
#include <math.h>
#include <float.h>
#include <stdint.h>
/* Some useful macros */
#define MAX(a,b) ((a)>(b) ? (a) : (b))
#define MIN(a,b) ((a)<(b) ? (a) : (b))
/* Convenient bit representation for modifier flags, which all fall
* within 31 codepoints of the space character. */
#define ALT_FORM (1U<<'#'-' ')
#define ZERO_PAD (1U<<'0'-' ')
#define LEFT_ADJ (1U<<'-'-' ')
#define PAD_POS (1U<<' '-' ')
#define MARK_POS (1U<<'+'-' ')
#define GROUPED (1U<<'\''-' ')
#define FLAGMASK (ALT_FORM|ZERO_PAD|LEFT_ADJ|PAD_POS|MARK_POS|GROUPED)
#if UINT_MAX == ULONG_MAX
#define LONG_IS_INT
#endif
#if SIZE_MAX != ULONG_MAX || UINTMAX_MAX != ULLONG_MAX
#define ODD_TYPES
#endif
/* State machine to accept length modifiers + conversion specifiers.
* Result is 0 on failure, or an argument type to pop on success. */
enum {
BARE, LPRE, LLPRE, HPRE, HHPRE, BIGLPRE,
ZTPRE, JPRE,
STOP,
PTR, INT, UINT, ULLONG,
#ifndef LONG_IS_INT
LONG, ULONG,
#else
#define LONG INT
#define ULONG UINT
#endif
SHORT, USHORT, CHAR, UCHAR,
#ifdef ODD_TYPES
LLONG, SIZET, IMAX, UMAX, PDIFF, UIPTR,
#else
#define LLONG ULLONG
#define SIZET ULONG
#define IMAX LLONG
#define UMAX ULLONG
#define PDIFF LONG
#define UIPTR ULONG
#endif
DBL, LDBL,
NOARG,
MAXSTATE
};
#define S(x) [(x)-'A']
static const unsigned char states[]['z'-'A'+1] = {
{ /* 0: bare types */
S('d') = INT, S('i') = INT,
S('o') = UINT, S('u') = UINT, S('x') = UINT, S('X') = UINT,
S('e') = DBL, S('f') = DBL, S('g') = DBL, S('a') = DBL,
S('E') = DBL, S('F') = DBL, S('G') = DBL, S('A') = DBL,
S('c') = CHAR, S('C') = INT,
S('s') = PTR, S('S') = PTR, S('p') = UIPTR, S('n') = PTR,
S('m') = NOARG,
S('l') = LPRE, S('h') = HPRE, S('L') = BIGLPRE,
S('z') = ZTPRE, S('j') = JPRE, S('t') = ZTPRE,
}, { /* 1: l-prefixed */
S('d') = LONG, S('i') = LONG,
S('o') = ULONG, S('u') = ULONG, S('x') = ULONG, S('X') = ULONG,
S('e') = DBL, S('f') = DBL, S('g') = DBL, S('a') = DBL,
S('E') = DBL, S('F') = DBL, S('G') = DBL, S('A') = DBL,
S('c') = INT, S('s') = PTR, S('n') = PTR,
S('l') = LLPRE,
}, { /* 2: ll-prefixed */
S('d') = LLONG, S('i') = LLONG,
S('o') = ULLONG, S('u') = ULLONG,
S('x') = ULLONG, S('X') = ULLONG,
S('n') = PTR,
}, { /* 3: h-prefixed */
S('d') = SHORT, S('i') = SHORT,
S('o') = USHORT, S('u') = USHORT,
S('x') = USHORT, S('X') = USHORT,
S('n') = PTR,
S('h') = HHPRE,
}, { /* 4: hh-prefixed */
S('d') = CHAR, S('i') = CHAR,
S('o') = UCHAR, S('u') = UCHAR,
S('x') = UCHAR, S('X') = UCHAR,
S('n') = PTR,
}, { /* 5: L-prefixed */
S('e') = LDBL, S('f') = LDBL, S('g') = LDBL, S('a') = LDBL,
S('E') = LDBL, S('F') = LDBL, S('G') = LDBL, S('A') = LDBL,
S('n') = PTR,
}, { /* 6: z- or t-prefixed (assumed to be same size) */
S('d') = PDIFF, S('i') = PDIFF,
S('o') = SIZET, S('u') = SIZET,
S('x') = SIZET, S('X') = SIZET,
S('n') = PTR,
}, { /* 7: j-prefixed */
S('d') = IMAX, S('i') = IMAX,
S('o') = UMAX, S('u') = UMAX,
S('x') = UMAX, S('X') = UMAX,
S('n') = PTR,
}
};
#define OOB(x) ((unsigned)(x)-'A' > 'z'-'A')
union arg
{
uintmax_t i;
long double f;
void *p;
};
static void pop_arg(union arg *arg, int type, va_list *ap)
{
/* Give the compiler a hint for optimizing the switch. */
if ((unsigned)type > MAXSTATE) return;
switch (type) {
case PTR: arg->p = va_arg(*ap, void *);
break; case INT: arg->i = va_arg(*ap, int);
break; case UINT: arg->i = va_arg(*ap, unsigned int);
#ifndef LONG_IS_INT
break; case LONG: arg->i = va_arg(*ap, long);
break; case ULONG: arg->i = va_arg(*ap, unsigned long);
#endif
break; case ULLONG: arg->i = va_arg(*ap, unsigned long long);
break; case SHORT: arg->i = (short)va_arg(*ap, int);
break; case USHORT: arg->i = (unsigned short)va_arg(*ap, int);
break; case CHAR: arg->i = (signed char)va_arg(*ap, int);
break; case UCHAR: arg->i = (unsigned char)va_arg(*ap, int);
#ifdef ODD_TYPES
break; case LLONG: arg->i = va_arg(*ap, long long);
break; case SIZET: arg->i = va_arg(*ap, size_t);
break; case IMAX: arg->i = va_arg(*ap, intmax_t);
break; case UMAX: arg->i = va_arg(*ap, uintmax_t);
break; case PDIFF: arg->i = va_arg(*ap, ptrdiff_t);
break; case UIPTR: arg->i = (uintptr_t)va_arg(*ap, void *);
#endif
break; case DBL: arg->f = va_arg(*ap, double);
break; case LDBL: arg->f = va_arg(*ap, long double);
}
}
static void out(FILE *f, const char *s, size_t l)
{
f->write(f, s, l);
}
static void pad(FILE *f, char c, int w, int l, int fl)
{
char pad[256];
if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return;
l = w - l;
memset(pad, c, l>sizeof pad ? sizeof pad : l);
for (; l >= sizeof pad; l -= sizeof pad)
out(f, pad, sizeof pad);
out(f, pad, l);
}
static const char xdigits[16] = {
"0123456789ABCDEF"
};
static char *fmt_x(uintmax_t x, char *s, int lower)
{
for (; x; x>>=4) *--s = xdigits[(x&15)]|lower;
return s;
}
static char *fmt_o(uintmax_t x, char *s)
{
for (; x; x>>=3) *--s = '0' + (x&7);
return s;
}
static char *fmt_u(uintmax_t x, char *s)
{
unsigned long y;
for ( ; x>ULONG_MAX; x/=10) *--s = '0' + x%10;
for (y=x; y; y/=10) *--s = '0' + y%10;
return s;
}
/* Do not override this check. The floating point printing code below
* depends on the float.h constants being right. If they are wrong, it
* may overflow the stack. */
#if LDBL_MANT_DIG == 53
typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)];
#endif
static int fmt_fp(FILE *f, double y, int w, int p, int fl, int t)
{
uint32_t big[(DBL_MANT_DIG+28)/29 + 1 // mantissa expansion
+ (DBL_MAX_EXP+DBL_MANT_DIG+28+8)/9]; // exponent expansion
uint32_t *a, *d, *r, *z;
int e2=0, e, i, j, l;
char buf[9+DBL_MANT_DIG/4], *s;
const char *prefix="-0X+0X 0X-0x+0x 0x";
int pl;
char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr;
pl=1;
if (signbit(y)) {
y=-y;
} else if (fl & MARK_POS) {
prefix+=3;
} else if (fl & PAD_POS) {
prefix+=6;
} else prefix++, pl=0;
if (!isfinite(y)) {
char *s = (t&32)?"inf":"INF";
if (y!=y) s=(t&32)?"nan":"NAN";
pad(f, ' ', w, 3+pl, fl&~ZERO_PAD);
out(f, prefix, pl);
out(f, s, 3);
pad(f, ' ', w, 3+pl, fl^LEFT_ADJ);
return MAX(w, 3+pl);
}
y = frexp(y, &e2) * 2;
if (y) e2--;
if ((t|32)=='a') {
double round = 8.0;
int re;
if (t&32) prefix += 9;
pl += 2;
if (p<0 || p>=DBL_MANT_DIG/4-1) re=0;
else re=DBL_MANT_DIG/4-1-p;
if (re) {
while (re--) round*=16;
if (*prefix=='-') {
y=-y;
y-=round;
y+=round;
y=-y;
} else {
y+=round;
y-=round;
}
}
estr=fmt_u(e2<0 ? -e2 : e2, ebuf);
if (estr==ebuf) *--estr='0';
*--estr = (e2<0 ? '-' : '+');
*--estr = t+('p'-'a');
s=buf;
do {
int x=y;
*s++=xdigits[x]|(t&32);
y=16*(y-x);
if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.';
} while (y);
if (p && s-buf-2 < p)
l = (p+2) + (ebuf-estr);
else
l = (s-buf) + (ebuf-estr);
pad(f, ' ', w, pl+l, fl);
out(f, prefix, pl);
pad(f, '0', w, pl+l, fl^ZERO_PAD);
out(f, buf, s-buf);
pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0);
out(f, estr, ebuf-estr);
pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
return MAX(w, pl+l);
}
if (p<0) p=6;
if (y) y *= 0x1p28, e2-=28;
if (e2<0) a=r=z=big;
else a=r=z=big+sizeof(big)/sizeof(*big) - DBL_MANT_DIG - 1;
do {
*z = y;
y = 1000000000*(y-*z++);
} while (y);
while (e2>0) {
uint32_t carry=0;
int sh=MIN(29,e2);
for (d=z-1; d>=a; d--) {
uint64_t x = ((uint64_t)*d<<sh)+carry;
*d = x % 1000000000;
carry = x / 1000000000;
}
if (carry) *--a = carry;
while (z>a && !z[-1]) z--;
e2-=sh;
}
while (e2<0) {
uint32_t carry=0, *b;
int sh=MIN(9,-e2), need=1+(p+DBL_MANT_DIG/3+8)/9;
for (d=a; d<z; d++) {
uint32_t rm = *d & (1<<sh)-1;
*d = (*d>>sh) + carry;
carry = (1000000000>>sh) * rm;
}
if (!*a) a++;
if (carry) *z++ = carry;
/* Avoid (slow!) computation past requested precision */
b = (t|32)=='f' ? r : a;
if (z-b > need) z = b+need;
e2+=sh;
}
if (a<z) for (i=10, e=9*(r-a); *a>=i; i*=10, e++);
else e=0;
/* Perform rounding: j is precision after the radix (possibly neg) */
j = p - ((t|32)!='f')*e - ((t|32)=='g' && p);
if (j < 9*(z-r-1)) {
uint32_t x;
/* We avoid C's broken division of negative numbers */
d = r + 1 + ((j+9*DBL_MAX_EXP)/9 - DBL_MAX_EXP);
j += 9*DBL_MAX_EXP;
j %= 9;
for (i=10, j++; j<9; i*=10, j++);
x = *d % i;
/* Are there any significant digits past j? */
if (x || d+1!=z) {
double round = 2/DBL_EPSILON;
double small;
if (*d/i & 1) round += 2;
if (x<i/2) small=0x0.8p0;
else if (x==i/2 && d+1==z) small=0x1.0p0;
else small=0x1.8p0;
if (pl && *prefix=='-') round*=-1, small*=-1;
*d -= x;
/* Decide whether to round by probing round+small */
if (round+small != round) {
*d = *d + i;
while (*d > 999999999) {
*d--=0;
if (d<a) *--a=0;
(*d)++;
}
for (i=10, e=9*(r-a); *a>=i; i*=10, e++);
}
}
if (z>d+1) z=d+1;
}
for (; z>a && !z[-1]; z--);
if ((t|32)=='g') {
if (!p) p++;
if (p>e && e>=-4) {
t--;
p-=e+1;
} else {
t-=2;
p--;
}
if (!(fl&ALT_FORM)) {
/* Count trailing zeros in last place */
if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++);
else j=9;
if ((t|32)=='f')
p = MIN(p,MAX(0,9*(z-r-1)-j));
else
p = MIN(p,MAX(0,9*(z-r-1)+e-j));
}
}
l = 1 + p + (p || (fl&ALT_FORM));
if ((t|32)=='f') {
if (e>0) l+=e;
} else {
estr=fmt_u(e<0 ? -e : e, ebuf);
while(ebuf-estr<2) *--estr='0';
*--estr = (e<0 ? '-' : '+');
*--estr = t;
l += ebuf-estr;
}
pad(f, ' ', w, pl+l, fl);
out(f, prefix, pl);
pad(f, '0', w, pl+l, fl^ZERO_PAD);
if ((t|32)=='f') {
if (a>r) a=r;
for (d=a; d<=r; d++) {
char *s = fmt_u(*d, buf+9);
if (d!=a) while (s>buf) *--s='0';
else if (s==buf+9) *--s='0';
out(f, s, buf+9-s);
}
if (p || (fl&ALT_FORM)) out(f, ".", 1);
for (; d<z && p>0; d++, p-=9) {
char *s = fmt_u(*d, buf+9);
while (s>buf) *--s='0';
out(f, s, MIN(9,p));
}
pad(f, '0', p+9, 9, 0);
} else {
if (z<=a) z=a+1;
for (d=a; d<z && p>=0; d++) {
char *s = fmt_u(*d, buf+9);
if (s==buf+9) *--s='0';
if (d!=a) while (s>buf) *--s='0';
else {
out(f, s++, 1);
if (p>0||(fl&ALT_FORM)) out(f, ".", 1);
}
out(f, s, MIN(buf+9-s, p));
p -= buf+9-s;
}
pad(f, '0', p+18, 18, 0);
out(f, estr, ebuf-estr);
}
pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
return MAX(w, pl+l);
}
static int getint(char **s) {
int i;
for (i=0; isdigit(**s); (*s)++)
i = 10*i + (**s-'0');
return i;
}
int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, int *nl_type)
{
char *a, *z, *s=(char *)fmt;
unsigned l10n=0, fl;
int w, p;
union arg arg;
int argpos;
unsigned st, ps;
int cnt=0, l=0;
int i;
char buf[sizeof(uintmax_t)*3+3+LDBL_MANT_DIG/4];
const char *prefix;
int t, pl;
for (;;) {
/* Update output count, end loop when fmt is exhausted */
if (cnt >= 0) {
if (l > INT_MAX - cnt) {
errno = EOVERFLOW;
cnt = -1;
} else cnt += l;
}
if (!*s) break;
/* Handle literal text and %% format specifiers */
for (a=s; *s && *s!='%'; s++);
for (z=s; s[0]=='%' && s[1]=='%'; z++, s+=2);
l = z-a;
if (f) out(f, a, l);
if (l) continue;
if (isdigit(s[1]) && s[2]=='$') {
l10n=1;
argpos = s[1]-'0';
s+=3;
} else {
argpos = -1;
s++;
}
/* Read modifier flags */
for (fl=0; (unsigned)*s-' '<32 && (FLAGMASK&(1U<<*s-' ')); s++)
fl |= 1U<<*s-' ';
/* Read field width */
if (*s=='*') {
if (isdigit(s[1]) && s[2]=='$') {
l10n=1;
nl_type[s[1]-'0'] = INT;
w = nl_arg[s[1]-'0'].i;
s+=3;
} else if (!l10n) {
w = f ? va_arg(*ap, int) : 0;
s++;
} else return -1;
if (w<0) fl|=LEFT_ADJ, w=-w;
} else if ((w=getint(&s))<0) return -1;
/* Read precision */
if (*s=='.' && s[1]=='*') {
if (isdigit(s[2]) && s[3]=='$') {
nl_type[s[2]-'0'] = INT;
p = nl_arg[s[2]-'0'].i;
s+=4;
} else if (!l10n) {
p = f ? va_arg(*ap, int) : 0;
s+=2;
} else return -1;
} else if (*s=='.') {
s++;
p = getint(&s);
} else p = -1;
/* Format specifier state machine */
st=0;
do {
if (OOB(*s)) return -1;
ps=st;
st=states[st]S(*s++);
} while (st-1<STOP);
if (!st) return -1;
/* Check validity of argument type (nl/normal) */
if (st==NOARG) {
if (argpos>=0) return -1;
} else {
if (argpos>=0) nl_type[argpos]=st, arg=nl_arg[argpos];
else if (f) pop_arg(&arg, st, ap);
else return 0;
}
if (!f) continue;
z = buf + sizeof(buf);
prefix = "-+ 0X0x";
pl = 0;
t = s[-1];
/* Transform ls,lc -> S,C */
if (ps && (t&15)==3) t&=~32;
/* - and 0 flags are mutually exclusive */
if (fl & LEFT_ADJ) fl &= ~ZERO_PAD;
switch(t) {
case 'n':
switch(ps) {
case BARE: *(int *)arg.p = cnt; break;
case LPRE: *(long *)arg.p = cnt; break;
case LLPRE: *(long long *)arg.p = cnt; break;
case HPRE: *(unsigned short *)arg.p = cnt; break;
case HHPRE: *(unsigned char *)arg.p = cnt; break;
case ZTPRE: *(size_t *)arg.p = cnt; break;
case JPRE: *(uintmax_t *)arg.p = cnt; break;
}
continue;
case 'p':
p = MAX(p, 2*sizeof(void*));
t = 'x';
fl |= ALT_FORM;
case 'x': case 'X':
a = fmt_x(arg.i, z, t&32);
if (arg.i && (fl & ALT_FORM)) prefix+=(t>>4), pl=2;
if (0) {
case 'o':
a = fmt_o(arg.i, z);
if ((fl&ALT_FORM) && p<z-a+1) p=z-a+1;
} if (0) {
case 'd': case 'i':
pl=1;
if (arg.i>INTMAX_MAX) {
arg.i=-arg.i;
} else if (fl & MARK_POS) {
prefix++;
} else if (fl & PAD_POS) {
prefix+=2;
} else pl=0;
case 'u':
a = fmt_u(arg.i, z);
}
if (p>=0) fl &= ~ZERO_PAD;
if (!arg.i && !p) {
a=z;
break;
}
p = MAX(p, z-a + !arg.i);
break;
case 'c':
*(a=z-(p=1))=arg.i;
fl &= ~ZERO_PAD;
break;
case 'm':
if (1) a = strerror(errno); else
case 's':
a = arg.p ? arg.p : "(null)";
z = memchr(a, 0, p);
if (!z) z=a+p;
else p=z-a;
fl &= ~ZERO_PAD;
break;
case 'e': case 'f': case 'g': case 'a':
case 'E': case 'F': case 'G': case 'A':
l = fmt_fp(f, arg.f, w, p, fl, t);
continue;
}
if (p < z-a) p = z-a;
if (w < pl+p) w = pl+p;
pad(f, ' ', w, pl+p, fl);
out(f, prefix, pl);
pad(f, '0', w, pl+p, fl^ZERO_PAD);
pad(f, '0', p, z-a, 0);
out(f, a, z-a);
pad(f, ' ', w, pl+p, fl^LEFT_ADJ);
l = w;
}
if (f) return cnt;
if (!l10n) return 0;
for (i=1; i<=NL_ARGMAX && nl_type[i]; i++)
pop_arg(nl_arg+i, nl_type[i], ap);
for (; i<=NL_ARGMAX && !nl_type[i]; i++);
if (i<=NL_ARGMAX) return -1;
return 1;
}
int vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap)
{
va_list ap2;
int nl_type[NL_ARGMAX+1] = {0};
union arg nl_arg[NL_ARGMAX+1];
int ret;
/* the copy allows passing va_list* even if va_list is an array */
va_copy(ap2, ap);
if (printf_core(0, fmt, &ap2, nl_arg, nl_type) < 0) {
va_end(ap2);
return -1;
}
ret = printf_core(f, fmt, &ap2, nl_arg, nl_type);
va_end(ap2);
return ret;
}

41
nolibc/vsnprintf.c Normal file
View file

@ -0,0 +1,41 @@
#include <stdio.h>
#include <limits.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>
static size_t sn_write(FILE *f, const char *s, size_t l)
{
size_t k = f->wend - f->wpos;
if (k > l) k = l;
memcpy(f->wpos, s, k);
f->wpos += k;
/* pretend to succeed, but discard extra data */
return l;
}
int vsnprintf(char *restrict s, size_t n, const char *restrict fmt, va_list ap)
{
int r;
char b;
FILE f = { .write = sn_write };
if (n-1 > INT_MAX-1) {
if (n) {
errno = EOVERFLOW;
return -1;
}
s = &b;
n = 1;
}
/* Ensure pointers don't wrap if "infinite" n is passed in */
if (n > (char *)0+SIZE_MAX-s-1) n = (char *)0+SIZE_MAX-s-1;
f.wpos = s;
f.wend = (s+n);
r = vfprintf(&f, fmt, ap);
/* Null-terminate, overwriting last char if dest buffer is full */
if (n) f.wpos[-(f.wpos == f.wend)] = 0;
return r;
}

7
openlibm/.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
*.o
*~
*.a
*.dll*
*.so*
*.dylib*
*.pc

61
openlibm/.mailmap Normal file
View file

@ -0,0 +1,61 @@
JuliaLang <julia-dev@googlegroups.com> <julia-dev@googlegroups.com>
JuliaLang <julia-dev@googlegroups.com> <julia-math@googlegroups.com>
Jeff Bezanson <jeff.bezanson@gmail.com> <jeff.bezanson@gmail.com>
Jeff Bezanson <jeff.bezanson@gmail.com> <bezanson@beagle.darwinproject.mit.edu>
Jeff Bezanson <jeff.bezanson@gmail.com> <bezanson@caspian.caspian.mit.edu>
Jeff Bezanson <jeff.bezanson@gmail.com> <bezanson@evolution.local>
Jeff Bezanson <jeff.bezanson@gmail.com> <bezanson@mathstation045.mit.edu>
Jeff Bezanson <jeff.bezanson@gmail.com> <bezanson@mathstation049.mit.edu>
Jeff Bezanson <jeff.bezanson@gmail.com> <bezanson@mathstation186.mit.edu>
Jeff Bezanson <jeff.bezanson@gmail.com> <bezanson@post.harvard.edu>
Jeff Bezanson <jeff.bezanson@gmail.com> <bezanson@scooby-doo.csail.mit.edu>
Jeff Bezanson <jeff.bezanson@gmail.com> <bezanson@shaggy.csail.mit.edu>
Jeff Bezanson <jeff.bezanson@gmail.com> <jeff@lagann.(none)>
Jeff Bezanson <jeff.bezanson@gmail.com> <julia@beowulf1.csail.mit.edu>
Jeff Bezanson <jeff.bezanson@gmail.com> <vcloud@julia02.domain.local>
Stefan Karpinski <stefan@karpinski.org> <stefan@karpinski.org>
Stefan Karpinski <stefan@karpinski.org> <stefan.karpinski@gmail.com>
Stefan Karpinski <stefan@karpinski.org> <stefan.karpinski@post.harvard.edu>
Viral B. Shah <viral@mayin.org> <viral@mayin.org>
Viral B. Shah <viral@mayin.org> <viral@beowulf1.csail.mit.edu>
Viral B. Shah <viral@mayin.org> <viral@neumann.cs.ucsb.edu>
Viral B. Shah <viral@mayin.org> <viral@ubuntu-VirtualBox.(none)>
George Xing <gxing@mit.edu> <gxing@mit.edu>
George Xing <gxing@mit.edu> <noobiecubie@gmail.com>
Stephan Boyer <boyers@mit.edu> <boyers@mit.edu>
Stephan Boyer <boyers@mit.edu> <stephan@julialang.xvm.mit.edu>
Stephan Boyer <boyers@mit.edu> <stephan@ubuntu.(none)>
Stephan Boyer <boyers@mit.edu> <stephan@ubuntu.ubuntu-domain>
Giuseppe Zingales <giuseppe.pet.zingales@gmail.com> <giuseppe.pet.zingales@gmail.com>
Giuseppe Zingales <giuseppe.pet.zingales@gmail.com> <g3@ubuntu.ubuntu-domain>
Jameson Nash <jameson@mit.edu> <jameson@mit.edu>
Jameson Nash <jameson@mit.edu> <vtjnash@comcast.net>
Jameson Nash <jameson@mit.edu> <vtjnash@gmail.com>
Alan Edelman <mit.edelman@gmail.com> <mit.edelman@gmail.com>
PlayMyCode <joe@playmycode.com> <joe@playmycode.com>
PlayMyCode <joe@playmycode.com> <hello@playmycode.com>
Corey M. Hoffstein <corey@hoffstein.com> <corey@hoffstein.com>
Corey M. Hoffstein <corey@hoffstein.com> <corey@newfoundresearch.com>
Stefan Kroboth <stefan.kroboth@gmail.com> <stefan.kroboth@gmail.com>
Tim Holy <tim.holy@gmail.com> <tim.holy@gmail.com>
Tim Holy <tim.holy@gmail.com> <holy@wustl.edu>
Patrick O'Leary <patrick.oleary@gmail.com> <patrick.oleary@gmail.com>
Ivan Mantova <horphus@gmail.com> <horphus@gmail.com>
Keno Fischer <kfischer@college.harvard.edu> <kfischer@college.harvard.edu>
Keno Fischer <kfischer@college.harvard.edu> <kfischer+github@college.harvard.edu>
Keno Fischer <kfischer@college.harvard.edu> <kenof@stanford.edu>

73
openlibm/.travis.yml Normal file
View file

@ -0,0 +1,73 @@
# We require a full (virtual) machine to load the kernel module for
# binfmt support, which is needed to test other architectures besides
# x86 using qemu user emulation. (This will not work in a container.)
sudo: required
dist: trusty
language: c
script:
- make $FLAGS
- make check $FLAGS $TEST_FLAGS
- make clean && git status --ignored --porcelain && test -z "$(git status --ignored --porcelain)"
matrix:
include:
- compiler: clang
os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.7
packages:
- clang-3.7
env: FLAGS="CC=clang-3.7 CXX=clang++-3.7"
- os: osx
env: FLAGS="CC=clang"
- os: linux
env: FLAGS="CC=gcc"
- os: linux
env: FLAGS="CC=gcc ARCH=i686" # implies -m32 -march=i686
addons:
apt:
packages:
- gcc-multilib
- os: linux
env: FLAGS="CC=aarch64-linux-gnu-gcc" TEST_FLAGS="LDFLAGS=-static"
addons:
apt:
packages:
- gcc-aarch64-linux-gnu
- libc6-dev-arm64-cross
- qemu-user-static
- binfmt-support
- os: linux
env: FLAGS="CC=arm-linux-gnueabihf-gcc" TEST_FLAGS="LDFLAGS=-static"
addons:
apt:
packages:
- gcc-arm-linux-gnueabihf
- libc6-dev-armhf-cross
- qemu-user-static
- binfmt-support
# This works, but only if qemu-user-static is >= v2.4. This is not the
# case on the default trusty images, so we add a PPA that has qemu 2.5
- os: linux
env: FLAGS="CC=powerpc64le-linux-gnu-gcc" TEST_FLAGS="LDFLAGS=-static"
addons:
apt:
sources:
- sourceline: "ppa:gns3/qemu"
packages:
- gcc-powerpc64le-linux-gnu
- libc6-dev-ppc64el-cross
- qemu-user-static
- binfmt-support
notifications:
email: false

576
openlibm/CMakeLists.txt Executable file
View file

@ -0,0 +1,576 @@
cmake_minimum_required(VERSION 3.25)
# Get version string from Make.inc
file(READ "${CMAKE_CURRENT_LIST_DIR}/Make.inc" MAKE_FILE)
string(REGEX MATCH "VERSION = ([0-9\.]+)" _ ${MAKE_FILE})
project(openlibm
VERSION ${CMAKE_MATCH_1}
LANGUAGES C ASM)
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
add_library("${PROJECT_NAME}")
# Find the relevant folder depending on the architecture
set(OPENLIBM_ARCH_FOLDER ${CMAKE_SYSTEM_PROCESSOR})
string(TOLOWER "${OPENLIBM_ARCH_FOLDER}" OPENLIBM_ARCH_FOLDER)
if(${OPENLIBM_ARCH_FOLDER} STREQUAL "amd64" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "x86_64")
set(OPENLIBM_ARCH_FOLDER "amd64")
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "arm64" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "aarch64")
set(OPENLIBM_ARCH_FOLDER "aarch64")
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "armv7-a")
set(OPENLIBM_ARCH_FOLDER "arm")
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "x86" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "i686")
set(OPENLIBM_ARCH_FOLDER "i387")
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "powerpc")
set(OPENLIBM_ARCH_FOLDER "powerpc")
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "riscv64")
set(OPENLIBM_ARCH_FOLDER "riscv64")
else()
message(FATAL_ERROR "${PROJECT_NAME} not set up for detected architecture: ${OPENLIBM_ARCH_FOLDER}")
endif()
# Compile flags
list(APPEND C_ASM_COMPILE_FLAGS "-ffp-contract=off" "-fno-fast-math" "-fno-rounding-math" "-fno-math-errno")
list(APPEND C_ASM_COMPILE_FLAGS "-fPIC" "-std=c99" "-fno-builtin")
list(APPEND C_ASM_COMPILE_FLAGS "-Wall" "-Wno-implicit-function-declaration")
list(APPEND C_ASM_COMPILE_FLAGS "-DASSEMBLER" "-D__BSD_VISIBLE" "-O3")
# Compiler-specific compile flags
if("${CMAKE_C_COMPILER_ID}" MATCHES "Clang")
list(APPEND C_ASM_COMPILE_FLAGS "-fno-strict-aliasing" "-ffp-exception-behavior=strict")
elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
list(APPEND C_ASM_COMPILE_FLAGS "-fno-gnu89-inline")
else()
message(FATAL_ERROR "${PROJECT_NAME} not set up to be compiled with ${CMAKE_C_COMPILER_ID}")
endif()
# Architecture-specific compile flags - take advantage of sse on x86
if(${OPENLIBM_ARCH_FOLDER} STREQUAL "i387")
list(APPEND C_ASM_COMPILE_FLAGS "-march=i686" "-m32" "-msse2" "-mfpmath=sse")
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "amd64")
list(APPEND C_ASM_COMPILE_FLAGS "-m64" "-msse2" "-mfpmath=sse")
endif()
# Suppress warnings if requested
if(OPENLIBM_SUPPRESS_WARNINGS)
list(APPEND C_ASM_COMPILE_FLAGS "-w")
endif()
# Add compile flags
target_compile_options("${PROJECT_NAME}" PUBLIC ${C_ASM_COMPILE_FLAGS})
# Project Source
set(PROJECT_SRC "${CMAKE_CURRENT_SOURCE_DIR}")
# Common
list(APPEND OPENLIBM_C_SOURCE
# src
"${PROJECT_SRC}/src/common.c"
"${PROJECT_SRC}/src/e_acos.c"
"${PROJECT_SRC}/src/e_acosf.c"
"${PROJECT_SRC}/src/e_acosh.c"
"${PROJECT_SRC}/src/e_acoshf.c"
"${PROJECT_SRC}/src/e_asin.c"
"${PROJECT_SRC}/src/e_asinf.c"
"${PROJECT_SRC}/src/e_atan2.c"
"${PROJECT_SRC}/src/e_atan2f.c"
"${PROJECT_SRC}/src/e_atanh.c"
"${PROJECT_SRC}/src/e_atanhf.c"
"${PROJECT_SRC}/src/e_cosh.c"
"${PROJECT_SRC}/src/e_coshf.c"
"${PROJECT_SRC}/src/e_exp.c"
"${PROJECT_SRC}/src/e_expf.c"
"${PROJECT_SRC}/src/e_fmod.c"
"${PROJECT_SRC}/src/e_fmodf.c"
"${PROJECT_SRC}/src/e_hypot.c"
"${PROJECT_SRC}/src/e_hypotf.c"
"${PROJECT_SRC}/src/e_j0.c"
"${PROJECT_SRC}/src/e_j0f.c"
"${PROJECT_SRC}/src/e_j1.c"
"${PROJECT_SRC}/src/e_j1f.c"
"${PROJECT_SRC}/src/e_jn.c"
"${PROJECT_SRC}/src/e_jnf.c"
"${PROJECT_SRC}/src/e_lgamma.c"
"${PROJECT_SRC}/src/e_lgamma_r.c"
"${PROJECT_SRC}/src/e_lgammaf.c"
"${PROJECT_SRC}/src/e_lgammaf_r.c"
"${PROJECT_SRC}/src/e_log.c"
"${PROJECT_SRC}/src/e_log10.c"
"${PROJECT_SRC}/src/e_log10f.c"
"${PROJECT_SRC}/src/e_log2.c"
"${PROJECT_SRC}/src/e_log2f.c"
"${PROJECT_SRC}/src/e_logf.c"
"${PROJECT_SRC}/src/e_pow.c"
"${PROJECT_SRC}/src/e_powf.c"
"${PROJECT_SRC}/src/e_remainder.c"
"${PROJECT_SRC}/src/e_remainderf.c"
"${PROJECT_SRC}/src/e_rem_pio2.c"
"${PROJECT_SRC}/src/e_rem_pio2f.c"
"${PROJECT_SRC}/src/e_sinh.c"
"${PROJECT_SRC}/src/e_sinhf.c"
"${PROJECT_SRC}/src/e_sqrt.c"
"${PROJECT_SRC}/src/e_sqrtf.c"
"${PROJECT_SRC}/src/k_cos.c"
"${PROJECT_SRC}/src/k_exp.c"
"${PROJECT_SRC}/src/k_expf.c"
"${PROJECT_SRC}/src/k_rem_pio2.c"
"${PROJECT_SRC}/src/k_sin.c"
"${PROJECT_SRC}/src/k_tan.c"
"${PROJECT_SRC}/src/k_cosf.c"
"${PROJECT_SRC}/src/k_sinf.c"
"${PROJECT_SRC}/src/k_tanf.c"
"${PROJECT_SRC}/src/s_asinh.c"
"${PROJECT_SRC}/src/s_asinhf.c"
"${PROJECT_SRC}/src/s_atan.c"
"${PROJECT_SRC}/src/s_atanf.c"
"${PROJECT_SRC}/src/s_carg.c"
"${PROJECT_SRC}/src/s_cargf.c"
"${PROJECT_SRC}/src/s_cbrt.c"
"${PROJECT_SRC}/src/s_cbrtf.c"
"${PROJECT_SRC}/src/s_ceil.c"
"${PROJECT_SRC}/src/s_ceilf.c"
"${PROJECT_SRC}/src/s_copysign.c"
"${PROJECT_SRC}/src/s_copysignf.c"
"${PROJECT_SRC}/src/s_cos.c"
"${PROJECT_SRC}/src/s_cosf.c"
"${PROJECT_SRC}/src/s_csqrt.c"
"${PROJECT_SRC}/src/s_csqrtf.c"
"${PROJECT_SRC}/src/s_erf.c"
"${PROJECT_SRC}/src/s_erff.c"
"${PROJECT_SRC}/src/s_exp2.c"
"${PROJECT_SRC}/src/s_exp2f.c"
"${PROJECT_SRC}/src/s_expm1.c"
"${PROJECT_SRC}/src/s_expm1f.c"
"${PROJECT_SRC}/src/s_fabs.c"
"${PROJECT_SRC}/src/s_fabsf.c"
"${PROJECT_SRC}/src/s_fdim.c"
"${PROJECT_SRC}/src/s_floor.c"
"${PROJECT_SRC}/src/s_floorf.c"
"${PROJECT_SRC}/src/s_fmax.c"
"${PROJECT_SRC}/src/s_fmaxf.c"
"${PROJECT_SRC}/src/s_fmin.c"
"${PROJECT_SRC}/src/s_fminf.c"
"${PROJECT_SRC}/src/s_fpclassify.c"
"${PROJECT_SRC}/src/s_frexp.c"
"${PROJECT_SRC}/src/s_frexpf.c"
"${PROJECT_SRC}/src/s_ilogb.c"
"${PROJECT_SRC}/src/s_ilogbf.c"
"${PROJECT_SRC}/src/s_isinf.c"
"${PROJECT_SRC}/src/s_isfinite.c"
"${PROJECT_SRC}/src/s_isnormal.c"
"${PROJECT_SRC}/src/s_isnan.c"
"${PROJECT_SRC}/src/s_log1p.c"
"${PROJECT_SRC}/src/s_log1pf.c"
"${PROJECT_SRC}/src/s_logb.c"
"${PROJECT_SRC}/src/s_logbf.c"
"${PROJECT_SRC}/src/s_modf.c"
"${PROJECT_SRC}/src/s_modff.c"
"${PROJECT_SRC}/src/s_nextafter.c"
"${PROJECT_SRC}/src/s_nextafterf.c"
"${PROJECT_SRC}/src/s_nexttowardf.c"
"${PROJECT_SRC}/src/s_remquo.c"
"${PROJECT_SRC}/src/s_remquof.c"
"${PROJECT_SRC}/src/s_rint.c"
"${PROJECT_SRC}/src/s_rintf.c"
"${PROJECT_SRC}/src/s_round.c"
"${PROJECT_SRC}/src/s_roundf.c"
"${PROJECT_SRC}/src/s_scalbln.c"
"${PROJECT_SRC}/src/s_scalbn.c"
"${PROJECT_SRC}/src/s_scalbnf.c"
"${PROJECT_SRC}/src/s_signbit.c"
"${PROJECT_SRC}/src/s_signgam.c"
"${PROJECT_SRC}/src/s_sin.c"
"${PROJECT_SRC}/src/s_sincos.c"
"${PROJECT_SRC}/src/s_sinf.c"
"${PROJECT_SRC}/src/s_sincosf.c"
"${PROJECT_SRC}/src/s_tan.c"
"${PROJECT_SRC}/src/s_tanf.c"
"${PROJECT_SRC}/src/s_tanh.c"
"${PROJECT_SRC}/src/s_tanhf.c"
"${PROJECT_SRC}/src/s_tgammaf.c"
"${PROJECT_SRC}/src/s_trunc.c"
"${PROJECT_SRC}/src/s_truncf.c"
"${PROJECT_SRC}/src/s_cpow.c"
"${PROJECT_SRC}/src/s_cpowf.c"
"${PROJECT_SRC}/src/w_cabs.c"
"${PROJECT_SRC}/src/w_cabsf.c"
"${PROJECT_SRC}/src/s_fma.c"
"${PROJECT_SRC}/src/s_fmaf.c"
"${PROJECT_SRC}/src/s_lrint.c"
"${PROJECT_SRC}/src/s_lrintf.c"
"${PROJECT_SRC}/src/s_lround.c"
"${PROJECT_SRC}/src/s_lroundf.c"
"${PROJECT_SRC}/src/s_llrint.c"
"${PROJECT_SRC}/src/s_llrintf.c"
"${PROJECT_SRC}/src/s_llround.c"
"${PROJECT_SRC}/src/s_llroundf.c"
"${PROJECT_SRC}/src/s_nearbyint.c"
# C99 complex functions
"${PROJECT_SRC}/src/s_ccosh.c"
"${PROJECT_SRC}/src/s_ccoshf.c"
"${PROJECT_SRC}/src/s_cexp.c"
"${PROJECT_SRC}/src/s_cexpf.c"
"${PROJECT_SRC}/src/s_cimag.c"
"${PROJECT_SRC}/src/s_cimagf.c"
"${PROJECT_SRC}/src/s_conj.c"
"${PROJECT_SRC}/src/s_conjf.c"
"${PROJECT_SRC}/src/s_cproj.c"
"${PROJECT_SRC}/src/s_cprojf.c"
"${PROJECT_SRC}/src/s_creal.c"
"${PROJECT_SRC}/src/s_crealf.c"
"${PROJECT_SRC}/src/s_csinh.c"
"${PROJECT_SRC}/src/s_csinhf.c"
"${PROJECT_SRC}/src/s_ctanh.c"
"${PROJECT_SRC}/src/s_ctanhf.c"
"${PROJECT_SRC}/src/s_cacos.c"
"${PROJECT_SRC}/src/s_cacosf.c"
"${PROJECT_SRC}/src/s_cacosh.c"
"${PROJECT_SRC}/src/s_cacoshf.c"
"${PROJECT_SRC}/src/s_casin.c"
"${PROJECT_SRC}/src/s_casinf.c"
"${PROJECT_SRC}/src/s_casinh.c"
"${PROJECT_SRC}/src/s_casinhf.c"
"${PROJECT_SRC}/src/s_catan.c"
"${PROJECT_SRC}/src/s_catanf.c"
"${PROJECT_SRC}/src/s_catanh.c"
"${PROJECT_SRC}/src/s_catanhf.c"
"${PROJECT_SRC}/src/s_clog.c"
"${PROJECT_SRC}/src/s_clogf.c"
# bsdsrc
"${PROJECT_SRC}/bsdsrc/b_exp.c"
"${PROJECT_SRC}/bsdsrc/b_log.c"
"${PROJECT_SRC}/bsdsrc/b_tgamma.c"
)
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
list(APPEND OPENLIBM_C_SOURCE
"${PROJECT_SRC}/src/s_nan.c"
)
endif()
# Determine if long double and double are the same size
include(CheckCSourceCompiles)
check_c_source_compiles("
#include <float.h>
#if (LDBL_MANT_DIG == DBL_MANT_DIG)
#error \"long double and double are the same size\"
#endif
int main(void ) { return 0; }
" LONG_DOUBLE_NOT_DOUBLE)
# Add in long double functions for x86, x64 and aarch64
if(LONG_DOUBLE_NOT_DOUBLE)
list(APPEND OPENLIBM_C_SOURCE
"${PROJECT_SRC}/src/s_copysignl.c"
"${PROJECT_SRC}/src/s_fabsl.c"
"${PROJECT_SRC}/src/s_llrintl.c"
"${PROJECT_SRC}/src/s_lrintl.c"
"${PROJECT_SRC}/src/s_modfl.c"
"${PROJECT_SRC}/src/e_acosl.c"
"${PROJECT_SRC}/src/e_asinl.c"
"${PROJECT_SRC}/src/e_atan2l.c"
"${PROJECT_SRC}/src/e_fmodl.c"
"${PROJECT_SRC}/src/s_fmaxl.c"
"${PROJECT_SRC}/src/s_fminl.c"
"${PROJECT_SRC}/src/s_ilogbl.c"
"${PROJECT_SRC}/src/e_hypotl.c"
"${PROJECT_SRC}/src/e_lgammal.c"
"${PROJECT_SRC}/src/e_remainderl.c"
"${PROJECT_SRC}/src/e_sqrtl.c"
"${PROJECT_SRC}/src/s_atanl.c"
"${PROJECT_SRC}/src/s_ceill.c"
"${PROJECT_SRC}/src/s_cosl.c"
"${PROJECT_SRC}/src/s_cprojl.c"
"${PROJECT_SRC}/src/s_csqrtl.c"
"${PROJECT_SRC}/src/s_floorl.c"
"${PROJECT_SRC}/src/s_fmal.c"
"${PROJECT_SRC}/src/s_frexpl.c"
"${PROJECT_SRC}/src/s_logbl.c"
"${PROJECT_SRC}/src/s_nexttoward.c"
"${PROJECT_SRC}/src/s_remquol.c"
"${PROJECT_SRC}/src/s_roundl.c"
"${PROJECT_SRC}/src/s_lroundl.c"
"${PROJECT_SRC}/src/s_llroundl.c"
"${PROJECT_SRC}/src/s_cpowl.c"
"${PROJECT_SRC}/src/s_cargl.c"
"${PROJECT_SRC}/src/s_sinl.c"
"${PROJECT_SRC}/src/s_sincosl.c"
"${PROJECT_SRC}/src/s_tanl.c"
"${PROJECT_SRC}/src/s_truncl.c"
"${PROJECT_SRC}/src/w_cabsl.c"
"${PROJECT_SRC}/src/s_nextafterl.c"
"${PROJECT_SRC}/src/s_rintl.c"
"${PROJECT_SRC}/src/s_scalbnl.c"
"${PROJECT_SRC}/src/polevll.c"
"${PROJECT_SRC}/src/s_casinl.c"
"${PROJECT_SRC}/src/s_ctanl.c"
"${PROJECT_SRC}/src/s_cimagl.c"
"${PROJECT_SRC}/src/s_conjl.c"
"${PROJECT_SRC}/src/s_creall.c"
"${PROJECT_SRC}/src/s_cacoshl.c"
"${PROJECT_SRC}/src/s_catanhl.c"
"${PROJECT_SRC}/src/s_casinhl.c"
"${PROJECT_SRC}/src/s_catanl.c"
"${PROJECT_SRC}/src/s_csinl.c"
"${PROJECT_SRC}/src/s_cacosl.c"
"${PROJECT_SRC}/src/s_cexpl.c"
"${PROJECT_SRC}/src/s_csinhl.c"
"${PROJECT_SRC}/src/s_ccoshl.c"
"${PROJECT_SRC}/src/s_clogl.c"
"${PROJECT_SRC}/src/s_ctanhl.c"
"${PROJECT_SRC}/src/s_ccosl.c"
"${PROJECT_SRC}/src/s_cbrtl.c"
)
endif()
if (LONG_DOUBLE_NOT_DOUBLE)
if (${OPENLIBM_ARCH_FOLDER} STREQUAL "i387" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "amd64")
list(APPEND OPENLIBM_C_SOURCE
# ld80
"${PROJECT_SRC}/ld80/invtrig.c"
"${PROJECT_SRC}/ld80/e_acoshl.c"
"${PROJECT_SRC}/ld80/e_powl.c"
"${PROJECT_SRC}/ld80/k_tanl.c"
"${PROJECT_SRC}/ld80/s_exp2l.c"
"${PROJECT_SRC}/ld80/e_atanhl.c"
"${PROJECT_SRC}/ld80/e_lgammal_r.c"
"${PROJECT_SRC}/ld80/e_sinhl.c"
"${PROJECT_SRC}/ld80/s_asinhl.c"
"${PROJECT_SRC}/ld80/s_expm1l.c"
"${PROJECT_SRC}/ld80/e_coshl.c"
"${PROJECT_SRC}/ld80/e_log10l.c"
"${PROJECT_SRC}/ld80/e_tgammal.c"
"${PROJECT_SRC}/ld80/e_expl.c"
"${PROJECT_SRC}/ld80/e_log2l.c"
"${PROJECT_SRC}/ld80/k_cosl.c"
"${PROJECT_SRC}/ld80/s_log1pl.c"
"${PROJECT_SRC}/ld80/s_tanhl.c"
"${PROJECT_SRC}/ld80/e_logl.c"
"${PROJECT_SRC}/ld80/k_sinl.c"
"${PROJECT_SRC}/ld80/s_erfl.c"
)
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
list(APPEND OPENLIBM_C_SOURCE
"${PROJECT_SRC}/ld80/s_nanl.c"
)
endif()
else()
if(${OPENLIBM_ARCH_FOLDER} STREQUAL "aarch64")
list(APPEND OPENLIBM_C_SOURCE
# ld128
"${PROJECT_SRC}/ld128/invtrig.c"
"${PROJECT_SRC}/ld128/e_acoshl.c"
"${PROJECT_SRC}/ld128/e_powl.c"
"${PROJECT_SRC}/ld128/k_tanl.c"
"${PROJECT_SRC}/ld128/s_exp2l.c"
"${PROJECT_SRC}/ld128/e_atanhl.c"
"${PROJECT_SRC}/ld128/e_lgammal_r.c"
"${PROJECT_SRC}/ld128/e_sinhl.c"
"${PROJECT_SRC}/ld128/s_asinhl.c"
"${PROJECT_SRC}/ld128/s_expm1l.c"
"${PROJECT_SRC}/ld128/e_coshl.c"
"${PROJECT_SRC}/ld128/e_log10l.c"
"${PROJECT_SRC}/ld128/e_tgammal.c"
"${PROJECT_SRC}/ld128/e_expl.c"
"${PROJECT_SRC}/ld128/e_log2l.c"
"${PROJECT_SRC}/ld128/k_cosl.c"
"${PROJECT_SRC}/ld128/s_log1pl.c"
"${PROJECT_SRC}/ld128/s_tanhl.c"
"${PROJECT_SRC}/ld128/e_logl.c"
"${PROJECT_SRC}/ld128/k_sinl.c"
"${PROJECT_SRC}/ld128/s_erfl.c"
)
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
list(APPEND OPENLIBM_C_SOURCE
"${PROJECT_SRC}/ld128/s_nanl.c"
)
endif()
endif()
endif()
endif()
# Architecture-specific sources
if (${OPENLIBM_ARCH_FOLDER} STREQUAL "amd64")
list(APPEND OPENLIBM_C_SOURCE
"${PROJECT_SRC}/amd64/fenv.c"
)
list(APPEND OPENLIBM_ASM_SOURCE
"${PROJECT_SRC}/amd64/e_remainder.S"
"${PROJECT_SRC}/amd64/e_remainderf.S"
"${PROJECT_SRC}/amd64/e_remainderl.S"
"${PROJECT_SRC}/amd64/e_sqrt.S"
"${PROJECT_SRC}/amd64/e_sqrtf.S"
"${PROJECT_SRC}/amd64/e_sqrtl.S"
"${PROJECT_SRC}/amd64/s_llrint.S"
"${PROJECT_SRC}/amd64/s_llrintf.S"
"${PROJECT_SRC}/amd64/s_llrintl.S"
"${PROJECT_SRC}/amd64/s_logbl.S"
"${PROJECT_SRC}/amd64/s_lrint.S"
"${PROJECT_SRC}/amd64/s_lrintf.S"
"${PROJECT_SRC}/amd64/s_lrintl.S"
"${PROJECT_SRC}/amd64/s_remquo.S"
"${PROJECT_SRC}/amd64/s_remquof.S"
"${PROJECT_SRC}/amd64/s_remquol.S"
"${PROJECT_SRC}/amd64/s_rintl.S"
"${PROJECT_SRC}/amd64/s_scalbn.S"
"${PROJECT_SRC}/amd64/s_scalbnf.S"
"${PROJECT_SRC}/amd64/s_scalbnl.S"
"${PROJECT_SRC}/amd64/e_fmod.S"
"${PROJECT_SRC}/amd64/e_fmodf.S"
"${PROJECT_SRC}/amd64/e_fmodl.S"
)
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "aarch64")
list(APPEND OPENLIBM_C_SOURCE
"${PROJECT_SRC}/aarch64/fenv.c"
)
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "arm")
list(APPEND OPENLIBM_C_SOURCE
"${PROJECT_SRC}/${OPENLIBM_ARCH_FOLDER}/fenv.c"
)
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "i387")
list(APPEND OPENLIBM_C_SOURCE
"${PROJECT_SRC}/i387/fenv.c"
)
list(APPEND OPENLIBM_ASM_SOURCE
"${PROJECT_SRC}/i387/e_exp.S"
"${PROJECT_SRC}/i387/e_fmod.S"
"${PROJECT_SRC}/i387/e_log.S"
"${PROJECT_SRC}/i387/e_log10.S"
"${PROJECT_SRC}/i387/e_remainder.S"
"${PROJECT_SRC}/i387/e_sqrt.S"
"${PROJECT_SRC}/i387/s_ceil.S"
"${PROJECT_SRC}/i387/s_copysign.S"
"${PROJECT_SRC}/i387/s_floor.S"
"${PROJECT_SRC}/i387/s_llrint.S"
"${PROJECT_SRC}/i387/s_logb.S"
"${PROJECT_SRC}/i387/s_lrint.S"
"${PROJECT_SRC}/i387/s_remquo.S"
"${PROJECT_SRC}/i387/s_rint.S"
"${PROJECT_SRC}/i387/s_tan.S"
"${PROJECT_SRC}/i387/s_trunc.S"
# float counterparts
"${PROJECT_SRC}/i387/e_log10f.S"
"${PROJECT_SRC}/i387/e_logf.S"
"${PROJECT_SRC}/i387/e_remainderf.S"
"${PROJECT_SRC}/i387/e_sqrtf.S"
"${PROJECT_SRC}/i387/s_ceilf.S"
"${PROJECT_SRC}/i387/s_copysignf.S"
"${PROJECT_SRC}/i387/s_floorf.S"
"${PROJECT_SRC}/i387/s_llrintf.S"
"${PROJECT_SRC}/i387/s_logbf.S"
"${PROJECT_SRC}/i387/s_lrintf.S"
"${PROJECT_SRC}/i387/s_remquof.S"
"${PROJECT_SRC}/i387/s_rintf.S"
"${PROJECT_SRC}/i387/s_truncf.S"
# long double counterparts
"${PROJECT_SRC}/i387/e_remainderl.S"
"${PROJECT_SRC}/i387/e_sqrtl.S"
"${PROJECT_SRC}/i387/s_ceill.S"
"${PROJECT_SRC}/i387/s_copysignl.S"
"${PROJECT_SRC}/i387/s_floorl.S"
"${PROJECT_SRC}/i387/s_llrintl.S"
"${PROJECT_SRC}/i387/s_logbl.S"
"${PROJECT_SRC}/i387/s_lrintl.S"
"${PROJECT_SRC}/i387/s_remquol.S"
"${PROJECT_SRC}/i387/s_rintl.S"
"${PROJECT_SRC}/i387/s_truncl.S"
)
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
list(APPEND OPENLIBM_ASM_SOURCE
"${PROJECT_SRC}/i387/s_scalbn.S"
"${PROJECT_SRC}/i387/s_scalbnf.S"
"${PROJECT_SRC}/i387/s_scalbnl.S"
)
endif()
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "powerpc")
list(APPEND OPENLIBM_C_SOURCE
"${PROJECT_SRC}/powerpc/fenv.c"
)
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "riscv64")
list(APPEND OPENLIBM_C_SOURCE
"${PROJECT_SRC}/riscv64/fenv.c")
else()
message(FATAL_ERROR "${PROJECT_NAME} CMake build is not set up for ${OPENLIBM_ARCH_FOLDER}")
endif()
# Filter out C implementation from compilation list if a native implementation exists
foreach(FILE_TO_REMOVE ${OPENLIBM_ASM_SOURCE})
# Get filename and strip out extension
cmake_path(GET FILE_TO_REMOVE FILENAME FILENAME_TO_REMOVE)
cmake_path(REMOVE_EXTENSION FILENAME_TO_REMOVE OUTPUT_VARIABLE FILENAME_TO_REMOVE)
message(DEBUG "Filename to remove: ${FILENAME_TO_REMOVE}")
# Go through files and remove one with the same name
foreach(CUR_FILE ${OPENLIBM_C_SOURCE})
cmake_path(GET CUR_FILE FILENAME CUR_FILENAME)
cmake_path(REMOVE_EXTENSION CUR_FILENAME OUTPUT_VARIABLE CUR_FILENAME)
if(${CUR_FILENAME} STREQUAL ${FILENAME_TO_REMOVE})
list(REMOVE_ITEM OPENLIBM_C_SOURCE ${CUR_FILE})
message(DEBUG "Removed source file from compilation list: ${CUR_FILE}")
break()
endif()
endforeach()
endforeach()
# Add sources
target_sources("${PROJECT_NAME}" PRIVATE ${OPENLIBM_C_SOURCE}
${OPENLIBM_ASM_SOURCE}
)
# Include directories
list(APPEND OPENLIBM_INCLUDE_DIRS
"${PROJECT_SRC}"
"${PROJECT_SRC}/include"
"${PROJECT_SRC}/${OPENLIBM_ARCH_FOLDER}"
"${PROJECT_SRC}/src"
)
if(${OPENLIBM_ARCH_FOLDER} STREQUAL "i387" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "amd64" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "powerpc")
list(APPEND OPENLIBM_INCLUDE_DIRS "${PROJECT_SRC}/ld80")
else()
if(${OPENLIBM_ARCH_FOLDER} STREQUAL "aarch64" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "riscv64")
list(APPEND OPENLIBM_INCLUDE_DIRS "${PROJECT_SRC}/ld128")
endif()
endif()
target_include_directories("${PROJECT_NAME}" PUBLIC ${OPENLIBM_INCLUDE_DIRS})
file(GLOB PUBLIC_HEADERS "*.h" "include/*.h" "${OPENLIBM_ARCH_FOLDER}/*.h" "src/*.h")
set_target_properties("${PROJECT_NAME}" PROPERTIES PUBLIC_HEADER "${PUBLIC_HEADERS}")
install (TARGETS "${PROJECT_NAME}")
# Can't use configure_file because openlibm.pc.in uses $var instead of CMake configure @var's
# Would rather string replace variables here instead of editing .pc.in, because editing .pc.in
# might build break autotools build.
file(READ "${PROJECT_SRC}/openlibm.pc.in" PC_FILE)
string(REPLACE "\${version}" ${CMAKE_PROJECT_VERSION} PC_FILE ${PC_FILE})
string(PREPEND PC_FILE "prefix=${CMAKE_INSTALL_PREFIX}
includedir=\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}
libdir=\${prefix}/${CMAKE_INSTALL_LIBDIR}\n
")
file(WRITE "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc" ${PC_FILE})
install(FILES "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")

115
openlibm/LICENSE.md Normal file
View file

@ -0,0 +1,115 @@
## OpenLibm
OpenLibm contains code that is covered by various licenses.
The OpenLibm code derives from the FreeBSD msun and OpenBSD libm
implementations, which in turn derives from FDLIBM 5.3. As a result, it
has a number of fixes and updates that have accumulated over the years
in msun, and also optimized assembly versions of many functions. These
improvements are provided under the BSD and ISC licenses. The msun
library also includes work placed under the public domain, which is
noted in the individual files. Further work on making a standalone
OpenLibm library from msun, as part of the Julia project is covered
under the MIT license. The test files, test-double.c and test-float.c
are under the LGPL.
## Parts copyrighted by the Julia project (MIT License)
> Copyright (c) 2011-14 The Julia Project.
> https://github.com/JuliaMath/openlibm/graphs/contributors
>
> Permission is hereby granted, free of charge, to any person obtaining
> a copy of this software and associated documentation files (the
> "Software"), to deal in the Software without restriction, including
> without limitation the rights to use, copy, modify, merge, publish,
> distribute, sublicense, and/or sell copies of the Software, and to
> permit persons to whom the Software is furnished to do so, subject to
> the following conditions:
>
> The above copyright notice and this permission notice shall be
> included in all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
> LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
> OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
> WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
## Parts copyrighted by Stephen L. Moshier (ISC License)
> Copyright (c) 2008 Stephen L. Moshier <steve@moshier.net>
>
> Permission to use, copy, modify, and distribute this software for any
> purpose with or without fee is hereby granted, provided that the above
> copyright notice and this permission notice appear in all copies.
>
> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
## FREEBSD MSUN (FreeBSD/2-clause BSD/Simplified BSD License)
> Copyright 1992-2011 The FreeBSD Project. All rights reserved.
>
> Redistribution and use in source and binary forms, with or without
> modification, are permitted provided that the following conditions are
> met:
>
> 1. Redistributions of source code must retain the above copyright
> notice, this list of conditions and the following disclaimer.
>
> 2. Redistributions in binary form must reproduce the above copyright
> notice, this list of conditions and the following disclaimer in the
> documentation and/or other materials provided with the distribution.
> THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY
> EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
> PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR
> CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
> EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
> PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
> PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
> LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
> NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
> SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
>
> The views and conclusions contained in the software and documentation
> are those of the authors and should not be interpreted as representing
> official policies, either expressed or implied, of the FreeBSD
> Project.
## FDLIBM
> Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
>
> Developed at SunPro, a Sun Microsystems, Inc. business.
> Permission to use, copy, modify, and distribute this
> software is freely granted, provided that this notice
> is preserved.
## Tests
> Copyright (C) 1997, 1999 Free Software Foundation, Inc.
> This file is part of the GNU C Library.
> Contributed by Andreas Jaeger <aj@suse.de>, 1997.
>
> The GNU C Library is free software; you can redistribute it and/or
> modify it under the terms of the GNU Lesser General Public
> License as published by the Free Software Foundation; either
> version 2.1 of the License, or (at your option) any later version.
>
> The GNU C Library is distributed in the hope that it will be useful,
> but WITHOUT ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> Lesser General Public License for more details.
>
> You should have received a copy of the GNU Lesser General Public
> License along with the GNU C Library; if not, write to the Free
> Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
> 02111-1307 USA.

190
openlibm/Make.inc Normal file
View file

@ -0,0 +1,190 @@
# -*- mode: makefile-gmake -*-
# Default build rule for any Makefile in this project: all
default: all
OS := $(shell uname)
# Do not forget to bump SOMINOR when changing VERSION,
# and SOMAJOR when breaking ABI in a backward-incompatible way
VERSION = 0.8.0
SOMAJOR = 4
SOMINOR = 0
DESTDIR =
prefix ?= /usr/local
bindir ?= $(prefix)/bin
libdir ?= $(prefix)/lib
includedir ?= $(prefix)/include
ifeq ($(OS), FreeBSD)
pkgconfigdir ?= $(prefix)/libdata/pkgconfig
else
pkgconfigdir ?= $(libdir)/pkgconfig
endif
# Build with Code Coverage
# Only test with Ubuntu + gcc + lcov, may not work for other platform.
# deps: https://github.com/linux-test-project/lcov
# You don't need to set this flag manually, `make coverage` will do it for you.
# Just Run: make clean && make coverage -j
CODE_COVERAGE ?= 0
USEGCC ?= 1
USECLANG ?= 0
TOOLPREFIX ?=
ifneq (,$(findstring $(OS),Darwin FreeBSD OpenBSD))
USEGCC ?= 0
USECLANG ?= 1
endif
ifeq ($(ARCH),wasm32)
USECLANG = 1
USEGCC = 0
TOOLPREFIX = llvm-
endif
AR := $(TOOLPREFIX)ar
ifeq ($(USECLANG),1)
USEGCC ?= 0
CC = clang
CFLAGS_add += -fno-builtin -fno-strict-aliasing
endif
ifeq ($(USEGCC),1)
CC := $(TOOLPREFIX)gcc
CFLAGS_add += -fno-gnu89-inline -fno-builtin
endif
ARCH ?= $(shell $(CC) -dumpmachine | sed "s/\([^-]*\).*$$/\1/")
ifeq ($(ARCH),mingw32)
$(error "the mingw32 compiler you are using fails the openblas testsuite. please see the Julia README.windows.md document for a replacement")
endif
# OS-specific stuff
ifeq ($(ARCH),arm64)
override ARCH := aarch64
endif
ifeq ($(findstring arm,$(ARCH)),arm)
override ARCH := arm
MARCH ?= armv7-a+fp
CFLAGS_add += -mhard-float
endif
ifeq ($(findstring powerpc,$(ARCH)),powerpc)
override ARCH := powerpc
endif
ifeq ($(findstring ppc,$(ARCH)),ppc)
override ARCH := powerpc
endif
ifeq ($(findstring s390,$(ARCH)),s390)
override ARCH := s390
endif
ifneq ($(filter $(ARCH),i386 i486 i586 i686 i387 i487 i587 i687),)
override ARCH := i387
MARCH ?= i686
endif
ifeq ($(ARCH),x86_64)
override ARCH := amd64
endif
ifeq ($(findstring mips,$(ARCH)),mips)
override ARCH := mips
endif
ifeq ($(findstring riscv64,$(ARCH)),riscv64)
override ARCH := riscv64
endif
ifeq ($(findstring loongarch64,$(ARCH)),loongarch64)
override ARCH := loongarch64
endif
# If CFLAGS does not contain a -O optimization flag, default to -O3
ifeq ($(findstring -O,$(CFLAGS)),)
CFLAGS_add += -O3
endif
ifneq (,$(findstring MINGW,$(OS)))
override OS=WINNT
endif
#keep these if statements separate
ifeq ($(OS), WINNT)
SHLIB_EXT = dll
SONAME_FLAG =
shlibdir = $(bindir)
else
ifeq ($(OS), Darwin)
SHLIB_EXT = dylib
SONAME_FLAG = -install_name
else
SHLIB_EXT = so
SONAME_FLAG = -soname
endif
CFLAGS_add += -fPIC
shlibdir = $(libdir)
endif
# Add `-march` to our CFLAGS if it's defined
ifneq ($(MARCH),)
CFLAGS_arch += -march=$(MARCH)
endif
ifeq ($(ARCH),i387)
CFLAGS_arch += -m32
SFLAGS_arch += -m32
LDFLAGS_arch += -m32
endif
ifeq ($(ARCH),amd64)
CFLAGS_arch += -m64
SFLAGS_arch += -m64
LDFLAGS_arch += -m64
endif
ifeq ($(ARCH),wasm32)
CFLAGS_arch += -ffreestanding -nostdlib -nostdinc --target=wasm32-unknown-unknown
endif
# Add our "arch"-related FLAGS in. We separate arch-related flags out so that
# we can conveniently get at them for targets that don't want the rest of
# *FLAGS_add, such as the testing Makefile targets
CFLAGS_add += $(CFLAGS_arch)
SFLAGS_add += $(SFLAGS_arch)
LDFLAGS_add += $(LDFLAGS_arch)
CFLAGS_add += -std=c99 -Wall -I$(OPENLIBM_HOME) -I$(OPENLIBM_HOME)/include -I$(OPENLIBM_HOME)/$(ARCH) -I$(OPENLIBM_HOME)/src -DASSEMBLER -D__BSD_VISIBLE -Wno-implicit-function-declaration
ifneq ($(filter $(ARCH),i387 amd64 powerpc),)
CFLAGS_add += -I$(OPENLIBM_HOME)/ld80
else
ifneq ($(filter $(ARCH),aarch64 riscv64),)
CFLAGS_add += -I$(OPENLIBM_HOME)/ld128
endif
endif
ifneq ($(filter $(ARCH),i387 amd64),)
# Determines whether `long double` is the same as `double` on this arch.
# linux x86_64, for instance, `long double` is 80 bits wide, whereas on macOS aarch64,
# `long double` is the same as `double`.
LONG_DOUBLE_NOT_DOUBLE := 1
else ifeq ($(ARCH), aarch64 riscv64)
ifeq ($(filter $(OS),Darwin WINNT),)
LONG_DOUBLE_NOT_DOUBLE := 1
endif
endif
ifeq ($(CODE_COVERAGE),1)
CFLAGS_add += -g -O0 --coverage
LDFLAGS_add += --coverage
endif # CODE_COVERAGE==1
%.c.o: %.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_add) -c $< -o $@
%.S.o: %.S
$(CC) $(CPPFLAGS) $(SFLAGS) $(SFLAGS_add) $(filter -m% -B% -I% -D%,$(CFLAGS_add)) -c $< -o $@
# Makefile debugging trick:
# call print-VARIABLE to see the runtime value of any variable
print-%:
@echo '$*=$($*)'

137
openlibm/Makefile Normal file
View file

@ -0,0 +1,137 @@
OPENLIBM_HOME=$(abspath .)
include ./Make.inc
SUBDIRS = src $(ARCH) bsdsrc
ifeq ($(LONG_DOUBLE_NOT_DOUBLE),1)
# Add ld80 directory on x86 and x64
ifneq ($(filter $(ARCH),i387 amd64),)
SUBDIRS += ld80
else
ifneq ($(filter $(ARCH),aarch64),)
SUBDIRS += ld128
else
endif
endif
endif
define INC_template
TEST=test
override CUR_SRCS = $(1)_SRCS
include $(1)/Make.files
SRCS += $$(addprefix $(1)/,$$($(1)_SRCS))
endef
DIR=test
$(foreach dir,$(SUBDIRS),$(eval $(call INC_template,$(dir))))
DUPLICATE_NAMES = $(filter $(patsubst %.S,%,$($(ARCH)_SRCS)),$(patsubst %.c,%,$(src_SRCS)))
DUPLICATE_SRCS = $(addsuffix .c,$(DUPLICATE_NAMES))
OBJS = $(patsubst %.f,%.f.o,\
$(patsubst %.S,%.S.o,\
$(patsubst %.c,%.c.o,$(filter-out $(addprefix src/,$(DUPLICATE_SRCS)),$(SRCS)))))
# If we're on windows, don't do versioned shared libraries. Also, generate an import library
# for the DLL. If we're on OSX, put the version number before the .dylib. Otherwise,
# put it after.
ifeq ($(OS), WINNT)
OLM_MAJOR_MINOR_SHLIB_EXT := $(SHLIB_EXT)
LDFLAGS_add += -Wl,--out-implib,libopenlibm.$(OLM_MAJOR_MINOR_SHLIB_EXT).a
else
ifeq ($(OS), Darwin)
OLM_MAJOR_MINOR_SHLIB_EXT := $(SOMAJOR).$(SOMINOR).$(SHLIB_EXT)
OLM_MAJOR_SHLIB_EXT := $(SOMAJOR).$(SHLIB_EXT)
else
OLM_MAJOR_MINOR_SHLIB_EXT := $(SHLIB_EXT).$(SOMAJOR).$(SOMINOR)
OLM_MAJOR_SHLIB_EXT := $(SHLIB_EXT).$(SOMAJOR)
endif
LDFLAGS_add += -Wl,$(SONAME_FLAG),libopenlibm.$(OLM_MAJOR_SHLIB_EXT)
endif
.PHONY: all check test clean distclean \
install install-static install-shared install-pkgconfig install-headers
OLM_LIBS := libopenlibm.a
ifneq ($(ARCH), wasm32)
OLM_LIBS += libopenlibm.$(OLM_MAJOR_MINOR_SHLIB_EXT)
endif
all : $(OLM_LIBS)
check test: test/test-double test/test-float
test/test-double
test/test-float
libopenlibm.a: $(OBJS)
$(AR) -rcs libopenlibm.a $(OBJS)
libopenlibm.$(OLM_MAJOR_MINOR_SHLIB_EXT): $(OBJS)
$(CC) -shared $(OBJS) $(LDFLAGS) $(LDFLAGS_add) -o $@
ifneq ($(OS),WINNT)
ln -sf $@ libopenlibm.$(OLM_MAJOR_SHLIB_EXT)
ln -sf $@ libopenlibm.$(SHLIB_EXT)
endif
test/test-double: libopenlibm.$(OLM_MAJOR_MINOR_SHLIB_EXT)
$(MAKE) -C test test-double
test/test-float: libopenlibm.$(OLM_MAJOR_MINOR_SHLIB_EXT)
$(MAKE) -C test test-float
COVERAGE_DIR:=cov-html
COVERAGE_FILE:=$(COVERAGE_DIR)/libopenlibm.info
# Gen cov report with: make clean && make coverage -j
coverage: clean-coverage
mkdir $(COVERAGE_DIR)
$(MAKE) test CODE_COVERAGE=1
lcov -d amd64 -d bsdsrc -d ld80 -d src \
--rc lcov_branch_coverage=1 --capture --output-file $(COVERAGE_FILE)
genhtml --legend --branch-coverage \
--title "Openlibm commit `git rev-parse HEAD`" \
--output-directory $(COVERAGE_DIR)/ \
$(COVERAGE_FILE)
# Zero coverage statistics and Delete report
clean-coverage:
-lcov -d amd64 -d bsdsrc -d ld80 -d src --zerocounters
rm -f ./*/*.gcda
rm -rf $(COVERAGE_DIR)/
clean: clean-coverage
rm -f aarch64/*.o amd64/*.o arm/*.o bsdsrc/*.o i387/*.o loongarch64/*.o ld80/*.o ld128/*.o src/*.o powerpc/*.o mips/*.o s390/*.o riscv64/*.o
rm -f libopenlibm.a libopenlibm.*$(SHLIB_EXT)*
rm -f ./*/*.gcno
$(MAKE) -C test clean
openlibm.pc: openlibm.pc.in Make.inc Makefile
echo "version=${VERSION}" > openlibm.pc
echo "libdir=$(DESTDIR)$(libdir)" >> openlibm.pc
echo "includedir=$(DESTDIR)$(includedir)/openlibm" >> openlibm.pc
cat openlibm.pc.in >> openlibm.pc
install-static: libopenlibm.a
mkdir -p $(DESTDIR)$(libdir)
cp -RpP -f libopenlibm.a $(DESTDIR)$(libdir)/
install-shared: libopenlibm.$(OLM_MAJOR_MINOR_SHLIB_EXT)
mkdir -p $(DESTDIR)$(shlibdir)
ifeq ($(OS), WINNT)
mkdir -p $(DESTDIR)$(libdir)
cp -RpP -f libopenlibm.*$(SHLIB_EXT) $(DESTDIR)$(shlibdir)/
cp -RpP -f libopenlibm.*$(SHLIB_EXT).a $(DESTDIR)$(libdir)/
else
cp -RpP -f libopenlibm.*$(SHLIB_EXT)* $(DESTDIR)$(shlibdir)/
endif
install-pkgconfig: openlibm.pc
mkdir -p $(DESTDIR)$(pkgconfigdir)
cp -RpP -f openlibm.pc $(DESTDIR)$(pkgconfigdir)/
install-headers:
mkdir -p $(DESTDIR)$(includedir)/openlibm
cp -RpP -f include/*.h $(DESTDIR)$(includedir)/openlibm
cp -RpP -f src/*.h $(DESTDIR)$(includedir)/openlibm
install: install-static install-shared install-pkgconfig install-headers

71
openlibm/README.md Normal file
View file

@ -0,0 +1,71 @@
# OpenLibm
[![codecov](https://codecov.io/gh/JuliaMath/openlibm/graph/badge.svg?token=eTAdN7d9cg)](https://codecov.io/gh/JuliaMath/openlibm)
[OpenLibm](https://openlibm.org/) is an effort to have a high quality, portable, standalone
C mathematical library ([`libm`](http://en.wikipedia.org/wiki/libm)).
It can be used standalone in applications and programming language
implementations.
The project was born out of a need to have a good `libm` for the
[Julia programming language](http://www.julialang.org) that worked
consistently across compilers and operating systems, and in 32-bit and
64-bit environments.
## Platform support
OpenLibm builds on Linux, macOS, Windows, FreeBSD, OpenBSD, NetBSD, and
DragonFly BSD. It builds with both GCC and clang. Although largely
tested and widely used on the x86 and x86-64 architectures, OpenLibm
also supports arm, aarch64, ppc64le, mips, wasm32, riscv, s390(x) and
loongarch64.
## Build instructions
### GNU Make
1. Use GNU Make to build OpenLibm. This is `make` on most systems, but `gmake` on BSDs.
2. Use `make USEGCC=1` to build with GCC. This is the default on
Linux and Windows.
3. Use `make USECLANG=1` to build with clang. This is the default on OS X, FreeBSD,
and OpenBSD.
4. Use `make ARCH=wasm32` to build the wasm32 library with clang.
5. Architectures are auto-detected. Use `make ARCH=i386` to force a
build for i386. Other supported architectures are i486, i586, and
i686. GCC 4.8 is the minimum requirement for correct codegen on
older 32-bit architectures.
**Cross Build**
Take `riscv64` as example:
1. install `qemu-riscv64-static`, `gcc-riscv64-linux-gnu`
2. Cross build:
```sh
ARCH=riscv64
TRIPLE=$ARCH-linux-gnu
make ARCH=$ARCH TOOLPREFIX=$TRIPLE- -j
make -C test ARCH=$ARCH TOOLPREFIX=$TRIPLE- -j
```
3. Run test with qemu:
```sh
qemu-$ARCH-static -L . -L /usr/$TRIPLE/ test/test-float
qemu-$ARCH-static -L . -L /usr/$TRIPLE/ test/test-double
```
### CMake
1. Create build directory with `mkdir build` and navigate into it with `cd build`.
2. Run CMake to configure project and generate native build system with `cmake /path/to/openlibm/`
or generate project with build system of choice e.g. `cmake /path/to/openlib/ -G "MinGW Makefiles"`.
3. Build with the build system with `cmake --build .`.
Default CMake configuration builds a shared library, this can easily be configured using
[BUILD_SHARED_LIBS](https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html)
configuration option.
## Acknowledgements
PowerPC support for openlibm was graciously sponsored by IBM.

View file

@ -0,0 +1 @@
$(CUR_SRCS) = fenv.c

51
openlibm/aarch64/fenv.c Normal file
View file

@ -0,0 +1,51 @@
/*-
* Copyright (c) 2004 David Schultz <das@FreeBSD.ORG>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD: src/lib/msun/arm/fenv.c,v 1.3 2011/10/16 05:37:56 das Exp $
*/
#include <openlibm_fenv.h>
#ifdef __GNUC_GNU_INLINE__
#error "This file must be compiled with C99 'inline' semantics"
#endif
/*
* Hopefully the system ID byte is immutable, so it's valid to use
* this as a default environment.
*/
const fenv_t __fe_dfl_env = 0;
extern inline int feclearexcept(int __excepts);
extern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts);
extern inline int fesetexceptflag(const fexcept_t *__flagp, int __excepts);
extern inline int feraiseexcept(int __excepts);
extern inline int fetestexcept(int __excepts);
extern inline int fegetround(void);
extern inline int fesetround(int __round);
extern inline int fegetenv(fenv_t *__envp);
extern inline int feholdexcept(fenv_t *__envp);
extern inline int fesetenv(const fenv_t *__envp);
extern inline int feupdateenv(const fenv_t *__envp);

Some files were not shown because too many files have changed in this diff Show more