This commit is contained in:
pommicket 2022-02-23 22:37:01 -08:00
parent 9bc8a11afe
commit c75af0c8e5
28 changed files with 711 additions and 64 deletions

View file

@ -1,42 +1,42 @@
# [bootstrap](../README.md) stage 05 # [bootstrap](../README.md) stage 05
This stage consists of a C compiler capable of compiling TCC (after some modifications This stage consists of a C compiler capable of compiling tcc (after some modifications
to TCC's source code). to tcc's source code).
Run Run
``` ```
$ make $ make
``` ```
to build our C compiler and TCC. This will take some time (approx. 25 seconds on my computer). to build our C compiler and tcc. This will take some time (approx. 25 seconds on my computer).
This also compiles a "Hello, world!" executable, `a.out`, with our compiler. This also compiles a "Hello, world!" executable, `a.out`, with our compiler.
We can now compile TCC with itself. But first, you'll need to install the header files and library files We can now compile tcc with itself. But first, you'll need to install the header files and library files
which are needed to compile (almost) any program with TCC: which are needed to compile (almost) any program with tcc:
``` ```
$ sudo make install-tcc0 $ sudo make install-tcc0
``` ```
The files will be installed to `/usr/local/lib/tcc-bootstrap`. If you want to change this, make sure to change The files will be installed to `/usr/local/lib/tcc-bootstrap`. If you want to change this, make sure to change
both the `TCCINST` variable in the makefile, and the `CONFIG_TCCDIR` macro in `config.h`. both the `TCCINST` variable in the makefile, and the `CONFIG_TCCDIR` macro in `tcc-0.9.27/config.h`.
Anyways, once this installation is done, you should be able to compile any C program with `tcc-0.9.27/tcc0`, Anyways, once this installation is done, you should be able to compile any C program with `tcc-0.9.27/tcc0`,
including TCC itself: including tcc itself:
``` ```
$ cd tcc-0.9.27 $ cd tcc-0.9.27
$ ./tcc0 tcc.c -o tcc1 $ ./tcc0 tcc.c -o tcc1
``` ```
Now, let's try doing the same thing, but starting with GCC instead of our C compiler: Now, let's try doing the same thing, but starting with gcc instead of our C compiler:
``` ```
$ gcc tcc.c -o tcc0a $ gcc tcc.c -o tcc0a
$ ./tcc0a tcc.c -o tcc1a $ ./tcc0a tcc.c -o tcc1a
``` ```
In theory, these should produce the same files, since the output of TCC shouldn't depend on which compiler it was compiled with. In theory, these should produce the same files, since the output of tcc shouldn't depend on which compiler it was compiled with.
If they are different, then perhaps a bug *was* introduced in some early version of GCC, and replicated in all C compilers since then! If they are different, then perhaps a bug *was* introduced in some early version of gcc, and replicated in all C compilers since then!
Well, only one way to find out: Well, only one way to find out:
``` ```
@ -53,9 +53,9 @@ $ diff tcc2 tcc1a
$ $
``` ```
Yes, after compiling TCC with itself one more time, we get the same executable as the GCC-TCC one. Yes, after compiling tcc with itself one more time, we get the same executable as the gcc-tcc one.
I'm not sure why `tcc1` differs from `tcc2`, but there you go. Turns out there isn't some malicious I'm not sure why `tcc1` differs from `tcc2`, but there you go. Turns out there isn't some malicious
self-replicating code hiding in GCC after all.\* self-replicating code hiding in gcc after all.\*
## the C compiler ## the C compiler
@ -69,7 +69,7 @@ idents.b - functions for creating mappings from identifiers to arbitrary 6
preprocess.b - preprocesses C files preprocess.b - preprocesses C files
tokenize.b - turns preprocessing tokens into tokens (see explanation below) tokenize.b - turns preprocessing tokens into tokens (see explanation below)
parse.b - turns tokens into a nice representation of the program parse.b - turns tokens into a nice representation of the program
codegen.b - turns parse.b's representation into actual code codegen.b - turns parse.b's representation into CPU instructions
main.b - puts everything together main.b - puts everything together
``` ```
@ -290,7 +290,7 @@ Here is a (probably incomplete) list of things we do wrong:
- You can't have a variable/function/etc. called `defined`. - You can't have a variable/function/etc. called `defined`.
- Various little things about when macros are evaluated in some contexts. - Various little things about when macros are evaluated in some contexts.
- The horrible, horrible function `setjmp`, which surely no one uses, is not properly supported. - The horrible, horrible function `setjmp`, which surely no one uses, is not properly supported.
Oh wait, TCC uses it. Fortunately it's not critically important to TCC. Oh wait, tcc uses it. Fortunately it's not critically important to tcc.
- Wide characters and wide character strings are not supported. - Wide characters and wide character strings are not supported.
- The `localtime()` function assumes you are in the UTC+0 timezone. - The `localtime()` function assumes you are in the UTC+0 timezone.
- `mktime()` always fails. - `mktime()` always fails.
@ -317,17 +317,16 @@ rounds down, but
0.09999999999999999861222121921855432447046041488647460937501 0.09999999999999999861222121921855432447046041488647460937501
rounds up. rounds up.
``` ```
Good luck writing a function which handles that! Good luck writing code which handles that!
- Originally, there was a bug where negative powers of 2 were - Originally, there was a bug where negative powers of 2 were
being interpreted as half of their actual value, e.g. `x = 0.25;` would set `x` to being interpreted as half of their actual value, e.g. `x = 0.25;` would set `x` to
`0.125`, but `x = 4;`, `x = 0.3;`, etc. would all work just fine. `0.125`, but `x = 4;`, `x = 0.3;`, etc. would all work just fine.
- Writing the functions in `math.h`, although probably not necessary for compiling TCC, - Writing the functions in `math.h`, although probably not necessary for compiling tcc,
was fun! There are quite a few interesting optimizations you can make, and little was fun! There are quite a few interesting optimizations you can make, and little
tricks for avoiding losses in floating-point accuracy. tricks for avoiding losses in floating-point accuracy.
- The <s>first</s> second non-trivial program I successfully compiled worked perfectly the first time I ran it! - A very difficult to track down bug happened the first time I ran tcc: there was a declaration along
- A very difficult to track down bug happened the first time I ran `tcc`: there was a declaration along
the lines of `char x[] = "a\0b\0c";` but it got compiled as `char x[] = "a";`! the lines of `char x[] = "a\0b\0c";` but it got compiled as `char x[] = "a";`!
- Originally, I was just treating labels the same as any other statements, but `tcc` actually has code like: - Originally, I was just treating labels the same as any other statements, but tcc actually has code like:
``` ```
... ...
goto lbl; goto lbl;
@ -337,7 +336,7 @@ if (some_condition)
``` ```
so the `do_something();` was not being considered as part of the `if` statement. so the `do_something();` was not being considered as part of the `if` statement.
- The first time I compiled tcc with itself (and then with itself again), I actually got a different - The first time I compiled tcc with itself (and then with itself again), I actually got a different
executable from the GCC one. After spending a long time looking at disassemblies, I found the culprit: executable from the gcc one. After spending a long time looking at disassemblies, I found the culprit:
``` ```
# if defined(__linux__) # if defined(__linux__)
tcc_define_symbol(s, "__linux__", NULL); tcc_define_symbol(s, "__linux__", NULL);
@ -345,8 +344,9 @@ executable from the GCC one. After spending a long time looking at disassemblies
# endif # endif
``` ```
If the `__linux__` macro is defined (to indicate that the target OS is linux), If the `__linux__` macro is defined (to indicate that the target OS is linux),
TCC will also define the `__linux__` macro. Unlike GCC, our compiler doesn't define the `__linux__` macro, tcc will also define the `__linux__` macro in any programs it compiles.
so when it's used to compile TCC, TCC won't define it either, no matter how many times you compile it Unlike gcc, our compiler doesn't define the `__linux__` macro,
so when it's used to compile tcc, tcc won't define it either, no matter how many times you compile it
with itself! with itself!
## modifications of tcc's source code ## modifications of tcc's source code
@ -359,7 +359,7 @@ here.
- First, we (and C89) don't allow a comma after the last member in an initializer. In several places, - First, we (and C89) don't allow a comma after the last member in an initializer. In several places,
the last comma in an initializer/enum definition was removed, or an irrelevant entry was added to the end. the last comma in an initializer/enum definition was removed, or an irrelevant entry was added to the end.
- Global variables were sometimes declared twice, which we don't support. - Global variables were sometimes declared twice, which we don't support.
So, a bunch of duplicate declarations were removed. So a bunch of duplicate declarations were removed.
- The `# if defined(__linux__)` and `# endif` mentioned above were removed. - The `# if defined(__linux__)` and `# endif` mentioned above were removed.
- In a bunch of places, `ELFW(something)` had to be replaced with `ELF64_something` due to - In a bunch of places, `ELFW(something)` had to be replaced with `ELF64_something` due to
subtleties of how we evaluate macros. subtleties of how we evaluate macros.
@ -368,12 +368,12 @@ some initializers were replaced by functions called at the top of `main`.
- In several places, `default:` had to be moved to after every `case` label. - In several places, `default:` had to be moved to after every `case` label.
- In two places, `-some_long_double_expression` had to be replaced with - In two places, `-some_long_double_expression` had to be replaced with
a function call to `negate_ld` (a function I wrote for negating long doubles). a function call to `negate_ld` (a function I wrote for negating long doubles).
This is because TCC only supports negating long doubles if This is because tcc only supports negating long doubles if
the compiler used to compile it has an 80-bit long double type, which our compiler doesn't. the compiler which compiled it has an 80-bit long double type, and our compiler doesn't.
- `\0` was replaced with `\n` as a separator for keyword names. - `\0` was replaced with `\n` as a separator for keyword names in the `tcc_keywords` global variable.
- Forced TCC to use `R_X86_64_PC32` relocations, because its `plt` code doesn't seem to work for static - Forced tcc to use `R_X86_64_PC32` relocations, because its `plt` code doesn't seem to work for static
executables. executables.
- Lastly, there's the `config.h` file, which is normally produced by TCC's `configure` script, - Lastly, there's the `config.h` file, which is normally produced by tcc's `configure` script,
but it's easy to write one manually: but it's easy to write one manually:
``` ```
#define TCC_VERSION "0.9.27" #define TCC_VERSION "0.9.27"
@ -386,30 +386,62 @@ but it's easy to write one manually:
``` ```
The last line causes the `inline` keyword (added in C99) to be ignored. The last line causes the `inline` keyword (added in C99) to be ignored.
Fewer changes would've been needed for an older version of TCC, but older versions didn't support
x86-64 assembly, which might end up being relevant...
## \*libc ## \*libc
If you look in TCC's source code, you will not find implementations of any of the C standard library functions. If you look in tcc's source code, you will not find implementations of any of the C standard library functions.
So how can programs compiled with TCC use those functions? So how can programs compiled with tcc use those functions?
When a program compiled with TCC (under default settings) calls `printf`, say, it actually gets the instructions When a program compiled with tcc (under default settings) calls `printf`, say, it actually gets the instructions
for `printf` from a separate library file for `printf` from a separate library file
(called something like `/usr/lib/x86_64-linux-gnu/libc-2.31.so`). There are very good reasons for this: for example, (called something like `/usr/lib/x86_64-linux-gnu/libc-2.31.so`). There are very good reasons for this: for example,
if there a security bug were found in `printf`, it would be much easier to replace the library file than re-compile if there a security bug were found in `printf`, it would be much easier to replace the library file than re-compile
every program which uses `printf`. every program which uses `printf`.
Now this library file is itself compiled from C source files (typically glibc). Now this library file is itself compiled from C source files (typically glibc).
So, we can't really say that the self-compiled TCC was built from scratch, and there could be malicious So, we can't really say that the self-compiled tcc was built from scratch, and there could be malicious
self-replicating code in glibc. self-replicating code in glibc.
You can't compile glibc with TCC, but ### compiling glibc
it's possible to build an old version of `musl`, an alternate libc
You can't compile glibc with tcc, but
it's possible to build an old version of musl, an alternate libc
(you can run `CC=../tcc-0.9.27/tcc0 make` in the `musl-0.6.0` directory here). (you can run `CC=../tcc-0.9.27/tcc0 make` in the `musl-0.6.0` directory here).
You should be able to use musl alongside TCC to build an old version of GCC (git revision You should be able to use musl alongside tcc to build an old version of gcc. This also requires
`79a6d9b7ff3822675ee44d8d6cad86027dadd664` seems workable). This also requires building several tools needed to compile gcc. You should then be able to build an old version of
building several tools needed to compile GCC. You should then be able to build (possibly an old version of) glibc, and with that, a modern version of gcc.
glibc, and with that, a modern version of GCC.
This is all extremely tedious, though, so I'm not planning on doing it anytime soon. Well, I tried this. And it is an absolute nightmare.
GNU has created a horrible web of programs that all depend on each other.
According to the recommended build process, you need awk to build awk, sed to build sed,
sed to build grep, etc. Here was a "guide" I was starting to write for how to
get to glibc:
- install tcc, musl
- build mrsh, make, basic utilities
- chroot
- build & install coreutils
- build & install dash
- build & install sed-4.2
- build & install ld, as (from binutils)
- build gcc
- build & install grep-3.7
- build & install awk
- build & install bash
- build & install glibc (didn't work)
Each of these programs uses a `./configure` script to set up the code and Makefiles.
These scripts are basically impossible to use without already having
most of these programs. So, I resorted to configuring the build with
the ordinary binary versions of `sed`, etc. I had on my machine.
This made broken Makefiles which I spent hours editing by hand
-- and is it really compiled from scratch if it's built from
computer-generated source files and Makefiles?
And although the developers at GNU
refrain from declaring variables after statements, and keep old-style function declarations
to support compilers from the 80s; they *still* manage to use gcc-specific extensions, and
not even extensions that all versions of gcc support!
After hours and hours of fixing compiler errors, I decided to give up.
THIS WAY LIES MADNESS.

View file

@ -1,2 +1,3 @@
*.o *.o
*.a *.a
musl-gcc

View file

@ -8,6 +8,7 @@
# Do not make changes here. # Do not make changes here.
# #
CC = ../tcc-0.9.27/tcc0
exec_prefix = /usr/local/musl-bootstrap exec_prefix = /usr/local/musl-bootstrap
bindir = $(exec_prefix)/bin bindir = $(exec_prefix)/bin
@ -16,7 +17,7 @@ includedir = $(prefix)/include
libdir = $(prefix)/lib libdir = $(prefix)/lib
SRCS = $(sort $(wildcard src/*/*.c)) SRCS = $(sort $(wildcard src/*/*.c))
OBJS = $(SRCS:.c=.o) src/alloca86_64-bt.o src/alloca86_64.o src/libtcc1.o src/va_list.o src/syscall56.o OBJS = $(SRCS:.c=.o) src/alloca86_64-bt.o src/alloca86_64.o src/libtcc1.o src/va_list.o src/syscall.o
LOBJS = $(OBJS:.o=.lo) LOBJS = $(OBJS:.o=.lo)
GENH = include/bits/alltypes.h GENH = include/bits/alltypes.h
@ -44,9 +45,10 @@ all: $(ALL_LIBS) $(ALL_TOOLS)
install: $(ALL_LIBS:lib/%=$(DESTDIR)$(libdir)/%) $(ALL_INCLUDES:include/%=$(DESTDIR)$(includedir)/%) $(ALL_TOOLS:tools/%=$(DESTDIR)$(bindir)/%) install: $(ALL_LIBS:lib/%=$(DESTDIR)$(libdir)/%) $(ALL_INCLUDES:include/%=$(DESTDIR)$(includedir)/%) $(ALL_TOOLS:tools/%=$(DESTDIR)$(bindir)/%)
src/syscall56.o: src/syscall56.s src/syscall.o: src/syscall.s
$(CC) -c -o $@ $< $(CC) -c -o $@ $<
src/%.o: ../tcc-0.9.27/lib/%.o
cp $< $@
clean: clean:
rm -f crt/*.o rm -f crt/*.o
rm -f $(OBJS) rm -f $(OBJS)

View file

@ -20,3 +20,23 @@ struct stat {
struct timespec st_ctim; struct timespec st_ctim;
long __unused[3]; long __unused[3];
}; };
struct stat64 {
unsigned long st_dev;
ino_t st_ino;
nlink_t st_nlink;
mode_t st_mode;
uid_t st_uid;
gid_t st_gid;
unsigned int __pad0;
dev_t st_rdev;
off_t st_size;
blksize_t st_blksize;
blkcnt_t st_blocks;
struct timespec st_atim;
struct timespec st_mtim;
struct timespec st_ctim;
long __unused[3];
};

View file

@ -1,2 +1,79 @@
#include "/usr/local/tcc-bootstrap/include/stdarg.h" #ifndef _STDARG_H
#define _STDARG_H
#ifdef __x86_64__
#ifndef _WIN64
//This should be in sync with the declaration on our lib/libtcc1.c
/* GCC compatible definition of va_list. */
typedef struct {
unsigned int gp_offset;
unsigned int fp_offset;
union {
unsigned int overflow_offset;
char *overflow_arg_area;
};
char *reg_save_area;
} __va_list_struct;
typedef __va_list_struct va_list[1];
void __va_start(__va_list_struct *ap, void *fp);
void *__va_arg(__va_list_struct *ap, int arg_type, int size, int align);
#define va_start(ap, last) __va_start(ap, __builtin_frame_address(0))
#define va_arg(ap, type) \
(*(type *)(__va_arg(ap, __builtin_va_arg_types(type), sizeof(type), __alignof__(type))))
#define va_copy(dest, src) (*(dest) = *(src))
#define va_end(ap)
/* avoid conflicting definition for va_list on Macs. */
#define _VA_LIST_T
#else /* _WIN64 */
typedef char *va_list;
#define va_start(ap,last) __builtin_va_start(ap,last)
#define va_arg(ap, t) ((sizeof(t) > 8 || (sizeof(t) & (sizeof(t) - 1))) \
? **(t **)((ap += 8) - 8) : *(t *)((ap += 8) - 8))
#define va_copy(dest, src) ((dest) = (src))
#define va_end(ap)
#endif
#elif __arm__
typedef char *va_list;
#define _tcc_alignof(type) ((int)&((struct {char c;type x;} *)0)->x)
#define _tcc_align(addr,type) (((unsigned)addr + _tcc_alignof(type) - 1) \
& ~(_tcc_alignof(type) - 1))
#define va_start(ap,last) ap = ((char *)&(last)) + ((sizeof(last)+3)&~3)
#define va_arg(ap,type) (ap = (void *) ((_tcc_align(ap,type)+sizeof(type)+3) \
&~3), *(type *)(ap - ((sizeof(type)+3)&~3)))
#define va_copy(dest, src) (dest) = (src)
#define va_end(ap)
#elif defined(__aarch64__)
typedef struct {
void *__stack;
void *__gr_top;
void *__vr_top;
int __gr_offs;
int __vr_offs;
} va_list;
#define va_start(ap, last) __va_start(ap, last)
#define va_arg(ap, type) __va_arg(ap, type)
#define va_end(ap)
#define va_copy(dest, src) ((dest) = (src))
#else /* __i386__ */
typedef char *va_list;
/* only correct for i386 */
#define va_start(ap,last) ap = ((char *)&(last)) + ((sizeof(last)+3)&~3)
#define va_arg(ap,type) (ap += (sizeof(type)+3)&~3, *(type *)(ap - ((sizeof(type)+3)&~3)))
#define va_copy(dest, src) (dest) = (src)
#define va_end(ap)
#endif
/* fix a buggy dependency on GCC in libio.h */
typedef va_list __gnuc_va_list;
#define _VA_LIST_DEFINED
#endif /* _STDARG_H */

