[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [MiNT] MiNTLib 0.51



On Fri, May 14, 1999 at 09:38:18PM +0200, Julian Reschke wrote:
> Who the hell is generating code like this? This would also fail on machines
> where a similar parameter passing scheme is used, or where sizeof (long) !=
> sizeof (void *).

That "who the hell" was me. ;-)
And all systems I know of that use the MiNTLib have 32 bit pointers and 32
bit long ints.

To avoid confusion, I think I should explain the background a little: I
needed to get the zlibc running.  The zlibc is actually a shared object
and on systems with shared libraries it is intended that you configure
your linker that all dynamically linked executables will get linked
against that shared uncompress.o.  The object uncompress.o replaces most
I/O primitives and the low-level filesystem interface routines so that
whenever such a call fails the routines will retry to find a file of the
same name but compressed with gzip and ".gz" appended to the name:

	int open (char* name, int flags, mode_t mode)
	{
		int retval = real_open (name, flags, mode);
		if (retval < 0 && errno == ENOENT)
		{
			... Try to stat NAME".gz" instead...
			... Iff successful 
				retval = popen ("gzip -c name.gz", ...)
		}
	}

That's the basic concept of the zlibc (and I must admit that it is a
little suspect to me).

I wanted to build a static library with the same concept.  The problem is
that you must somehow allow the library to override the library calls but
still be able to access the original functionality.  The easiest way to do
this would be to

	#define real_open(name, flags, mode) (syscall (SYS_open, name,
flags, mode))

Another possibility would be to access internal library functions.

Both ways didn't work with the MiNTLib and now they do (at least with gcc,
not yet with PureC).  I have a simple text file "syscalls-list" which
contains of entries like the following:

open long int ...

which translates into something like: There is a system call called
"open", which takes one 32-bit arg, one int-arg (16 or 32 bit, depending
on -mshort) and a va_list.  An awk-script generates various other files
from that list, first <sys/syscall.h> which defines opcodes to use for
syscall(), and for each system call a C source file, e.g. open.c which
looks like:

...
int open (long arg1, int arg2, ...)
{
  ... va stuff ...
  retval = __open (arg1, arg2, variadic_args);
  ... va end stuff ...
}

These files will only get compiled if your linker doesn't support weak
symbols.  If weak symbols are supported, then "open" would simply be a
weak alias for "__open()".  In the latter case (which will hopefully soon
be the default) the whole mechanism works without any overhead.

Now for syscall.c.  The implementation for syscall() works like follows:

struct syscall_entry 
{
  int (*real_func) ();
  int (*wrapper_func) (int opcode, ...);
};

struct syscall_entry entries[] =
{
  ...
  { __open, wrapper_liv },
  ...
};

The jumptable (array entries) is also generated by that awk script.  In
the open example it means that syscall() should call the function
WRAPPER_LIV (which returns an int and takes a 32 bit, an int arg and a
dots arg) and return the return value from that function.

The result: To open a file you can now write

	open ("/etc/passwd", O_RDONLY); or
	__open ("/etc/passwd", O_RDONLY); or
	syscall (SYS_open, "/etc/passwd", O_RDONLY);

Again, normally using one of the first two methods should be equivalent.
The syscall-method is highly deprecated but it is there if you happen to
need it.  The main disadvantage of syscall() is that you will drag all
modules that define the underlying functions into the link.

Uhm, after doing all that stuff I finally realized that I really needed
the "libz" (which doesn't require such ugly hacks) and not the "zlibc".
But having a working syscall() is not so bad after all and so I would
really like to leave that inside.  For example Perl is happy with that
(and for Perl it is no problem that most of the library gets linked
because it needs that anyway).

Just if somebody wonders why all this stuff is necessary just to implement
a single function:  On normal eunuchs systems (correct me if I'm wrong,
that's how I think it works) the syscall function works much like our
macros in <osbind.h> resp. <mintbind.h>, i. e. the function simply packs
the arguments onto the stack and executes some trap instruction to call
the respective kernel routine.  In other words, the function "open" is
really an operating system call and not a library routine (and that's why
you find the manpage for open in section 2, not section 3 of the manuals).

MiNT is different.  The GEMDOS-Fopen() function is not compatible to other
systems and the MiNTLib open() function tries its best to emulate the
behavior of eunuchs-open both on MiNT and non-MiNT systems.

If somebody can think of a smarter way to implement syscall() I'll be 
happy to hear about that. Until then: Don't use it but if you need it, you
have it.

Ciao

Guido

P.S.: To return to the question, the code is ugly because I wanted to
simplify things as much as possible.  With gcc it works because gcc
internally doesn't care if an argument or return value is really a pointer
or a long (or unsigned long).

And (P.P.S.), yes, I know that the above described mechanism
doesn't work flawlessly with variadic functions.  But I've taken care of
that and at least with gcc it works.
-- 
http://stud.uni-sb.de/~gufl0000
mailto:gufl0000@stud.uni-sb.de