The low-level support in libgloss and newlib sycall emulation for
stat() is very basic, since the semihosting API only supports
functions defined by ISO C, while stat() is part of POSIX.
Unfortunately, this causes problems for some fortran support which
tries to use stat() to see if certain calls involve re-opening a file.
In particular it looks at the inode and dev fields.
We can't find the real inode numbers, but we can do a bit better than
simply giving every file inode 0 and dev 0. This patch fakes an inode
number by generating a simple hash of the file's name and sets the dev
field to 1 or 0 depending on whether or not the file is one of the
std{in,out,err} streams. While this is far from perfect, it does
ensure that if the same file/filename is passed to the stat()/fstat()
api then we get the same inode number. It's certainly enough to fix a
number of tests in the fortran testsuite of GCC.
Unfortunately, libgloss and newlib's syscall code have diverged
somewhat, so there are two implementations in this patch. They do the
same thing, but have to be coded differently due to some changes that
have not been synced across the two implementations. Maybe some day
I'll have time to rework newlib code to bring it closer to that in
libgloss. Or maybe the time is now right to move away from the newlib
embedded syscalls code entirely...
---
libgloss/arm/syscalls.c | 33 +++++++++++++-
newlib/libc/sys/arm/syscalls.c | 83 +++++++++++++++++++++++++++++-----
2 files changed, 103 insertions(+), 13 deletions(-)
I'll wait a couple of days before pushing this, just in case somebody
would like to comment.
@@ -56,6 +56,8 @@ struct fdent
{
int handle;
int pos;
+ dev_t dev;
+ ino_t inode; /* Not really an inode, just a hash of the filename. */
};
#define MAX_OPEN_FILES 20
@@ -210,13 +212,19 @@ initialise_monitor_handles (void)
openfiles[0].handle = monitor_stdin;
openfiles[0].pos = 0;
+ openfiles[0].dev = 1;
+ openfiles[0].inode = 0;
if (_has_ext_stdout_stderr ())
{
openfiles[1].handle = monitor_stdout;
openfiles[1].pos = 0;
+ openfiles[1].dev = 1;
+ openfiles[1].inode = 1;
openfiles[2].handle = monitor_stderr;
openfiles[2].pos = 0;
+ openfiles[2].dev = 1;
+ openfiles[2].inode = 1; /* Same as stdout. */
}
}
@@ -550,6 +558,25 @@ _write (int fd, const void * ptr, size_t len)
return (len - res);
}
+static inline unsigned
+rotate (unsigned v)
+{
+ return (v << 7) | (v >> (32 - 7));
+}
+
+/* Generate a hash of the file opened, so that we have something we
+ can tell stat about this file. We don't have real inodes, so this
+ is a poor-man's substitute, but should be good enough for most
+ purposes here. */
+static inline unsigned
+fn_hash (const char *path)
+{
+ unsigned hash = 0;
+ while (*path)
+ hash = rotate (hash) ^ *path++;
+ return hash;
+}
+
int
_swiopen (const char * path, int flags)
{
@@ -621,6 +648,8 @@ _swiopen (const char * path, int flags)
{
openfiles[fd].handle = fh;
openfiles[fd].pos = 0;
+ openfiles[fd].dev = 0;
+ openfiles[fd].inode = fn_hash (path);
return fd;
}
else
@@ -755,6 +784,8 @@ _swistat (int fd, struct stat * st)
return -1;
/* Return the file size. */
st->st_size = res;
+ st->st_dev = pfd->dev;
+ st->st_ino = pfd->inode; /* Faked */
return 0;
}
@@ -774,8 +805,8 @@ _stat (const char *fname, struct stat *st)
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);
+ st->st_mode |= S_IFREG | S_IREAD;
/* Not interested in the error. */
_close (fd);
return res;
@@ -79,6 +79,8 @@ typedef struct
{
int handle;
off_t pos;
+ dev_t dev;
+ ino_t inode;
}
poslog;
@@ -163,8 +165,12 @@ initialise_monitor_handles (void)
openfiles[0].handle = monitor_stdin;
openfiles[0].pos = 0;
+ openfiles[0].dev = 1;
+ openfiles[0].inode = 0;
openfiles[1].handle = monitor_stdout;
openfiles[1].pos = 0;
+ openfiles[1].dev = 1;
+ openfiles[1].inode = 1;
}
static int
@@ -353,6 +359,25 @@ _write (int file, const void * ptr, size_t len)
return len - x;
}
+static inline unsigned
+rotate (unsigned v)
+{
+ return (v << 7) | (v >> (32 - 7));
+}
+
+/* Generate a hash of the file opened, so that we have something we
+ can tell stat about this file. We don't have real inodes, so this
+ is a poor-man's substitute, but should be good enough for most
+ purposes here. */
+static inline unsigned
+fn_hash (const char *path)
+{
+ unsigned hash = 0;
+ while (*path)
+ hash = rotate (hash) ^ *path++;
+ return hash;
+}
+
extern int strlen (const char *);
int
@@ -409,6 +434,8 @@ _swiopen (const char * path, int flags)
{
openfiles[i].handle = fh;
openfiles[i].pos = 0;
+ openfiles[i].dev = 0;
+ openfiles[i].inode = fn_hash (path);
}
return fh >= 0 ? fh + FILE_HANDLE_OFFSET : error (fh);
@@ -546,31 +573,63 @@ _sbrk (ptrdiff_t incr)
extern void memset (struct stat *, int, unsigned int);
+int
+_swistat (int fd, struct stat * st)
+{
+ int slot;
+ int res;
+
+ slot = findslot (remap_handle (fd));
+ if (slot == MAX_OPEN_FILES)
+ {
+ errno = EBADF;
+ return -1;
+ }
+
+ /* Always assume a character device,
+ with 1024 byte blocks. */
+ st->st_mode |= S_IFCHR;
+ st->st_blksize = 1024;
+#ifdef ARM_RDI_MONITOR
+ res = wrap (do_AngelSWI (AngelSWI_Reason_FLen,
+ &openfiles[slot].handle));
+#else
+ asm ("mov r0, %2; swi %a1; mov %0, r0"
+ : "=r" (res)
+ : "i" (SWI_Flen), "r" (openfiles[slot].handle)
+ : "r0");
+ wrap (res);
+#endif
+ if (res == -1)
+ return -1;
+ /* Return the file size. */
+ st->st_size = res;
+ st->st_dev = openfiles[slot].dev;
+ st->st_ino = openfiles[slot].inode; /* Faked */
+ return 0;
+}
+
int __attribute__((weak))
_fstat (int file, struct stat * st)
{
memset (st, 0, sizeof (* st));
- st->st_mode = S_IFCHR;
- st->st_blksize = 1024;
- return 0;
- file = file;
+ return _swistat (file, st);
}
int __attribute__((weak))
_stat (const char *fname, struct stat *st)
{
- int file;
+ 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 ((file = _open (fname, O_RDONLY)) < 0)
+ if ((fd = _open (fname, O_RDONLY)) < 0)
return -1;
-
- memset (st, 0, sizeof (* st));
- st->st_mode = S_IFREG | S_IREAD;
- st->st_blksize = 1024;
- _swiclose (file); /* Not interested in the error. */
- return 0;
+ res = _swistat (fd, st);
+ st->st_mode |= S_IFREG | S_IREAD;
+ _swiclose (fd); /* Not interested in the error. */
+ return res;
}
int __attribute__((weak))