diff --git a/native/src/crt0/nolibc.c b/native/src/crt0/nolibc.c index d220227ca..9df3cbfa8 100644 --- a/native/src/crt0/nolibc.c +++ b/native/src/crt0/nolibc.c @@ -1,6 +1,5 @@ #include "nolibc/crt.h" #include "nolibc/arch.h" -#include "nolibc/stdio.h" // errno diff --git a/native/src/crt0/nolibc/crt.h b/native/src/crt0/nolibc/crt.h index 21d5cb2e8..ccbe2ea17 100644 --- a/native/src/crt0/nolibc/crt.h +++ b/native/src/crt0/nolibc/crt.h @@ -10,6 +10,7 @@ char **environ; const unsigned long *_auxv; void _exit(int); +void __init_stdio(void); typedef void init_func_t(int, char*[], char*[]); typedef void fini_func_t(void); @@ -68,6 +69,7 @@ void __attribute__((used)) _start_c(long *sp) _auxv = auxv; /* call preinit and init */ + __init_stdio(); call_array(__preinit_array_start, __preinit_array_end, argc, argv, envp); call_array(__init_array_start, __init_array_end, argc, argv, envp); diff --git a/native/src/crt0/nolibc/stdio.h b/native/src/crt0/nolibc/stdio.h deleted file mode 100644 index 872f82625..000000000 --- a/native/src/crt0/nolibc/stdio.h +++ /dev/null @@ -1,250 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ -/* - * minimal stdio function definitions for NOLIBC - * Copyright (C) 2017-2021 Willy Tarreau - */ - -#ifndef _NOLIBC_STDIO_H -#define _NOLIBC_STDIO_H - -#include -#include -#include -#include -#include - -#ifndef EOF -#define EOF (-1) -#endif - -/* Buffering mode used by setvbuf. */ -#define _IOFBF 0 /* Fully buffered. */ -#define _IOLBF 1 /* Line buffered. */ -#define _IONBF 2 /* No buffering. */ - -/* just define FILE as a non-empty type. The value of the pointer gives - * the FD: FILE=~fd for fd>=0 or NULL for fd<0. This way positive FILE - * are immediately identified as abnormal entries (i.e. possible copies - * of valid pointers to something else). - */ -typedef struct __sFILE { - char dummy[1]; -} FILE; - -extern int vfprintf(FILE *stream, const char *fmt, va_list args); - -FILE* stdin = (FILE*)(intptr_t)~STDIN_FILENO; -FILE* stdout = (FILE*)(intptr_t)~STDOUT_FILENO; -FILE* stderr = (FILE*)(intptr_t)~STDERR_FILENO; - -/* provides a FILE* equivalent of fd. The mode is ignored. */ -FILE *fdopen(int fd, const char *mode __attribute__((unused))) -{ - if (fd < 0) { - errno = EBADF; - return NULL; - } - return (FILE*)(intptr_t)~fd; -} - -/* provides the fd of stream. */ -int fileno(FILE *stream) -{ - intptr_t i = (intptr_t)stream; - - if (i >= 0) { - errno = EBADF; - return -1; - } - return ~i; -} - -/* flush a stream. */ -int fflush(FILE *stream) -{ - intptr_t i = (intptr_t)stream; - - /* NULL is valid here. */ - if (i > 0) { - errno = EBADF; - return -1; - } - - /* Don't do anything, nolibc does not support buffering. */ - return 0; -} - -/* flush a stream. */ -int fclose(FILE *stream) -{ - intptr_t i = (intptr_t)stream; - - if (i >= 0) { - errno = EBADF; - return -1; - } - - if (close(~i)) - return EOF; - - return 0; -} - -/* getc(), fgetc(), getchar() */ - -#define getc(stream) fgetc(stream) - -int fgetc(FILE* stream) -{ - unsigned char ch; - - if (read(fileno(stream), &ch, 1) <= 0) - return EOF; - return ch; -} - -int getchar(void) -{ - return fgetc(stdin); -} - - -/* putc(), fputc(), putchar() */ - -#define putc(c, stream) fputc(c, stream) - -int fputc(int c, FILE* stream) -{ - unsigned char ch = c; - - if (write(fileno(stream), &ch, 1) <= 0) - return EOF; - return ch; -} - -int putchar(int c) -{ - return fputc(c, stdout); -} - - -/* fwrite(), puts(), fputs(). Note that puts() emits '\n' but not fputs(). */ - -/* internal fwrite()-like function which only takes a size and returns 0 on - * success or EOF on error. It automatically retries on short writes. - */ -static int _fwrite(const void *buf, size_t size, FILE *stream) -{ - ssize_t ret; - int fd = fileno(stream); - - while (size) { - ret = write(fd, buf, size); - if (ret <= 0) - return EOF; - size -= ret; - buf += ret; - } - return 0; -} - -size_t fwrite(const void *s, size_t size, size_t nmemb, FILE *stream) -{ - size_t written; - - for (written = 0; written < nmemb; written++) { - if (_fwrite(s, size, stream) != 0) - break; - s += size; - } - return written; -} - -int fputs(const char *s, FILE *stream) -{ - return _fwrite(s, strlen(s), stream); -} - -int puts(const char *s) -{ - if (fputs(s, stdout) == EOF) - return EOF; - return putchar('\n'); -} - - -/* fgets() */ -char *fgets(char *s, int size, FILE *stream) -{ - int ofs; - int c; - - for (ofs = 0; ofs + 1 < size;) { - c = fgetc(stream); - if (c == EOF) - break; - s[ofs++] = c; - if (c == '\n') - break; - } - if (ofs < size) - s[ofs] = 0; - return ofs ? s : NULL; -} - -int vprintf(const char *fmt, va_list args) -{ - return vfprintf(stdout, fmt, args); -} - -__attribute__((format(printf, 2, 3))) -int fprintf(FILE *stream, const char *fmt, ...) -{ - va_list args; - int ret; - - va_start(args, fmt); - ret = vfprintf(stream, fmt, args); - va_end(args); - return ret; -} - -__attribute__((format(printf, 1, 2))) -int printf(const char *fmt, ...) -{ - va_list args; - int ret; - - va_start(args, fmt); - ret = vfprintf(stdout, fmt, args); - va_end(args); - return ret; -} - -void perror(const char *msg) -{ - fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno); -} - -int setvbuf(FILE *stream __attribute__((unused)), - char *buf __attribute__((unused)), - int mode, - size_t size __attribute__((unused))) -{ - /* - * nolibc does not support buffering so this is a nop. Just check mode - * is valid as required by the spec. - */ - switch (mode) { - case _IOFBF: - case _IOLBF: - case _IONBF: - break; - default: - return EOF; - } - - return 0; -} - -#endif /* _NOLIBC_STDIO_H */ diff --git a/native/src/crt0/stdio.c b/native/src/crt0/stdio.c index 708faf5a9..c3b73f05c 100644 --- a/native/src/crt0/stdio.c +++ b/native/src/crt0/stdio.c @@ -1,6 +1,127 @@ #include #include #include +#include + +typedef struct file_ptr_t { + int fd; + void *cookie; + int (*read_fn)(void*, char*, int); + int (*write_fn)(void*, const char*, int); + fpos_t (*seek_fn)(void*, fpos_t, int); + int (*close_fn)(void*); +} file_ptr_t; + +static int fp_read_fn(void *p, char *buf, int sz) { + intptr_t fd = (intptr_t) p; + return read(fd, buf, sz); +} + +static int fp_write_fn(void *p, const char *buf, int sz) { + intptr_t fd = (intptr_t) p; + return write(fd, buf, sz); +} + +static fpos_t fp_seek_fn(void *p, fpos_t pos, int whence) { + intptr_t fd = (intptr_t) p; + return lseek(fd, pos, whence); +} + +static int fp_close_fn(void *p) { + intptr_t fd = (intptr_t) p; + return close(fd); +} + +static void set_fp_fd(file_ptr_t *fp, int fd) { + fp->fd = fd; + fp->cookie = NULL; + fp->read_fn = fp_read_fn; + fp->write_fn = fp_write_fn; + fp->seek_fn = fp_seek_fn; + fp->close_fn = fp_close_fn; +} + +static file_ptr_t __stdio_fp[3]; + +FILE* stdin = (FILE *) &__stdio_fp[0]; +FILE* stdout = (FILE *) &__stdio_fp[1]; +FILE* stderr = (FILE *) &__stdio_fp[2]; + +void __init_stdio(void) { + set_fp_fd((file_ptr_t *) stdin, 0); + set_fp_fd((file_ptr_t *) stdout, 1); + set_fp_fd((file_ptr_t *) stderr, 2); +} + +FILE *fdopen(int fd, const char *mode __attribute__((unused))) { + file_ptr_t *fp = malloc(sizeof(file_ptr_t)); + set_fp_fd(fp, fd); + return (FILE *) fp; +} + +FILE *funopen(const void* cookie, + int (*read_fn)(void*, char*, int), + int (*write_fn)(void*, const char*, int), + fpos_t (*seek_fn)(void*, fpos_t, int), + int (*close_fn)(void*)) { + file_ptr_t *fp = malloc(sizeof(file_ptr_t)); + fp->fd = -1; + fp->cookie = (void *) cookie; + fp->read_fn = read_fn; + fp->write_fn = write_fn; + fp->seek_fn = seek_fn; + fp->close_fn = close_fn; + return (FILE *) fp; +} + +#define fn_arg (fp->fd > 0 ? (void*)(intptr_t) fp->fd : fp->cookie) + +int fclose(FILE *stream) { + file_ptr_t *fp = (file_ptr_t *) stream; + int ret = fp->close_fn(fn_arg); + free(stream); + return ret; +} + +int fileno(FILE *stream) { + file_ptr_t *fp = (file_ptr_t *) stream; + return fp->fd; +} + +int fputc(int ch, FILE *stream) { + char c = ch; + file_ptr_t *fp = (file_ptr_t *) stream; + return fp->write_fn(fn_arg, &c, 1) >= 0 ? 0 : EOF; +} + +size_t fwrite(const void* buf, size_t size, size_t count, FILE* stream) { + file_ptr_t *fp = (file_ptr_t *) stream; + int ret = fp->write_fn(fn_arg, buf, size * count); + return ret >= 0 ? ret : 0; +} + +int fputs(const char* s, FILE* stream) { + file_ptr_t *fp = (file_ptr_t *) stream; + size_t length = strlen(s); + return fp->write_fn(fn_arg, s, length) == length ? 0 : EOF; +} + +int fgetc(FILE *stream) { + char ch; + file_ptr_t *fp = (file_ptr_t *) stream; + if (fp->read_fn(fn_arg, &ch, 1) == 1) { + return ch; + } + return -1; +} + +size_t fread(void *buf, size_t size, size_t count, FILE* stream) { + file_ptr_t *fp = (file_ptr_t *) stream; + int ret = fp->read_fn(fn_arg, buf, size * count); + return ret >= 0 ? ret : 0; +} + +void setbuf(FILE* fp, char* buf) {} #include "tinystdio/tinystdio.c" @@ -33,6 +154,30 @@ int vasprintf(char **strp, const char *fmt, va_list ap) { return size; } +int vprintf(const char *fmt, va_list args) { + return vfprintf(stdout, fmt, args); +} + +int fprintf(FILE *stream, const char *fmt, ...) { + va_list args; + int ret; + + va_start(args, fmt); + ret = vfprintf(stream, fmt, args); + va_end(args); + return ret; +} + +int printf(const char *fmt, ...) { + va_list args; + int ret; + + va_start(args, fmt); + ret = vfprintf(stdout, fmt, args); + va_end(args); + return ret; +} + int sscanf(const char *str, const char *format, ...) { va_list ap; int retval; @@ -42,3 +187,42 @@ int sscanf(const char *str, const char *format, ...) { va_end(ap); return retval; } + +// Original source: https://github.com/freebsd/freebsd/blob/master/contrib/file/src/getline.c +// License: BSD, full copyright notice please check original source +ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp) { + char *ptr, *eptr; + + if (*buf == NULL || *bufsiz == 0) { + *bufsiz = BUFSIZ; + if ((*buf = (char *) malloc(*bufsiz)) == NULL) + return -1; + } + + for (ptr = *buf, eptr = *buf + *bufsiz;;) { + int c = fgetc(fp); + if (c == -1) { + return ptr == *buf ? -1 : ptr - *buf; + } + *ptr++ = c; + if (c == delimiter) { + *ptr = '\0'; + return ptr - *buf; + } + if (ptr + 2 >= eptr) { + char *nbuf; + size_t nbufsiz = *bufsiz * 2; + ssize_t d = ptr - *buf; + if ((nbuf = (char *) realloc(*buf, nbufsiz)) == NULL) + return -1; + *buf = nbuf; + *bufsiz = nbufsiz; + eptr = nbuf + nbufsiz; + ptr = nbuf + d; + } + } +} + +ssize_t getline(char **buf, size_t *bufsiz, FILE *fp) { + return getdelim(buf, bufsiz, '\n', fp); +}