View file

@ -24,6 +24,7 @@ extern long __syscall_ret(unsigned long);
#define SYSCALL "syscall" #define SYSCALL "syscall"
#if 0
static inline long syscall0(long n) static inline long syscall0(long n)
{ {
unsigned long ret; unsigned long ret;
@ -63,7 +64,6 @@ static inline long syscall4(long n, long a1, long a2, long a3, long a4)
return __syscall_ret(ret); return __syscall_ret(ret);
} }
#if 0
static inline long syscall5(long n, long a1, long a2, long a3, long a4, static inline long syscall5(long n, long a1, long a2, long a3, long a4,
long a5) long a5)
{ {
@ -88,6 +88,11 @@ static inline long syscall6(long n, long a1, long a2, long a3, long a4,
return __syscall_ret(ret); return __syscall_ret(ret);
} }
#else #else
extern long syscall0(long);
extern long syscall1(long, long);
extern long syscall2(long, long, long);
extern long syscall3(long, long, long, long);
extern long syscall4(long, long, long, long, long);
extern long syscall5(long, long, long, long, long, long); extern long syscall5(long, long, long, long, long, long);
extern long syscall6(long, long, long, long, long, long, long); extern long syscall6(long, long, long, long, long, long, long);
#endif #endif

View file

@ -34,6 +34,33 @@ int dirfd(DIR *);
int alphasort(const struct dirent **, const struct dirent **); int alphasort(const struct dirent **, const struct dirent **);
int scandir(const char *, struct dirent ***, int (*)(const struct dirent *), int (*)(const struct dirent **, const struct dirent **)); int scandir(const char *, struct dirent ***, int (*)(const struct dirent *), int (*)(const struct dirent **, const struct dirent **));
/* File types for `d_type'. */
enum
{
DT_UNKNOWN = 0,
# define DT_UNKNOWN DT_UNKNOWN
DT_FIFO = 1,
# define DT_FIFO DT_FIFO
DT_CHR = 2,
# define DT_CHR DT_CHR
DT_DIR = 4,
# define DT_DIR DT_DIR
DT_BLK = 6,
# define DT_BLK DT_BLK
DT_REG = 8,
# define DT_REG DT_REG
DT_LNK = 10,
# define DT_LNK DT_LNK
DT_SOCK = 12,
# define DT_SOCK DT_SOCK
DT_WHT = 14
# define DT_WHT DT_WHT
};
/* Convert between stat structure types and directory types. */
# define IFTODT(mode) (((mode) & 0170000) >> 12)
# define DTTOIF(dirtype) ((dirtype) << 12)
#ifdef __cplusplus #ifdef __cplusplus
extern } extern }
#endif #endif

