]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
fix: Bash cross-compilation for i686-adros toolchain
authorTulio A M Mendes <[email protected]>
Sat, 14 Mar 2026 06:54:09 +0000 (03:54 -0300)
committerTulio A M Mendes <[email protected]>
Sat, 14 Mar 2026 06:54:09 +0000 (03:54 -0300)
- build.sh: remove ulibc header install (conflicts with newlib POSIX headers)
- build.sh: add sys/ioctl.h, sys/termios.h, sys/dirent.h stubs to sysroot
- build.sh: add libgloss build step (crt0.o + libadros.a with posix_stubs)
- build.sh: add GCC specs file creation (--start-group/-lc/-ladros/--end-group)
- build.sh: comprehensive Bash cross-compile config.cache (types, headers, funcs)
- build.sh: use -D_POSIX_VERSION=200112L and --allow-multiple-definition
- syscalls.c: remove rename() (provided by newlib libc.a)
- posix_stubs.c: new file with POSIX function stubs for Bash linking
  (dup2, fcntl, pipe, select, pselect, tcgetattr, sigaction, sigprocmask,
   getcwd, waitpid, getpwnam, opendir, access, chdir, umask, sleep, etc.)

Bash 5.2.21 now cross-compiles as a static ELF 32-bit i386 binary.

newlib/libgloss/adros/posix_stubs.c [new file with mode: 0644]
newlib/libgloss/adros/syscalls.c
toolchain/build.sh

