// // This file is part of the uOS++ III distribution // Parts of this file are from the newlib sources, issued under GPL. // Copyright (c) 2014 Liviu Ionescu // // ---------------------------------------------------------------------------- int errno; void *__dso_handle __attribute__ ((weak)); // ---------------------------------------------------------------------------- #if !defined(OS_USE_SEMIHOSTING) #include <_ansi.h> #include <_syslist.h> #include //#include #include #include #include #include #include void __initialize_args(int* p_argc, char*** p_argv); // This is the standard default implementation for the routine to // process args. It returns a single empty arg. // For semihosting applications, this is redefined to get the real // args from the debugger. You can also use it if you decide to keep // some args in a non-volatile memory. void __attribute__((weak)) __initialize_args(int* p_argc, char*** p_argv) { // By the time we reach this, the data and bss should have been initialised. // The strings pointed to by the argv array shall be modifiable by the // program, and retain their last-stored values between program startup // and program termination. (static, no const) static char name[] = ""; // The string pointed to by argv[0] represents the program name; // argv[0][0] shall be the null character if the program name is not // available from the host environment. argv[argc] shall be a null pointer. // (static, no const) static char* argv[2] = { name, NULL }; *p_argc = 1; *p_argv = &argv[0]; return; } // These functions are defined here to avoid linker errors in freestanding // applications. They might be called in some error cases from library // code. // // If you detect other functions to be needed, just let us know // and we'll add them. int raise(int sig __attribute__((unused))) { errno = ENOSYS; return -1; } int kill(pid_t pid, int sig); int kill(pid_t pid __attribute__((unused)), int sig __attribute__((unused))) { errno = ENOSYS; return -1; } #endif // !defined(OS_USE_SEMIHOSTING) // ---------------------------------------------------------------------------- // If you need the empty definitions, remove the -ffreestanding option. #if __STDC_HOSTED__ == 1 char* __env[1] = { 0 }; char** environ = __env; #if !defined(OS_USE_SEMIHOSTING) // Forward declarations int _chown(const char* path, uid_t owner, gid_t group); int _close(int fildes); int _execve(char* name, char** argv, char** env); int _fork(void); int _fstat(int fildes, struct stat* st); int _getpid(void); int _gettimeofday(struct timeval* ptimeval, void* ptimezone); int _isatty(int file); int _kill(int pid, int sig); int _link(char* existing, char* _new); int _lseek(int file, int ptr, int dir); int _open(char* file, int flags, int mode); int _read(int file, char* ptr, int len); int _readlink(const char* path, char* buf, size_t bufsize); int _stat(const char* file, struct stat* st); int _symlink(const char* path1, const char* path2); clock_t _times(struct tms* buf); int _unlink(char* name); int _wait(int* status); int _write(int file, char* ptr, int len); // Definitions int __attribute__((weak)) _chown(const char* path __attribute__((unused)), uid_t owner __attribute__((unused)), gid_t group __attribute__((unused))) { errno = ENOSYS; return -1; } int __attribute__((weak)) _close(int fildes __attribute__((unused))) { errno = ENOSYS; return -1; } int __attribute__((weak)) _execve(char* name __attribute__((unused)), char** argv __attribute__((unused)), char** env __attribute__((unused))) { errno = ENOSYS; return -1; } int __attribute__((weak)) _fork(void) { errno = ENOSYS; return -1; } int __attribute__((weak)) _fstat(int fildes __attribute__((unused)), struct stat* st __attribute__((unused))) { errno = ENOSYS; return -1; } int __attribute__((weak)) _getpid(void) { errno = ENOSYS; return -1; } int __attribute__((weak)) _gettimeofday(struct timeval* ptimeval __attribute__((unused)), void* ptimezone __attribute__((unused))) { errno = ENOSYS; return -1; } int __attribute__((weak)) _isatty(int file __attribute__((unused))) { errno = ENOSYS; return 0; } int __attribute__((weak)) _kill(int pid __attribute__((unused)), int sig __attribute__((unused))) { errno = ENOSYS; return -1; } int __attribute__((weak)) _link(char* existing __attribute__((unused)), char* _new __attribute__((unused))) { errno = ENOSYS; return -1; } int __attribute__((weak)) _lseek(int file __attribute__((unused)), int ptr __attribute__((unused)), int dir __attribute__((unused))) { errno = ENOSYS; return -1; } int __attribute__((weak)) _open(char* file __attribute__((unused)), int flags __attribute__((unused)), int mode __attribute__((unused))) { errno = ENOSYS; return -1; } int __attribute__((weak)) _read(int file __attribute__((unused)), char* ptr __attribute__((unused)), int len __attribute__((unused))) { errno = ENOSYS; return -1; } int __attribute__((weak)) _readlink(const char* path __attribute__((unused)), char* buf __attribute__((unused)), size_t bufsize __attribute__((unused))) { errno = ENOSYS; return -1; } int __attribute__((weak)) _stat(const char* file __attribute__((unused)), struct stat* st __attribute__((unused))) { errno = ENOSYS; return -1; } int __attribute__((weak)) _symlink(const char* path1 __attribute__((unused)), const char* path2 __attribute__((unused))) { errno = ENOSYS; return -1; } clock_t __attribute__((weak)) _times(struct tms* buf __attribute__((unused))) { errno = ENOSYS; return ((clock_t) -1); } int __attribute__((weak)) _unlink(char* name __attribute__((unused))) { errno = ENOSYS; return -1; } int __attribute__((weak)) _wait(int* status __attribute__((unused))) { errno = ENOSYS; return -1; } int __attribute__((weak)) _write(int file __attribute__((unused)), char* ptr __attribute__((unused)), int len __attribute__((unused))) { errno = ENOSYS; return -1; } // ---------------------------------------------------------------------------- #else // defined(OS_USE_SEMIHOSTING) // ---------------------------------------------------------------------------- /* Support files for GNU libc. Files in the system namespace go here. Files in the C namespace (ie those that do not start with an underscore) go in .c. */ #include <_ansi.h> #include //#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "semihosting.h" int _kill (int pid, int sig); void __attribute__((noreturn)) _exit (int status); // Forward declarations. int _system (const char*); int _rename (const char*, const char*); int _isatty (int); clock_t _times (struct tms*); int _gettimeofday (struct timeval *, void*); int _unlink (const char*); int _link (void); int _stat (const char*, struct stat*); int _fstat (int, struct stat*); int _swistat (int fd, struct stat* st); int _getpid (int); int _close (int); clock_t _clock (void); int _swiclose (int); int _open (const char*, int, ...); int _swiopen (const char*, int); int _write (int, char*, int); int _swiwrite (int, char*, int); int _lseek (int, int, int); int _swilseek (int, int, int); int _read (int, char*, int); int _swiread (int, char*, int); void initialise_monitor_handles (void); void __initialize_args (int* p_argc, char*** p_argv); static int checkerror (int); static int error (int); static int get_errno (void); // ---------------------------------------------------------------------------- #define ARGS_BUF_ARRAY_SIZE 80 #define ARGV_BUF_ARRAY_SIZE 10 typedef struct { char* pCommandLine; int size; } CommandLineBlock; void __initialize_args (int* p_argc, char*** p_argv) { // Array of chars to receive the command line from the host static char args_buf[ARGS_BUF_ARRAY_SIZE]; // Array of pointers to store the final argv pointers (pointing // in the above array). static char* argv_buf[ARGV_BUF_ARRAY_SIZE]; int argc = 0; int isInArgument = 0; CommandLineBlock cmdBlock; cmdBlock.pCommandLine = args_buf; cmdBlock.size = sizeof(args_buf) - 1; int ret = call_host (SEMIHOSTING_SYS_GET_CMDLINE, &cmdBlock); if (ret == 0) { // In case the host send more than we can chew, limit the // string to our buffer. args_buf[ARGS_BUF_ARRAY_SIZE - 1] = '\0'; // The command line is a null terminated string char* p = cmdBlock.pCommandLine; int delim = '\0'; int ch; while ((ch = *p) != '\0') { if (isInArgument == 0) { if (!isblank(ch)) { if (argc >= (int) ((sizeof(argv_buf) / sizeof(argv_buf[0])) - 1)) break; if (ch == '"' || ch == '\'') { // Remember the delimiter to search for the // corresponding terminator delim = ch; ++p; // skip the delimiter ch = *p; } // Remember the arg beginning address argv_buf[argc++] = p; isInArgument = 1; } } else if (delim != '\0') { if ((ch == delim)) { delim = '\0'; *p = '\0'; isInArgument = 0; } } else if (isblank(ch)) { delim = '\0'; *p = '\0'; isInArgument = 0; } ++p; } } if (argc == 0) { // No args found in string, return a single empty name. args_buf[0] = '\0'; argv_buf[0] = &args_buf[0]; ++argc; } // Must end the array with a null pointer. argv_buf[argc] = NULL; *p_argc = argc; *p_argv = &argv_buf[0]; // temporary here initialise_monitor_handles (); return; } // ---------------------------------------------------------------------------- void _exit (int status) { /* There is only one SWI for both _exit and _kill. For _exit, call the SWI with the second argument set to -1, an invalid value for signum, so that the SWI handler can distinguish the two calls. Note: The RDI implementation of _kill throws away both its arguments. */ report_exception (status == 0 ? ADP_Stopped_ApplicationExit : ADP_Stopped_RunTimeError); } // ---------------------------------------------------------------------------- int __attribute__((weak)) _kill (int pid __attribute__((unused)), int sig __attribute__((unused))) { errno = ENOSYS; return -1; } // ---------------------------------------------------------------------------- /* Struct used to keep track of the file position, just so we can implement fseek(fh,x,SEEK_CUR). */ struct fdent { int handle; int pos; }; #define MAX_OPEN_FILES 20 /* User file descriptors (fd) are integer indexes into the openfiles[] array. Error checking is done by using findslot(). This openfiles array is manipulated directly by only these 5 functions: findslot() - Translate entry. newslot() - Find empty entry. initilise_monitor_handles() - Initialize entries. _swiopen() - Initialize entry. _close() - Handle stdout == stderr case. Every other function must use findslot(). */ static struct fdent openfiles[MAX_OPEN_FILES]; static struct fdent* findslot (int); static int newslot (void); /* Register name faking - works in collusion with the linker. */ register char* stack_ptr asm ("sp"); /* following is copied from libc/stdio/local.h to check std streams */ extern void __sinit(struct _reent*); #define CHECK_INIT(ptr) \ do \ { \ if ((ptr) && !(ptr)->__sdidinit) \ __sinit (ptr); \ } \ while (0) static int monitor_stdin; static int monitor_stdout; static int monitor_stderr; /* Return a pointer to the structure associated with the user file descriptor fd. */ static struct fdent* findslot (int fd) { CHECK_INIT(_REENT); /* User file descriptor is out of range. */ if ((unsigned int) fd >= MAX_OPEN_FILES) { return NULL; } /* User file descriptor is open? */ if (openfiles[fd].handle == -1) { return NULL; } /* Valid. */ return &openfiles[fd]; } /* Return the next lowest numbered free file structure, or -1 if we can't find one. */ static int newslot (void) { int i; for (i = 0; i < MAX_OPEN_FILES; i++) { if (openfiles[i].handle == -1) { break; } } if (i == MAX_OPEN_FILES) { return -1; } return i; } void initialise_monitor_handles (void) { int i; /* Open the standard file descriptors by opening the special * teletype device, ":tt", read-only to obtain a descriptor for * standard input and write-only to obtain a descriptor for standard * output. Finally, open ":tt" in append mode to obtain a descriptor * for standard error. Since this is a write mode, most kernels will * probably return the same value as for standard output, but the * kernel can differentiate the two using the mode flag and return a * different descriptor for standard error. */ int volatile block[3]; block[0] = (int) ":tt"; block[2] = 3; /* length of filename */ block[1] = 0; /* mode "r" */ monitor_stdin = call_host (SEMIHOSTING_SYS_OPEN, (void*) block); block[0] = (int) ":tt"; block[2] = 3; /* length of filename */ block[1] = 4; /* mode "w" */ monitor_stdout = call_host (SEMIHOSTING_SYS_OPEN, (void*) block); block[0] = (int) ":tt"; block[2] = 3; /* length of filename */ block[1] = 8; /* mode "a" */ monitor_stderr = call_host (SEMIHOSTING_SYS_OPEN, (void*) block); /* If we failed to open stderr, redirect to stdout. */ if (monitor_stderr == -1) { monitor_stderr = monitor_stdout; } for (i = 0; i < MAX_OPEN_FILES; i++) { openfiles[i].handle = -1; } openfiles[0].handle = monitor_stdin; openfiles[0].pos = 0; openfiles[1].handle = monitor_stdout; openfiles[1].pos = 0; openfiles[2].handle = monitor_stderr; openfiles[2].pos = 0; } static int get_errno (void) { return call_host (SEMIHOSTING_SYS_ERRNO, NULL); } /* Set errno and return result. */ static int error (int result) { errno = get_errno (); return result; } /* Check the return and set errno appropriately. */ static int checkerror (int result) { if (result == -1) { return error (-1); } return result; } /* fh, is a valid internal file handle. ptr, is a null terminated string. len, is the length in bytes to read. Returns the number of bytes *not* written. */ int _swiread (int fh, char* ptr, int len) { int block[3]; block[0] = fh; block[1] = (int) ptr; block[2] = len; return checkerror (call_host (SEMIHOSTING_SYS_READ, block)); } /* fd, is a valid user file handle. Translates the return of _swiread into bytes read. */ int _read (int fd, char* ptr, int len) { int res; struct fdent *pfd; pfd = findslot (fd); if (pfd == NULL) { errno = EBADF; return -1; } res = _swiread (pfd->handle, ptr, len); if (res == -1) { return res; } pfd->pos += len - res; /* res == len is not an error, at least if we want feof() to work. */ return len - res; } /* fd, is a user file descriptor. */ int _swilseek (int fd, int ptr, int dir) { int res; struct fdent *pfd; /* Valid file descriptor? */ pfd = findslot (fd); if (pfd == NULL) { errno = EBADF; return -1; } /* Valid whence? */ if ((dir != SEEK_CUR) && (dir != SEEK_SET) && (dir != SEEK_END)) { errno = EINVAL; return -1; } /* Convert SEEK_CUR to SEEK_SET */ if (dir == SEEK_CUR) { ptr = pfd->pos + ptr; /* The resulting file offset would be negative. */ if (ptr < 0) { errno = EINVAL; if ((pfd->pos > 0) && (ptr > 0)) { errno = EOVERFLOW; } return -1; } dir = SEEK_SET; } int block[2]; if (dir == SEEK_END) { block[0] = pfd->handle; res = checkerror (call_host (SEMIHOSTING_SYS_FLEN, block)); if (res == -1) { return -1; } ptr += res; } /* This code only does absolute seeks. */ block[0] = pfd->handle; block[1] = ptr; res = checkerror (call_host (SEMIHOSTING_SYS_SEEK, block)); /* At this point ptr is the current file position. */ if (res >= 0) { pfd->pos = ptr; return ptr; } else { return -1; } } int _lseek (int fd, int ptr, int dir) { return _swilseek (fd, ptr, dir); } /* fh, is a valid internal file handle. Returns the number of bytes *not* written. */ int _swiwrite (int fh, char* ptr, int len) { int block[3]; block[0] = fh; block[1] = (int) ptr; block[2] = len; return checkerror (call_host (SEMIHOSTING_SYS_WRITE, block)); } /* fd, is a user file descriptor. */ int _write (int fd, char* ptr, int len) { int res; struct fdent *pfd; pfd = findslot (fd); if (pfd == NULL) { errno = EBADF; return -1; } res = _swiwrite (pfd->handle, ptr, len); /* Clearly an error. */ if (res < 0) { return -1; } pfd->pos += len - res; /* We wrote 0 bytes? Retrieve errno just in case. */ if ((len - res) == 0) { return error (0); } return (len - res); } int _swiopen (const char* path, int flags) { int aflags = 0, fh; uint32_t block[3]; int fd = newslot (); if (fd == -1) { errno = EMFILE; return -1; } /* It is an error to open a file that already exists. */ if ((flags & O_CREAT) && (flags & O_EXCL)) { struct stat st; int res; res = _stat (path, &st); if (res != -1) { errno = EEXIST; return -1; } } /* The flags are Unix-style, so we need to convert them. */ #ifdef O_BINARY if (flags & O_BINARY) { aflags |= 1; } #endif /* In O_RDONLY we expect aflags == 0. */ if (flags & O_RDWR) { aflags |= 2; } if ((flags & O_CREAT) || (flags & O_TRUNC) || (flags & O_WRONLY)) { aflags |= 4; } if (flags & O_APPEND) { /* Can't ask for w AND a; means just 'a'. */ aflags &= ~4; aflags |= 8; } block[0] = (uint32_t) path; block[2] = strlen (path); block[1] = (uint32_t) aflags; fh = call_host (SEMIHOSTING_SYS_OPEN, block); /* Return a user file descriptor or an error. */ if (fh >= 0) { openfiles[fd].handle = fh; openfiles[fd].pos = 0; return fd; } else { return error (fh); } } int _open (const char* path, int flags, ...) { return _swiopen (path, flags); } /* fh, is a valid internal file handle. */ int _swiclose (int fh) { return checkerror (call_host (SEMIHOSTING_SYS_CLOSE, &fh)); } /* fd, is a user file descriptor. */ int _close (int fd) { int res; struct fdent *pfd; pfd = findslot (fd); if (pfd == NULL) { errno = EBADF; return -1; } /* Handle stderr == stdout. */ if ((fd == 1 || fd == 2) && (openfiles[1].handle == openfiles[2].handle)) { pfd->handle = -1; return 0; } /* Attempt to close the handle. */ res = _swiclose (pfd->handle); /* Reclaim handle? */ if (res == 0) { pfd->handle = -1; } return res; } int __attribute__((weak)) _getpid (int n __attribute__ ((unused))) { return 1; } int _swistat (int fd, struct stat* st) { struct fdent *pfd; int res; pfd = findslot (fd); if (pfd == NULL) { errno = EBADF; return -1; } /* Always assume a character device, with 1024 byte blocks. */ st->st_mode |= S_IFCHR; st->st_blksize = 1024; res = checkerror (call_host (SEMIHOSTING_SYS_FLEN, &pfd->handle)); if (res == -1) { return -1; } /* Return the file size. */ st->st_size = res; return 0; } int __attribute__((weak)) _fstat (int fd, struct stat* st) { memset (st, 0, sizeof(*st)); return _swistat (fd, st); } int __attribute__((weak)) _stat (const char*fname, struct stat *st) { int fd, res; memset (st, 0, sizeof(*st)); /* The best we can do is try to open the file readonly. If it exists, then we can guess a few things about it. */ if ((fd = _open (fname, O_RDONLY)) == -1) { return -1; } st->st_mode |= S_IFREG | S_IREAD; res = _swistat (fd, st); /* Not interested in the error. */ _close (fd); return res; } int __attribute__((weak)) _link (void) { errno = ENOSYS; return -1; } int _unlink (const char* path) { int res; uint32_t block[2]; block[0] = (uint32_t) path; block[1] = strlen (path); res = call_host (SEMIHOSTING_SYS_REMOVE, block); if (res == -1) { return error (res); } return 0; } int _gettimeofday (struct timeval* tp, void* tzvp) { struct timezone* tzp = tzvp; if (tp) { /* Ask the host for the seconds since the Unix epoch. */ tp->tv_sec = call_host (SEMIHOSTING_SYS_TIME, NULL); tp->tv_usec = 0; } /* Return fixed data for the timezone. */ if (tzp) { tzp->tz_minuteswest = 0; tzp->tz_dsttime = 0; } return 0; } /* Return a clock that ticks at 100Hz. */ clock_t _clock (void) { clock_t timeval; timeval = (clock_t) call_host (SEMIHOSTING_SYS_CLOCK, NULL); return timeval; } /* Return a clock that ticks at 100Hz. */ clock_t _times (struct tms* tp) { clock_t timeval = _clock (); if (tp) { tp->tms_utime = timeval; /* user time */ tp->tms_stime = 0; /* system time */ tp->tms_cutime = 0; /* user time, children */ tp->tms_cstime = 0; /* system time, children */ } return timeval; } int _isatty (int fd) { struct fdent *pfd; int tty; pfd = findslot (fd); if (pfd == NULL) { errno = EBADF; return 0; } tty = call_host (SEMIHOSTING_SYS_ISTTY, &pfd->handle); if (tty == 1) { return 1; } errno = get_errno (); return 0; } int _system (const char* s) { uint32_t block[2]; int e; /* Hmmm. The ARM debug interface specification doesn't say whether SYS_SYSTEM does the right thing with a null argument, or assign any meaning to its return value. Try to do something reasonable.... */ if (!s) { return 1; /* maybe there is a shell available? we can hope. :-P */ } block[0] = (uint32_t) s; block[1] = strlen (s); e = checkerror (call_host (SEMIHOSTING_SYS_SYSTEM, block)); if ((e >= 0) && (e < 256)) { /* We have to convert e, an exit status to the encoded status of the command. To avoid hard coding the exit status, we simply loop until we find the right position. */ int exit_code; for (exit_code = e; e && WEXITSTATUS (e) != exit_code; e <<= 1) { continue; } } return e; } int _rename (const char* oldpath, const char* newpath) { uint32_t block[4]; block[0] = (uint32_t) oldpath; block[1] = strlen (oldpath); block[2] = (uint32_t) newpath; block[3] = strlen (newpath); return checkerror (call_host (SEMIHOSTING_SYS_RENAME, block)) ? -1 : 0; } // ---------------------------------------------------------------------------- // Required by Google Tests int mkdir (const char *path __attribute__((unused)), mode_t mode __attribute__((unused))) { #if 0 // always return true return 0; #else errno = ENOSYS; return -1; #endif } char *getcwd (char *buf, size_t size) { // no cwd available via semihosting, so we use the temporary folder strncpy (buf, "/tmp", size); return buf; } #endif // defined OS_USE_SEMIHOSTING #endif // __STDC_HOSTED__ == 1