View file

@ -1 +1,87 @@
#include "/usr/local/tcc-bootstrap/include/stdarg.h" #ifndef _STDARG_H
#define _STDARG_H
#ifdef __GNUC__
#define va_list __builtin_va_list
#define va_arg __builtin_va_arg
#define va_start __builtin_va_start
#define va_end __builtin_va_end
#define va_copy __builtin_va_copy
#else
#ifdef __x86_64__
#ifndef _WIN64
//This should be in sync with the declaration on our lib/libtcc1.c
/* GCC compatible definition of va_list. */
typedef struct {
unsigned int gp_offset;
unsigned int fp_offset;
union {
unsigned int overflow_offset;
char *overflow_arg_area;
};
char *reg_save_area;
} __va_list_struct;
typedef __va_list_struct va_list[1];
void __va_start(__va_list_struct *ap, void *fp);
void *__va_arg(__va_list_struct *ap, int arg_type, int size, int align);
#define va_start(ap, last) __va_start(ap, __builtin_frame_address(0))
#define va_arg(ap, type) \
(*(type *)(__va_arg(ap, __builtin_va_arg_types(type), sizeof(type), __alignof__(type))))
#define va_copy(dest, src) (*(dest) = *(src))
#define va_end(ap)
/* avoid conflicting definition for va_list on Macs. */
#define _VA_LIST_T
#else /* _WIN64 */
typedef char *va_list;
#define va_start(ap,last) __builtin_va_start(ap,last)
#define va_arg(ap, t) ((sizeof(t) > 8 || (sizeof(t) & (sizeof(t) - 1))) \
? **(t **)((ap += 8) - 8) : *(t *)((ap += 8) - 8))
#define va_copy(dest, src) ((dest) = (src))
#define va_end(ap)
#endif
#elif __arm__
typedef char *va_list;
#define _tcc_alignof(type) ((int)&((struct {char c;type x;} *)0)->x)
#define _tcc_align(addr,type) (((unsigned)addr + _tcc_alignof(type) - 1) \
& ~(_tcc_alignof(type) - 1))
#define va_start(ap,last) ap = ((char *)&(last)) + ((sizeof(last)+3)&~3)
#define va_arg(ap,type) (ap = (void *) ((_tcc_align(ap,type)+sizeof(type)+3) \
&~3), *(type *)(ap - ((sizeof(type)+3)&~3)))
#define va_copy(dest, src) (dest) = (src)
#define va_end(ap)
#elif defined(__aarch64__)
typedef struct {
void *__stack;
void *__gr_top;
void *__vr_top;
int __gr_offs;
int __vr_offs;
} va_list;
#define va_start(ap, last) __va_start(ap, last)
#define va_arg(ap, type) __va_arg(ap, type)
#define va_end(ap)
#define va_copy(dest, src) ((dest) = (src))
#else /* __i386__ */
typedef char *va_list;
/* only correct for i386 */
#define va_start(ap,last) ap = ((char *)&(last)) + ((sizeof(last)+3)&~3)
#define va_arg(ap,type) (ap += (sizeof(type)+3)&~3, *(type *)(ap - ((sizeof(type)+3)&~3)))
#define va_copy(dest, src) (dest) = (src)
#define va_end(ap)
#endif
/* fix a buggy dependency on GCC in libio.h */
typedef va_list __gnuc_va_list;
#define _VA_LIST_DEFINED
#endif
#endif /* _STDARG_H */