diff --git a/newlib/libgloss/adros/posix_stubs.c b/newlib/libgloss/adros/posix_stubs.c
new file mode 100644 (file)
index 0000000..c1c4e9d
--- /dev/null
@@ -0,0 +1,134 @@
+/* posix_stubs.c — POSIX stubs for Bash cross-compilation on AdrOS/newlib */
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <termios.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <pwd.h>
+#include <sys/ioctl.h>
+#include <dirent.h>
+
+static int nosys(void) { errno = ENOSYS; return -1; }
+
+/* File descriptor ops */
+int dup(int fd) { (void)fd; return nosys(); }
+int dup2(int o, int n) { (void)o; (void)n; return nosys(); }
+int fcntl(int fd, int c, ...) { (void)fd; (void)c; return nosys(); }
+int pipe(int p[2]) { (void)p; return nosys(); }
+
+/* File ops */
+int chown(const char *p, uid_t o, gid_t g) { (void)p; (void)o; (void)g; return nosys(); }
+int lstat(const char *p, struct stat *s) { return stat(p, s); }
+
+/* Process/signal */
+int killpg(int pg, int s) { return kill(-pg, s); }
+int setpgid(pid_t p, pid_t g) { (void)p; (void)g; return nosys(); }
+pid_t setsid(void) { return (pid_t)nosys(); }
+pid_t getpgrp(void) { return getpid(); }
+int sigaction(int sig, const struct sigaction *a, struct sigaction *o) {
+    (void)sig; (void)a; (void)o; return nosys();
+}
+int sigprocmask(int h, const sigset_t *s, sigset_t *o) {
+    (void)h; (void)s; (void)o; return 0;
+}
+
+/* Terminal */
+int tcgetattr(int fd, struct termios *t) { (void)fd; (void)t; return nosys(); }
+int tcsetattr(int fd, int a, const struct termios *t) { (void)fd; (void)a; (void)t; return nosys(); }
+pid_t tcgetpgrp(int fd) { (void)fd; return (pid_t)nosys(); }
+int tcsetpgrp(int fd, pid_t pg) { (void)fd; (void)pg; return nosys(); }
+
+/* I/O multiplexing */
+int select(int n, fd_set *r, fd_set *w, fd_set *e, struct timeval *t) {
+    (void)n; (void)r; (void)w; (void)e; (void)t; return nosys();
+}
+
+/* Misc */
+char *getcwd(char *b, size_t sz) {
+    if (b && sz > 1) { b[0] = '/'; b[1] = 0; return b; }
+    errno = ERANGE; return 0;
+}
+int gethostname(char *n, size_t l) {
+    if (n && l > 5) { strcpy(n, "adros"); return 0; }
+    errno = ENAMETOOLONG; return -1;
+}
+int ioctl(int fd, unsigned long r, ...) { (void)fd; (void)r; return nosys(); }
+
+/* pwd stubs */
+static struct passwd _pw = {"root", "", 0, 0, "", "/", "/bin/sh"};
+struct passwd *getpwuid(uid_t u) { (void)u; return &_pw; }
+struct passwd *getpwnam(const char *n) { (void)n; return &_pw; }
+
+/* Additional stubs */
+int mkfifo(const char *p, mode_t m) { (void)p; (void)m; errno = ENOSYS; return -1; }
+int tcdrain(int fd) { (void)fd; return 0; }
+int tcflow(int fd, int a) { (void)fd; (void)a; return 0; }
+int tcflush(int fd, int q) { (void)fd; (void)q; return 0; }
+int tcsendbreak(int fd, int d) { (void)fd; (void)d; return 0; }
+speed_t cfgetispeed(const struct termios *t) { (void)t; return 0; }
+speed_t cfgetospeed(const struct termios *t) { (void)t; return 0; }
+int cfsetispeed(struct termios *t, speed_t s) { (void)t; (void)s; return 0; }
+int cfsetospeed(struct termios *t, speed_t s) { (void)t; (void)s; return 0; }
+
+/* Directory stubs */
+DIR *opendir(const char *p) { (void)p; errno = ENOSYS; return 0; }
+struct dirent *readdir(DIR *d) { (void)d; return 0; }
+int closedir(DIR *d) { (void)d; return 0; }
+void rewinddir(DIR *d) { (void)d; }
+
+/* pselect stub */
+int pselect(int n, fd_set *r, fd_set *w, fd_set *e,
+            const struct timespec *t, const sigset_t *sm) {
+    (void)n; (void)r; (void)w; (void)e; (void)t; (void)sm;
+    errno = ENOSYS; return -1;
+}
+
+/* --- Link-time stubs for Bash --- */
+#include <regex.h>
+#include <fnmatch.h>
+#include <time.h>
+
+int access(const char *p, int m) { (void)p; (void)m; return 0; }
+unsigned alarm(unsigned s) { (void)s; return 0; }
+int chdir(const char *p) { (void)p; errno = ENOSYS; return -1; }
+int fchmod(int fd, mode_t m) { (void)fd; (void)m; errno = ENOSYS; return -1; }
+uid_t getuid(void) { return 0; }
+uid_t geteuid(void) { return 0; }
+gid_t getegid(void) { return 0; }
+pid_t getppid(void) { return 1; }
+int setuid(uid_t u) { (void)u; return 0; }
+int setreuid(uid_t r, uid_t e) { (void)r; (void)e; return 0; }
+int setregid(gid_t r, gid_t e) { (void)r; (void)e; return 0; }
+unsigned sleep(unsigned s) { (void)s; return 0; }
+mode_t umask(mode_t m) { (void)m; return 022; }
+char *ttyname(int fd) { (void)fd; return "/dev/tty"; }
+pid_t waitpid(pid_t p, int *s, int o) { (void)p; (void)s; (void)o; errno = ECHILD; return -1; }
+void setgrent(void) {}
+void endgrent(void) {}
+
+int fnmatch(const char *pat, const char *str, int flags) {
+    (void)flags;
+    /* Trivial: only support exact match */
+    return strcmp(pat, str) == 0 ? 0 : 1;
+}
+
+int regcomp(regex_t *r, const char *p, int f) { (void)r; (void)p; (void)f; return -1; }
+int regexec(const regex_t *r, const char *s, size_t n, regmatch_t pm[], int f) {
+    (void)r; (void)s; (void)n; (void)pm; (void)f; return -1;
+}
+void regfree(regex_t *r) { (void)r; }
+size_t regerror(int e, const regex_t *r, char *buf, size_t sz) {
+    (void)e; (void)r;
+    if (buf && sz > 0) { buf[0] = 0; }
+    return 0;
+}
+
+/* Group/user stubs */
+gid_t getgid(void) { return 0; }
+int setgid(gid_t g) { (void)g; return 0; }
+struct group { char *gr_name; char *gr_passwd; gid_t gr_gid; char **gr_mem; };
+static struct group _gr = {"root", "", 0, 0};
+struct group *getgrent(void) { return &_gr; }
index a040dc68979ca4812732371249ad9249f4b38508..02f72c76d0323cf7ff607c05f0fe2543bcd7e6af 100644 (file)
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/times.h>
+#include <sys/time.h>
 #include <errno.h>
 #include <stdint.h>
 
+#ifndef _CADDR_T
+typedef char *caddr_t;
+#endif
+
 /* ---- AdrOS syscall numbers (must match include/syscall.h) ---- */
 #define SYS_WRITE       1
 #define SYS_EXIT        2