View file

@ -0,0 +1,276 @@
/* Structures and definitions for magnetic tape I/O control commands.
Copyright (C) 1996-2020 Free Software Foundation, Inc.
This file is part of the GNU C Library.
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, see
<https://www.gnu.org/licenses/>. */
/* Written by H. Bergman <hennus@cybercomm.nl>. */
#ifndef _SYS_MTIO_H
#define _SYS_MTIO_H 1
/* Get necessary definitions from system and kernel headers. */
#include <sys/types.h>
#include <sys/ioctl.h>
/* Structure for MTIOCTOP - magnetic tape operation command. */
struct mtop
{
short int mt_op; /* Operations defined below. */
int mt_count; /* How many of them. */
};
#define _IOT_mtop /* Hurd ioctl type field. */ \
_IOT (_IOTS (short), 1, _IOTS (int), 1, 0, 0)
/* Magnetic Tape operations [Not all operations supported by all drivers]. */
#define MTRESET 0 /* +reset drive in case of problems. */
#define MTFSF 1 /* Forward space over FileMark,
* position at first record of next file. */
#define MTBSF 2 /* Backward space FileMark (position before FM). */
#define MTFSR 3 /* Forward space record. */
#define MTBSR 4 /* Backward space record. */
#define MTWEOF 5 /* Write an end-of-file record (mark). */
#define MTREW 6 /* Rewind. */
#define MTOFFL 7 /* Rewind and put the drive offline (eject?). */
#define MTNOP 8 /* No op, set status only (read with MTIOCGET). */
#define MTRETEN 9 /* Retension tape. */
#define MTBSFM 10 /* +backward space FileMark, position at FM. */
#define MTFSFM 11 /* +forward space FileMark, position at FM. */
#define MTEOM 12 /* Goto end of recorded media (for appending files).
MTEOM positions after the last FM, ready for
appending another file. */
#define MTERASE 13 /* Erase tape -- be careful! */
#define MTRAS1 14 /* Run self test 1 (nondestructive). */
#define MTRAS2 15 /* Run self test 2 (destructive). */
#define MTRAS3 16 /* Reserved for self test 3. */
#define MTSETBLK 20 /* Set block length (SCSI). */
#define MTSETDENSITY 21 /* Set tape density (SCSI). */
#define MTSEEK 22 /* Seek to block (Tandberg, etc.). */
#define MTTELL 23 /* Tell block (Tandberg, etc.). */
#define MTSETDRVBUFFER 24 /* Set the drive buffering according to SCSI-2.
Ordinary buffered operation with code 1. */
#define MTFSS 25 /* Space forward over setmarks. */
#define MTBSS 26 /* Space backward over setmarks. */
#define MTWSM 27 /* Write setmarks. */
#define MTLOCK 28 /* Lock the drive door. */
#define MTUNLOCK 29 /* Unlock the drive door. */
#define MTLOAD 30 /* Execute the SCSI load command. */
#define MTUNLOAD 31 /* Execute the SCSI unload command. */
#define MTCOMPRESSION 32/* Control compression with SCSI mode page 15. */
#define MTSETPART 33 /* Change the active tape partition. */
#define MTMKPART 34 /* Format the tape with one or two partitions. */
/* structure for MTIOCGET - mag tape get status command */
struct mtget
{
long int mt_type; /* Type of magtape device. */
long int mt_resid; /* Residual count: (not sure)
number of bytes ignored, or
number of files not skipped, or
number of records not skipped. */
/* The following registers are device dependent. */
long int mt_dsreg; /* Status register. */
long int mt_gstat; /* Generic (device independent) status. */
long int mt_erreg; /* Error register. */
/* The next two fields are not always used. */
int mt_fileno; /* Number of current file on tape. */
int mt_blkno; /* Current block number. */
};
#define _IOT_mtget /* Hurd ioctl type field. */ \
_IOT (_IOTS (long), 7, 0, 0, 0, 0)
/* Constants for mt_type. Not all of these are supported, and
these are not all of the ones that are supported. */
#define MT_ISUNKNOWN 0x01
#define MT_ISQIC02 0x02 /* Generic QIC-02 tape streamer. */
#define MT_ISWT5150 0x03 /* Wangtek 5150EQ, QIC-150, QIC-02. */
#define MT_ISARCHIVE_5945L2 0x04 /* Archive 5945L-2, QIC-24, QIC-02?. */
#define MT_ISCMSJ500 0x05 /* CMS Jumbo 500 (QIC-02?). */
#define MT_ISTDC3610 0x06 /* Tandberg 6310, QIC-24. */
#define MT_ISARCHIVE_VP60I 0x07 /* Archive VP60i, QIC-02. */
#define MT_ISARCHIVE_2150L 0x08 /* Archive Viper 2150L. */
#define MT_ISARCHIVE_2060L 0x09 /* Archive Viper 2060L. */
#define MT_ISARCHIVESC499 0x0A /* Archive SC-499 QIC-36 controller. */
#define MT_ISQIC02_ALL_FEATURES 0x0F /* Generic QIC-02 with all features. */
#define MT_ISWT5099EEN24 0x11 /* Wangtek 5099-een24, 60MB, QIC-24. */
#define MT_ISTEAC_MT2ST 0x12 /* Teac MT-2ST 155mb drive,
Teac DC-1 card (Wangtek type). */
#define MT_ISEVEREX_FT40A 0x32 /* Everex FT40A (QIC-40). */
#define MT_ISDDS1 0x51 /* DDS device without partitions. */
#define MT_ISDDS2 0x52 /* DDS device with partitions. */
#define MT_ISSCSI1 0x71 /* Generic ANSI SCSI-1 tape unit. */
#define MT_ISSCSI2 0x72 /* Generic ANSI SCSI-2 tape unit. */
/* QIC-40/80/3010/3020 ftape supported drives.
20bit vendor ID + 0x800000 (see vendors.h in ftape distribution). */
#define MT_ISFTAPE_UNKNOWN 0x800000 /* obsolete */
#define MT_ISFTAPE_FLAG 0x800000
struct mt_tape_info
{
long int t_type; /* Device type id (mt_type). */
char *t_name; /* Descriptive name. */
};
#define MT_TAPE_INFO \
{ \
{MT_ISUNKNOWN, "Unknown type of tape device"}, \
{MT_ISQIC02, "Generic QIC-02 tape streamer"}, \
{MT_ISWT5150, "Wangtek 5150, QIC-150"}, \
{MT_ISARCHIVE_5945L2, "Archive 5945L-2"}, \
{MT_ISCMSJ500, "CMS Jumbo 500"}, \
{MT_ISTDC3610, "Tandberg TDC 3610, QIC-24"}, \
{MT_ISARCHIVE_VP60I, "Archive VP60i, QIC-02"}, \
{MT_ISARCHIVE_2150L, "Archive Viper 2150L"}, \
{MT_ISARCHIVE_2060L, "Archive Viper 2060L"}, \
{MT_ISARCHIVESC499, "Archive SC-499 QIC-36 controller"}, \
{MT_ISQIC02_ALL_FEATURES, "Generic QIC-02 tape, all features"}, \
{MT_ISWT5099EEN24, "Wangtek 5099-een24, 60MB"}, \
{MT_ISTEAC_MT2ST, "Teac MT-2ST 155mb data cassette drive"}, \
{MT_ISEVEREX_FT40A, "Everex FT40A, QIC-40"}, \
{MT_ISSCSI1, "Generic SCSI-1 tape"}, \
{MT_ISSCSI2, "Generic SCSI-2 tape"}, \
{0, NULL} \
}
/* Structure for MTIOCPOS - mag tape get position command. */
struct mtpos
{
long int mt_blkno; /* Current block number. */
};
#define _IOT_mtpos /* Hurd ioctl type field. */ \
_IOT_SIMPLE (long)
/* Structure for MTIOCGETCONFIG/MTIOCSETCONFIG primarily intended
as an interim solution for QIC-02 until DDI is fully implemented. */
struct mtconfiginfo
{
long int mt_type; /* Drive type. */
long int ifc_type; /* Interface card type. */
unsigned short int irqnr; /* IRQ number to use. */
unsigned short int dmanr; /* DMA channel to use. */
unsigned short int port; /* IO port base address. */
unsigned long int debug; /* Debugging flags. */
unsigned have_dens:1;
unsigned have_bsf:1;
unsigned have_fsr:1;
unsigned have_bsr:1;
unsigned have_eod:1;
unsigned have_seek:1;
unsigned have_tell:1;
unsigned have_ras1:1;
unsigned have_ras2:1;
unsigned have_ras3:1;
unsigned have_qfa:1;
unsigned pad1:5;
char reserved[10];
};
#define _IOT_mtconfiginfo /* Hurd ioctl type field. */ \
_IOT (_IOTS (long), 2, _IOTS (short), 3, _IOTS (long), 1) /* XXX wrong */
/* Magnetic tape I/O control commands. */
#define MTIOCTOP _IOW('m', 1, struct mtop) /* Do a mag tape op. */
#define MTIOCGET _IOR('m', 2, struct mtget) /* Get tape status. */
#define MTIOCPOS _IOR('m', 3, struct mtpos) /* Get tape position.*/
/* The next two are used by the QIC-02 driver for runtime reconfiguration.
See tpqic02.h for struct mtconfiginfo. */
#define MTIOCGETCONFIG _IOR('m', 4, struct mtconfiginfo) /* Get tape config.*/
#define MTIOCSETCONFIG _IOW('m', 5, struct mtconfiginfo) /* Set tape config.*/
/* Generic Mag Tape (device independent) status macros for examining
mt_gstat -- HP-UX compatible.
There is room for more generic status bits here, but I don't
know which of them are reserved. At least three or so should
be added to make this really useful. */
#define GMT_EOF(x) ((x) & 0x80000000)
#define GMT_BOT(x) ((x) & 0x40000000)
#define GMT_EOT(x) ((x) & 0x20000000)
#define GMT_SM(x) ((x) & 0x10000000) /* DDS setmark */
#define GMT_EOD(x) ((x) & 0x08000000) /* DDS EOD */
#define GMT_WR_PROT(x) ((x) & 0x04000000)
/* #define GMT_ ? ((x) & 0x02000000) */
#define GMT_ONLINE(x) ((x) & 0x01000000)
#define GMT_D_6250(x) ((x) & 0x00800000)
#define GMT_D_1600(x) ((x) & 0x00400000)
#define GMT_D_800(x) ((x) & 0x00200000)
/* #define GMT_ ? ((x) & 0x00100000) */
/* #define GMT_ ? ((x) & 0x00080000) */
#define GMT_DR_OPEN(x) ((x) & 0x00040000) /* Door open (no tape). */
/* #define GMT_ ? ((x) & 0x00020000) */
#define GMT_IM_REP_EN(x) ((x) & 0x00010000) /* Immediate report mode.*/
/* 16 generic status bits unused. */
/* SCSI-tape specific definitions. Bitfield shifts in the status */
#define MT_ST_BLKSIZE_SHIFT 0
#define MT_ST_BLKSIZE_MASK 0xffffff
#define MT_ST_DENSITY_SHIFT 24
#define MT_ST_DENSITY_MASK 0xff000000
#define MT_ST_SOFTERR_SHIFT 0
#define MT_ST_SOFTERR_MASK 0xffff
/* Bitfields for the MTSETDRVBUFFER ioctl. */
#define MT_ST_OPTIONS 0xf0000000
#define MT_ST_BOOLEANS 0x10000000
#define MT_ST_SETBOOLEANS 0x30000000
#define MT_ST_CLEARBOOLEANS 0x40000000
#define MT_ST_WRITE_THRESHOLD 0x20000000
#define MT_ST_DEF_BLKSIZE 0x50000000
#define MT_ST_DEF_OPTIONS 0x60000000
#define MT_ST_BUFFER_WRITES 0x1
#define MT_ST_ASYNC_WRITES 0x2
#define MT_ST_READ_AHEAD 0x4
#define MT_ST_DEBUGGING 0x8
#define MT_ST_TWO_FM 0x10
#define MT_ST_FAST_MTEOM 0x20
#define MT_ST_AUTO_LOCK 0x40
#define MT_ST_DEF_WRITES 0x80
#define MT_ST_CAN_BSR 0x100
#define MT_ST_NO_BLKLIMS 0x200
#define MT_ST_CAN_PARTITIONS 0x400
#define MT_ST_SCSI2LOGICAL 0x800
/* The mode parameters to be controlled. Parameter chosen with bits 20-28. */
#define MT_ST_CLEAR_DEFAULT 0xfffff
#define MT_ST_DEF_DENSITY (MT_ST_DEF_OPTIONS | 0x100000)
#define MT_ST_DEF_COMPRESSION (MT_ST_DEF_OPTIONS | 0x200000)
#define MT_ST_DEF_DRVBUFFER (MT_ST_DEF_OPTIONS | 0x300000)
/* The offset for the arguments for the special HP changer load command. */
#define MT_ST_HPLOADER_OFFSET 10000
/* Specify default tape device. */
#ifndef DEFTAPE
# define DEFTAPE "/dev/tape"
#endif
#endif /* mtio.h */

View file

@ -324,7 +324,7 @@ void *malloc(size_t n)
{ {
struct chunk *c; struct chunk *c;
int i, j; int i, j;
if (n == 0) n = 1;/* everyone depends on this behavior for some fucking reason */
if (!n || adjust_size(&n) < 0) return 0; if (!n || adjust_size(&n) < 0) return 0;
if (n > MMAP_THRESHOLD) { if (n > MMAP_THRESHOLD) {

View file

@ -7,4 +7,7 @@ int __madvise(void *addr, size_t len, int advice)
return syscall3(__NR_madvise, (long)addr, len, advice); return syscall3(__NR_madvise, (long)addr, len, advice);
} }
weak_alias(__madvise, madvise); int madvise(void *addr, size_t len, int advice)
{
return syscall3(__NR_madvise, (long)addr, len, advice);
}

View file

@ -17,6 +17,16 @@ void *__mmap(void *start, size_t len, int prot, int flags, int fd, off_t off)
#endif #endif
} }
weak_alias(__mmap, mmap); void *mmap(void *start, size_t len, int prot, int flags, int fd, off_t off)
{
if (sizeof(off_t) > sizeof(long))
if (((long)off & 0xfff) | ((long)((unsigned long long)off>>(12 + 8*(sizeof(off_t)-sizeof(long))))))
start = (void *)-1;
#ifdef __NR_mmap2
return (void *)syscall6(__NR_mmap2, (long)start, len, prot, flags, fd, off>>12);
#else
return (void *)syscall6(__NR_mmap, (long)start, len, prot, flags, fd, off);
#endif
}
LFS64(mmap); LFS64(mmap);