@@ -74,15 +79,11 @@ static inline int _check(int r) {
     return r;
 }
 
-/* ---- Environment ---- */
-char *__env[1] = { 0 };
-char **environ = __env;
-
 /* ---- Heap management via brk() ---- */
 
 static char *_heap_end = 0;
 
-caddr_t _sbrk(int incr) {
+caddr_t sbrk(int incr) {
     if (!_heap_end) {
         /* Get current break */
         _heap_end = (char *)(uintptr_t)_sc1(SYS_BRK, 0);
@@ -107,53 +108,51 @@ caddr_t _sbrk(int incr) {
 
 /* ---- File I/O ---- */
 
-int _open(const char *name, int flags, int mode) {
+int open(const char *name, int flags, int mode) {
     (void)mode;
     return _check(_sc2(SYS_OPEN, (int)name, flags));
 }
 
-int _close(int fd) {
+int close(int fd) {
     return _check(_sc1(SYS_CLOSE, fd));
 }
 
-int _read(int fd, char *buf, int len) {
+int read(int fd, char *buf, int len) {
     return _check(_sc3(SYS_READ, fd, (int)buf, len));
 }
 
-int _write(int fd, const char *buf, int len) {
+int write(int fd, const char *buf, int len) {
     return _check(_sc3(SYS_WRITE, fd, (int)buf, len));
 }
 
-int _lseek(int fd, int offset, int whence) {
+int lseek(int fd, int offset, int whence) {
     return _check(_sc3(SYS_LSEEK, fd, offset, whence));
 }
 
-int _fstat(int fd, struct stat *st) {
+int fstat(int fd, struct stat *st) {
     return _check(_sc2(SYS_FSTAT, fd, (int)st));
 }
 
-int _stat(const char *path, struct stat *st) {
+int stat(const char *path, struct stat *st) {
     return _check(_sc2(SYS_STAT, (int)path, (int)st));
 }
 
-int _link(const char *oldpath, const char *newpath) {
+int link(const char *oldpath, const char *newpath) {
     return _check(_sc2(SYS_LINK, (int)oldpath, (int)newpath));
 }
 
-int _unlink(const char *name) {
+int unlink(const char *name) {
     return _check(_sc1(SYS_UNLINK, (int)name));
 }
 
-int _rename(const char *oldpath, const char *newpath) {
-    return _check(_sc2(SYS_RENAME, (int)oldpath, (int)newpath));
-}
+/* rename is provided by newlib libc.a */
 
-int _mkdir(const char *path, int mode) {
+int mkdir(const char *path, mode_t mode) {
     (void)mode;
     return _check(_sc1(SYS_MKDIR, (int)path));
 }
 
-int _isatty(int fd) {
+int isatty(int fd) {
     /* Use ioctl TIOCGPGRP (0x540F) — if it succeeds, fd is a tty */
     int r = _sc3(SYS_IOCTL, fd, 0x540F, 0);
     if (r < 0) {
@@ -170,33 +169,33 @@ void _exit(int status) {
     __builtin_unreachable();
 }
 
-int _getpid(void) {
+int getpid(void) {
     return _sc0(SYS_GETPID);
 }
 
-int _kill(int pid, int sig) {
+int kill(int pid, int sig) {
     return _check(_sc2(SYS_KILL, pid, sig));
 }
 
-int _fork(void) {
+int fork(void) {
     return _check(_sc0(SYS_FORK));
 }
 
-int _execve(const char *name, char *const argv[], char *const envp[]) {
+int execve(const char *name, char *const argv[], char *const envp[]) {
     return _check(_sc3(SYS_EXECVE, (int)name, (int)argv, (int)envp));
 }
 
-int _wait(int *status) {
+int wait(int *status) {
     return _check(_sc3(SYS_WAITPID, -1, (int)status, 0));
 }
 
 /* ---- Time ---- */
 
-int _gettimeofday(struct timeval *tv, void *tz) {
+int gettimeofday(struct timeval *tv, void *tz) {
     (void)tz;
     return _check(_sc2(SYS_GETTIMEOFDAY, (int)tv, 0));
 }
 
-int _times(struct tms *buf) {
-    return _check(_sc1(SYS_TIMES, (int)buf));
+clock_t times(struct tms *buf) {
+    return (clock_t)_check(_sc1(SYS_TIMES, (int)buf));
 }
index 627524e02c973d4c5a1becb60c9e2998a625f829..0170a459ca03b999d32630633b1b9989e34ef356 100755 (executable)
@@ -205,7 +205,7 @@ i[34567]86-*-adros*)\
 #define ENDFILE_SPEC "crtend.o%s crtn.o%s"
 
 #undef  LIB_SPEC
-#define LIB_SPEC "-lc -ladros -lgcc"
+#define LIB_SPEC "--start-group -lc -ladros --end-group -lgcc"
 
 #undef  LINK_SPEC
 #define LINK_SPEC "-m elf_i386 %{shared:-shared} %{static:-static} %{!static: %{rdynamic:-export-dynamic}}"
@@ -230,7 +230,7 @@ ADROS_H
         sed -i '/^x86_64-\*-elf\* | x86_64-\*-rtems\*)/i\
 i[34567]86-*-adros*)\
 \ttmake_file="$tmake_file i386/t-crtstuff t-crtstuff-pic t-libgcc-pic"\
-\textra_parts="$extra_parts crti.o crtn.o"\
+\textra_parts="$extra_parts crti.o crtn.o crtbegin.o crtend.o"\
 \t;;' "$d/libgcc/config.host"
         step "Patched libgcc/config.host"
     fi
@@ -285,7 +285,7 @@ patch_newlib() {
   i[34567]86-*-adros*)\
 \tsys_dir=adros\
 \thave_crt0="no"\
-\tnewlib_cflags="${newlib_cflags} -DSIGNAL_PROVIDED -DHAVE_OPENDIR -DHAVE_SYSTEM -DMALLOC_PROVIDED"\
+\tnewlib_cflags="${newlib_cflags} -DSIGNAL_PROVIDED -DHAVE_OPENDIR -DHAVE_SYSTEM"\
 \t;;
 }' "$d/newlib/configure.host"
         step "Patched newlib/configure.host"
@@ -298,7 +298,6 @@ patch_newlib() {
 /* AdrOS target configuration */\
 #ifdef __adros__\
 #define _READ_WRITE_RETURN_TYPE int\
-#define __DYNAMIC_REENT__\
 #define HAVE_SYSTEM\
 #define HAVE_OPENDIR\
 #endif
@@ -327,11 +326,10 @@ EOF
         step "Patched libgloss/configure.ac"
     fi
 
-    # Copy our libgloss/adros stubs into the Newlib source tree
-    if [[ ! -d "$d/libgloss/adros" ]]; then
-        cp -r "$ADROS_ROOT/newlib/libgloss/adros" "$d/libgloss/adros"
-        step "Copied libgloss/adros/ stubs"
-    fi
+    # Copy/sync our libgloss/adros stubs into the Newlib source tree
+    mkdir -p "$d/libgloss/adros"
+    cp -u "$ADROS_ROOT/newlib/libgloss/adros/"*.{c,S,h} "$d/libgloss/adros/" 2>/dev/null || true
+    step "Synced libgloss/adros/ stubs"
 
     # Create libgloss/adros autoconf files if not present
     if [[ ! -f "$d/libgloss/adros/configure.in" ]]; then
@@ -411,25 +409,174 @@ EOF
     touch "$marker"
 }
 
+patch_bash() {
+    local d="$SRC_DIR/bash-${BASH_VER}"
+    [[ ! -d "$d" ]] && return
+    local marker="$d/.adros_patched"
+    [[ -f "$marker" ]] && { step "Bash already patched"; return; }
+
+    patch_config_sub "$d/support/config.sub"
+
+    touch "$marker"
+}
+
 msg "Applying AdrOS target patches"
 patch_binutils
 patch_gcc
 patch_newlib
-
-# ---- Install sysroot headers ----
-msg "Installing sysroot headers"
-step "Copying ulibc headers to ${SYSROOT}/include..."
-mkdir -p "${SYSROOT}/include/sys" "${SYSROOT}/include/linux"
-cp -r "$ADROS_ROOT/user/ulibc/include/"*.h "${SYSROOT}/include/" 2>/dev/null || true
-cp -r "$ADROS_ROOT/user/ulibc/include/sys/"*.h "${SYSROOT}/include/sys/" 2>/dev/null || true
-cp -r "$ADROS_ROOT/user/ulibc/include/linux/"*.h "${SYSROOT}/include/linux/" 2>/dev/null || true
-
-# Copy kernel headers needed by the toolchain
-step "Copying kernel headers to sysroot..."
-for h in errno.h syscall.h socket.h; do
-    [[ -f "$ADROS_ROOT/include/$h" ]] && cp "$ADROS_ROOT/include/$h" "${SYSROOT}/include/kernel_${h}"
+patch_bash
+
+# ---- Install AdrOS-specific sysroot headers ----
+# NOTE: We do NOT copy the full ulibc headers here because they conflict
+# with Newlib's POSIX-compliant headers (signal.h, errno.h, stdio.h, etc.).
+# ulibc headers are for AdrOS userland built with -nostdlib; the cross-
+# toolchain uses Newlib headers instead.  Only truly AdrOS-specific headers
+# (syscall numbers, ioctl defs) are installed under a kernel_ prefix.
+msg "Installing AdrOS-specific sysroot headers"
+mkdir -p "${SYSROOT}/include/sys"
+for h in syscall.h; do
+    if [[ -f "$ADROS_ROOT/user/ulibc/include/$h" ]]; then
+        cp "$ADROS_ROOT/user/ulibc/include/$h" "${SYSROOT}/include/$h"
+        step "Installed $h"
+    fi
 done
 
+# Create sys/ioctl.h stub (newlib doesn't provide one)
+if [[ ! -f "${SYSROOT}/include/sys/ioctl.h" ]]; then
+    cat > "${SYSROOT}/include/sys/ioctl.h" << 'IOCTL_H'
+#ifndef _SYS_IOCTL_H
+#define _SYS_IOCTL_H
+#define TIOCGPGRP  0x540F
+#define TIOCSPGRP  0x5410
+#define TIOCGWINSZ 0x5413
+#define TIOCSWINSZ 0x5414
+#define FIONREAD   0x541B
+struct winsize {
+    unsigned short ws_row;
+    unsigned short ws_col;
+    unsigned short ws_xpixel;
+    unsigned short ws_ypixel;
+};
+int ioctl(int fd, unsigned long request, ...);
+#endif
+IOCTL_H
+    step "Created sys/ioctl.h stub"
+fi
+
+# Create sys/termios.h (newlib's termios.h includes it but doesn't provide it)
+if [[ ! -f "${SYSROOT}/include/sys/termios.h" ]]; then
+    cat > "${SYSROOT}/include/sys/termios.h" << 'TERMIOS_H'
+#ifndef _SYS_TERMIOS_H
+#define _SYS_TERMIOS_H
+#include <sys/types.h>
+#define NCCS 32
+typedef unsigned int tcflag_t;
+typedef unsigned char cc_t;
+typedef unsigned int speed_t;
+struct termios {
+    tcflag_t c_iflag; tcflag_t c_oflag;
+    tcflag_t c_cflag; tcflag_t c_lflag;
+    cc_t c_cc[NCCS]; speed_t c_ispeed; speed_t c_ospeed;
+};
+#define ECHO 0x0008
+#define ECHOE 0x0010
+#define ECHOK 0x0020
+#define ECHONL 0x0040
+#define ICANON 0x0002
+#define ISIG 0x0001
+#define NOFLSH 0x0080
+#define TOSTOP 0x0100
+#define IEXTEN 0x8000
+#define ECHOCTL 0x0200
+#define ECHOKE 0x0400
+#define IGNBRK 0x0001
+#define BRKINT 0x0002
+#define IGNPAR 0x0004
+#define PARMRK 0x0008
+#define INPCK 0x0010
+#define ISTRIP 0x0020
+#define INLCR 0x0040
+#define IGNCR 0x0080
+#define ICRNL 0x0100
+#define IXON 0x0400
+#define IXOFF 0x1000
+#define IXANY 0x0800
+#define OPOST 0x0001
+#define ONLCR 0x0004
+#define CSIZE 0x0030
+#define CS5 0x0000
+#define CS6 0x0010
+#define CS7 0x0020
+#define CS8 0x0030
+#define CSTOPB 0x0040
+#define CREAD 0x0080
+#define PARENB 0x0100
+#define HUPCL 0x0400
+#define CLOCAL 0x0800
+#define VEOF 0
+#define VEOL 1
+#define VERASE 2
+#define VKILL 3
+#define VINTR 4
+#define VQUIT 5
+#define VSUSP 6
+#define VSTART 7
+#define VSTOP 8
+#define VMIN 9
+#define VTIME 10
+#define TCSANOW 0
+#define TCSADRAIN 1
+#define TCSAFLUSH 2
+#define TCOON 1
+#define TCOOFF 0
+#define TCION 2
+#define TCIOFF 3
+#define TCIFLUSH 0
+#define TCOFLUSH 1
+#define TCIOFLUSH 2
+#define B0 0
+#define B9600 13
+#define B19200 14
+#define B38400 15
+#define B57600 16
+#define B115200 17
+int tcgetattr(int, struct termios *);
+int tcsetattr(int, int, const struct termios *);
+pid_t tcgetpgrp(int);
+int tcsetpgrp(int, pid_t);
+speed_t cfgetispeed(const struct termios *);
+speed_t cfgetospeed(const struct termios *);
+int cfsetispeed(struct termios *, speed_t);
+int cfsetospeed(struct termios *, speed_t);
+int tcsendbreak(int, int);
+#endif
+TERMIOS_H
+    step "Created sys/termios.h stub"
+fi
+
+# Create sys/dirent.h (newlib's default one just #errors)
+if grep -q '#error' "${SYSROOT}/include/sys/dirent.h" 2>/dev/null; then
+    cat > "${SYSROOT}/include/sys/dirent.h" << 'DIRENT_H'
+#ifndef _SYS_DIRENT_H
+#define _SYS_DIRENT_H
+#include <sys/types.h>
+#define MAXNAMLEN 255
+struct dirent {
+    ino_t  d_ino;
+    char   d_name[MAXNAMLEN + 1];
+};
+typedef struct {
+    int dd_fd; int dd_loc; int dd_size; char *dd_buf;
+} DIR;
+DIR *opendir(const char *);
+struct dirent *readdir(DIR *);
+int closedir(DIR *);
+void rewinddir(DIR *);
+#endif
+DIRENT_H
+    step "Created sys/dirent.h stub"
+fi
+
 # ==================================================================
 # STEP 1: Build Binutils
 # ==================================================================
@@ -511,6 +658,36 @@ else
     step "Newlib already installed"
 fi
 
+# ==================================================================
+# STEP 3b: Build libgloss/adros (crt0.o + libadros.a)
+# ==================================================================
+msg "Building libgloss/adros (crt0.o + libadros.a)"
+if [[ ! -f "${SYSROOT}/lib/libadros.a" ]]; then
+    local_gloss="$SRC_DIR/newlib-${NEWLIB_VER}/libgloss/adros"
+    ${TARGET}-gcc -c "$local_gloss/crt0.S"     -o /tmp/adros_crt0.o
+    ${TARGET}-gcc -c "$local_gloss/syscalls.c"     -o /tmp/adros_syscalls.o
+    ${TARGET}-gcc -c "$local_gloss/posix_stubs.c"  -o /tmp/adros_posix_stubs.o
+    ${TARGET}-ar rcs /tmp/libadros.a /tmp/adros_syscalls.o /tmp/adros_posix_stubs.o
+    cp /tmp/adros_crt0.o   "${SYSROOT}/lib/crt0.o"
+    cp /tmp/libadros.a     "${SYSROOT}/lib/libadros.a"
+    rm -f /tmp/adros_crt0.o /tmp/adros_syscalls.o /tmp/adros_posix_stubs.o /tmp/libadros.a
+    step "crt0.o + libadros.a installed to sysroot"
+else
+    step "libadros.a already installed"
+fi
+
+# ==================================================================
+# STEP 3c: Create GCC specs file (fix LIB_SPEC link order)
+# ==================================================================
+SPECS_FILE="$PREFIX/lib/gcc/${TARGET}/${GCC_VER}/specs"
+if [[ ! -f "$SPECS_FILE" ]]; then
+    ${TARGET}-gcc -dumpspecs > "$SPECS_FILE"
+    sed -i 's/-lc -ladros -lgcc/--start-group -lc -ladros --end-group -lgcc/' "$SPECS_FILE"
+    step "Created specs file with corrected LIB_SPEC"
+else
+    step "Specs file already exists"
+fi
+
 # ==================================================================
 # STEP 4: Rebuild GCC (full, with Newlib)
 # ==================================================================
@@ -552,31 +729,194 @@ if [[ $SKIP_BASH -eq 0 ]]; then
 
     if [[ ! -f "$BUILD_DIR/bash/bash" ]]; then
         # Bash needs a config.cache for cross-compilation
+        # Comprehensive cross-compilation cache for Bash on AdrOS/newlib.
+        # Cross-compile configure tests can't run programs, so we must
+        # pre-seed results for functions/headers that newlib provides.
         cat > config.cache <<'CACHE_EOF'
+# --- Types ---
+ac_cv_type_getgroups=gid_t
+ac_cv_type_sigset_t=yes
+ac_cv_type_sig_atomic_t=yes
+ac_cv_type_clock_t=yes
+ac_cv_c_long_double=yes
+ac_cv_sizeof_int=4
+ac_cv_sizeof_long=4
+ac_cv_sizeof_char_p=4
+ac_cv_sizeof_double=8
+ac_cv_sizeof_long_long=8
+ac_cv_sizeof_intmax_t=4
+ac_cv_sizeof_wchar_t=4
+
+# --- Headers (newlib provides these) ---
+ac_cv_header_unistd_h=yes
+ac_cv_header_stdlib_h=yes
+ac_cv_header_string_h=yes
+ac_cv_header_strings_h=yes
+ac_cv_header_memory_h=yes
+ac_cv_header_locale_h=yes
+ac_cv_header_termios_h=yes
+ac_cv_header_termio_h=no
+ac_cv_header_sys_wait_h=yes
+ac_cv_header_sys_select_h=yes
+ac_cv_header_sys_file_h=no
+ac_cv_header_sys_resource_h=yes
+ac_cv_header_sys_param_h=yes
+ac_cv_header_sys_socket_h=no
+ac_cv_header_sys_ioctl_h=yes
+ac_cv_header_sys_mman_h=no
+ac_cv_header_sys_pte_h=no
+ac_cv_header_sys_ptem_h=no
+ac_cv_header_sys_stream_h=no
+ac_cv_header_dirent_h=yes
+ac_cv_header_dirent_dirent_h=yes
+ac_cv_header_grp_h=yes
+ac_cv_header_pwd_h=yes
+ac_cv_header_regex_h=yes
+ac_cv_header_fnmatch_h=yes
+ac_cv_header_dlfcn_h=no
+ac_cv_header_netdb_h=no
+ac_cv_header_netinet_in_h=no
+ac_cv_header_arpa_inet_h=no
+ac_cv_header_wctype_h=yes
+ac_cv_header_wchar_h=yes
+ac_cv_header_langinfo_h=no
+ac_cv_header_libintl_h=no
+ac_cv_header_stdint_h=yes
+ac_cv_header_inttypes_h=yes
+ac_cv_header_stdbool_h=yes
+ac_cv_header_sys_stat_h=yes
+ac_cv_header_sys_types_h=yes
+ac_cv_header_fcntl_h=yes
+ac_cv_header_signal_h=yes
+ac_cv_header_limits_h=yes
+
+# --- Functions in newlib libc.a ---
+ac_cv_func_memmove=yes
+ac_cv_func_memset=yes
+ac_cv_func_strchr=yes
+ac_cv_func_strerror=yes
+ac_cv_func_strtol=yes
+ac_cv_func_strtoul=yes
+ac_cv_func_strtod=yes
+ac_cv_func_strtoimax=yes
+ac_cv_func_strtoumax=yes
+ac_cv_func_snprintf=yes
+ac_cv_func_vsnprintf=yes
+ac_cv_func_setlocale=yes
+ac_cv_func_putenv=yes
+ac_cv_func_setenv=yes
+ac_cv_func_mkstemp=yes
+ac_cv_func_rename=yes
+ac_cv_func_mbrtowc=yes
+ac_cv_func_wcrtomb=yes
+ac_cv_func_wctomb=yes
+ac_cv_func_mbrlen=yes
+ac_cv_func_regcomp=yes
+ac_cv_func_regexec=yes
+ac_cv_func_fnmatch=yes
+ac_cv_func_strsignal=yes
+ac_cv_func_raise=yes
+ac_cv_func_getopt=no
+
+# --- Functions provided by libadros.a stubs ---
+ac_cv_func_dup2=yes
+ac_cv_func_fcntl=yes
+ac_cv_func_getcwd=yes
+ac_cv_func_pipe=yes
+ac_cv_func_select=yes
+ac_cv_func_pselect=yes
+ac_cv_func_chown=yes
+ac_cv_func_lstat=yes
+ac_cv_func_readlink=no
+ac_cv_func_killpg=yes
+ac_cv_func_tcgetattr=yes
+ac_cv_func_tcsetattr=yes
+ac_cv_func_tcgetpgrp=yes
+ac_cv_func_tcsetpgrp=yes
+ac_cv_func_tcsendbreak=no
+ac_cv_func_cfgetospeed=no
+ac_cv_func_sigaction=yes
+ac_cv_func_sigprocmask=yes
+ac_cv_func_siginterrupt=no
+ac_cv_func_waitpid=yes
+ac_cv_func_gethostname=yes
+ac_cv_func_getpwnam=yes
+ac_cv_func_getpwuid=yes
+ac_cv_func_getpwent=no
+ac_cv_func_getgroups=no
+ac_cv_func_getrlimit=no
+ac_cv_func_setrlimit=no
+ac_cv_func_sysconf=no
+ac_cv_func_pathconf=no
+ac_cv_func_getpagesize=no
+ac_cv_func_getdtablesize=no
+ac_cv_func_mkfifo=yes
+ac_cv_func_opendir=yes
+ac_cv_func_readdir=yes
+ac_cv_func_closedir=yes
 ac_cv_func_mmap_fixed_mapped=no
 ac_cv_func_setvbuf_reversed=no
 ac_cv_func_strcoll_works=yes
 ac_cv_func_working_mktime=yes
-ac_cv_type_getgroups=gid_t
+ac_cv_func_getenv=yes
+ac_cv_func_setpgid=yes
+ac_cv_func_setsid=yes
+ac_cv_func_getpgrp=yes
+ac_cv_func_setpgrp=no
+ac_cv_func_getpeername=no
+ac_cv_func_gethostbyname=no
+ac_cv_func_getaddrinfo=no
+ac_cv_func_getservbyname=no
+ac_cv_func_getservent=no
+ac_cv_func_inet_aton=no
+ac_cv_func_dlopen=no
+ac_cv_func_dlclose=no
+ac_cv_func_dlsym=no
+ac_cv_func_confstr=no
+ac_cv_func_eaccess=no
+ac_cv_func_faccessat=no
+ac_cv_func_arc4random=no
+ac_cv_func_getrandom=no
+ac_cv_func_getentropy=no
+ac_cv_func_iconv=no
+ac_cv_func_getwd=no
+ac_cv_func_doprnt=no
+ac_cv_func_mbscasecmp=no
+ac_cv_func_mbschr=no
+ac_cv_func_mbscmp=no
+ac_cv_func_setdtablesize=no
+ac_cv_func_getrusage=no
+ac_cv_func_locale_charset=no
+
+# --- Bash-specific cross-compile overrides ---
 ac_cv_rl_version=8.2
-bash_cv_func_sigsetjmp=present
+bash_cv_func_sigsetjmp=missing
 bash_cv_func_ctype_nonascii=no
-bash_cv_must_reinstall_sighandlers=no
 bash_cv_func_snprintf=yes
 bash_cv_func_vsnprintf=yes
 bash_cv_printf_a_format=no
+bash_cv_must_reinstall_sighandlers=no
 bash_cv_pgrp_pipe=no
 bash_cv_sys_named_pipes=missing
 bash_cv_job_control_missing=present
 bash_cv_sys_siglist=yes
 bash_cv_under_sys_siglist=yes
 bash_cv_opendir_not_robust=no
-bash_cv_ulimit_maxfds=yes
+bash_cv_ulimit_maxfds=no
 bash_cv_getenv_redef=yes
 bash_cv_getcwd_malloc=yes
 bash_cv_type_rlimit=long
 bash_cv_type_intmax_t=int
 bash_cv_type_uintmax_t=unsigned
+bash_cv_posix_signals=yes
+bash_cv_bsd_signals=no
+bash_cv_sysv_signals=no
+bash_cv_speed_t_in_sys_types=no
+bash_cv_struct_winsize_header=other
+bash_cv_struct_winsize_in_ioctl=yes
+bash_cv_tiocstat_in_ioctl=no
+bash_cv_tiocgwinsz_in_ioctl=yes
+bash_cv_unusedvar=yes
 CACHE_EOF
 
         "$SRC_DIR/bash-${BASH_VER}/configure" \
@@ -588,8 +928,8 @@ CACHE_EOF
             CC="${TARGET}-gcc" \
             AR="${TARGET}-ar" \
             RANLIB="${TARGET}-ranlib" \
-            CFLAGS="-Os -static" \
-            LDFLAGS="-static" \
+            CFLAGS="-Os -static -D_POSIX_VERSION=200112L" \
+            LDFLAGS="-static -Wl,--allow-multiple-definition" \
             2>&1 | tee "$LOG_DIR/bash-configure.log"
 
         make -j"$JOBS" 2>&1 | tee "$LOG_DIR/bash-build.log"