View file

@ -8,4 +8,7 @@ int __munmap(void *start, size_t len)
return syscall2(__NR_munmap, (long)start, len); return syscall2(__NR_munmap, (long)start, len);
} }
weak_alias(__munmap, munmap); int munmap(void *start, size_t len)
{
return syscall2(__NR_munmap, (long)start, len);
}

View file

@ -42,4 +42,12 @@ int __sigaction(int sig, const struct sigaction *sa, struct sigaction *old)
return __libc_sigaction(sig, sa, old); return __libc_sigaction(sig, sa, old);
} }
weak_alias(__sigaction, sigaction); int sigaction(int sig, const struct sigaction *sa, struct sigaction *old)
{
if (sig == SIGCANCEL || sig == SIGSYSCALL) {
errno = EINVAL;
return -1;
}
return __libc_sigaction(sig, sa, old);
}

View file

@ -19,5 +19,26 @@ int __sigprocmask(int how, const sigset_t *set, sigset_t *old)
return __libc_sigprocmask(how, set, old); return __libc_sigprocmask(how, set, old);
} }
weak_alias(__sigprocmask, sigprocmask); int sigprocmask(int how, const sigset_t *set, sigset_t *old)
weak_alias(__sigprocmask, pthread_sigmask); {
sigset_t tmp;
/* Quickly mask out bits 32 and 33 (thread control signals) */
if (0 && how != SIG_UNBLOCK && (set->__bits[4/sizeof *set->__bits] & 3UL<<(32&8*sizeof *set->__bits-1))) {
tmp = *set;
set = &tmp;
tmp.__bits[4/sizeof *set->__bits] &= ~(3UL<<(32&8*sizeof *set->__bits-1));
}
return __libc_sigprocmask(how, set, old);
}
int pthread_sigmask(int how, const sigset_t *set, sigset_t *old)
{
sigset_t tmp;
/* Quickly mask out bits 32 and 33 (thread control signals) */
if (0 && how != SIG_UNBLOCK && (set->__bits[4/sizeof *set->__bits] & 3UL<<(32&8*sizeof *set->__bits-1))) {
tmp = *set;
set = &tmp;
tmp.__bits[4/sizeof *set->__bits] &= ~(3UL<<(32&8*sizeof *set->__bits-1));
}
return __libc_sigprocmask(how, set, old);
}

View file

@ -7,4 +7,10 @@ void clearerr(FILE *f)
FUNLOCK(f); FUNLOCK(f);
} }
weak_alias(clearerr, clearerr_unlocked); void clearerr_unlocked(FILE *f)
{
FLOCK(f);
f->flags &= ~(F_EOF|F_ERR);
FUNLOCK(f);
}

View file

@ -7,4 +7,7 @@ int feof(FILE *f)
return !!(f->flags & F_EOF); return !!(f->flags & F_EOF);
} }
weak_alias(feof, feof_unlocked); int feof_unlocked(FILE *f)
{
return !!(f->flags & F_EOF);
}

View file

@ -7,4 +7,7 @@ int ferror(FILE *f)
return !!(f->flags & F_ERR); return !!(f->flags & F_ERR);
} }
weak_alias(ferror, ferror_unlocked); int ferror_unlocked(FILE *f)
{
return !!(f->flags & F_ERR);
}

View file

@ -8,3 +8,12 @@ int fgetc(FILE *f)
FUNLOCK(f); FUNLOCK(f);
return c; return c;
} }
int fgetc_unlocked(FILE *f)
{
int c;
FLOCK(f);
c = f->rpos < f->rstop ? *f->rpos++ : __uflow(f);
FUNLOCK(f);
return c;
}

View file

@ -31,4 +31,7 @@ char *fgets(char *s, int n, FILE *f)
return (p == s) ? 0 : s; return (p == s) ? 0 : s;
} }
weak_alias(fgets, fgets_unlocked);
char *fgets_unlocked(char *s, int n, FILE *f) {
return fgets(s, n, f);
}

View file

@ -5,4 +5,7 @@ int fileno(FILE *f)
return f->fd; return f->fd;
} }
weak_alias(fileno, fileno_unlocked); int fileno_unlocked(FILE *f)
{
return f->fd;
}

View file

@ -8,3 +8,12 @@ int fputc(int c, FILE *f)
FUNLOCK(f); FUNLOCK(f);
return c; return c;
} }
int fputc_unlocked(int c, FILE *f)
{
FLOCK(f);
if (c != f->lbf && f->wpos + 1 < f->wend) *f->wpos++ = c;
else c = __overflow(f, c);
FUNLOCK(f);
return c;
}

View file

@ -7,4 +7,9 @@ int fputs(const char *s, FILE *f)
return (int)fwrite(s, l, 1, f) - 1; return (int)fwrite(s, l, 1, f) - 1;
} }
weak_alias(fputs, fputs_unlocked); int fputs_unlocked(const char *s, FILE *f)
{
size_t l = strlen(s);
if (!l) return 0;
return (int)fwrite(s, l, 1, f) - 1;
}

View file

@ -46,4 +46,6 @@ eof:
return (len-l)/size; return (len-l)/size;
} }
weak_alias(fread, fread_unlocked); size_t fread_unlocked(void *destv, size_t size, size_t nmemb, FILE *f) {
return fread(destv, size, nmemb, f);
}

View file

@ -48,4 +48,12 @@ size_t fwrite(const void *src, size_t size, size_t nmemb, FILE *f)
return l/size; return l/size;
} }
weak_alias(fwrite, fwrite_unlocked); size_t fwrite_unlocked(const void *src, size_t size, size_t nmemb, FILE *f)
{
size_t l = size*nmemb;
if (!l) return l;
FLOCK(f);
l = __fwritex(src, l, f);
FUNLOCK(f);
return l/size;
}

View file

@ -10,4 +10,10 @@ char *__strdup(const char *s)
return memcpy(d, s, l+1); return memcpy(d, s, l+1);
} }
weak_alias(__strdup, strdup); char *strdup(const char *s)
{
size_t l = strlen(s);
char *d = malloc(l+1);
if (!d) return NULL;
return memcpy(d, s, l+1);
}

View file

@ -1,6 +1,18 @@
# this file is necessary because tcc doesn't like musl's inline-assembly implementation
# of syscall
.global syscall0
.global syscall1
.global syscall2
.global syscall3
.global syscall4
.global syscall5 .global syscall5
.global syscall6 .global syscall6
syscall0:
syscall1:
syscall2:
syscall3:
syscall4:
syscall5: syscall5:
syscall6: syscall6:
# SysV calling convention: RDI, RSI, RDX, RCX, R8, R9, 8(%rsp) # SysV calling convention: RDI, RSI, RDX, RCX, R8, R9, 8(%rsp)
@ -13,4 +25,6 @@ syscall6:
mov %r9, %r8 mov %r9, %r8
mov 8(%rsp), %r9 mov 8(%rsp), %r9
syscall syscall
mov %rax, %rdi
call __syscall_ret
ret ret

View file

@ -5,3 +5,8 @@ int clock_gettime(clockid_t clk, struct timespec *ts)
{ {
return syscall2(__NR_clock_gettime, clk, (long)ts); return syscall2(__NR_clock_gettime, clk, (long)ts);
} }
int clock_settime(clockid_t clk, const struct timespec *ts)
{
return syscall2(__NR_clock_settime, clk, (long)ts);
}