CBFalconer <
[email protected]> writes:
Well, in terms of C (this is comp.lang.c, after all), I'm afraid the
above is nonsense. Attempting to increment a function pointer is a
constraint violation, requiring a diagnostic. Attempting to increment
a return address doesn't even make sense, since return addresses
aren't directly accessible in C.
A function pointer needn't even be a machine address. It could, for
example, simply be an index into a table. (I don't know of any
implementations that do this.)
Original Tandem NonStop, still emulated on its Compaq^WHP successor
machines. The ISA was originally defined with call instructions having
a 9-bit index into a table (at the beginning of the code segment) of
almost 512 entries each containing a 16-bit instruction address.
This provided a minor gain in code compactness, and at the same time a
security feature: the OS kernel was simply another code segment, and
kernel calls were a variant call instruction with an index into the
system PEP table instead of the user one, so you couldn't jump into
the middle of a system routine. Moreover, the entries in the table
were grouped into privilege levels (using the first two table slots,
leaving only 510 real entries, hence my 'almost' above) and the CPU
would only raise the privilege level on a system call to those
procedures (= slots = indexes) marked to allow it, like the entry
'gates' in Multics; this meant that the OS segment could also contain
internal routines usable only when already in privileged state
(normally in system code, but see below), and (shared) utility
routines like atoi usable from either user or system code but
privileged only when called from privileged/system code.
Further, the same feature was applied to 'user' (really process) code
segments, so that a process with special privilege (only) could
effectively localize exercise of that privilege to certain procedures,
automatically. This was used with a message-passing OS that today
would be called microkernel -- when a user program does an I/O call,
it directly calls (as above) a small kernel routine that looks up the
fd (or name in some cases) and then sends the request as a message to
the _process_ handling that device -- one* process for each disk
drive, one for magtape, one for each comms line/protocol of which
historically Tandem supported a wide variety although nowadays
TCP/IP/Ethernet is dominant. That driver process code would include
privileged routines (per the PEP table) to do (only) the hardware I/O.
(* Actually driver processes were at least pairs for fault tolerance
and sometimes more for load balancing; but the point here is that they
were always a different process than the user process, with their own
code segment and thus PEP and procedure indexes.)
When this architecture needed to expand code space beyond one (plus)
16-bit segment, they added a second level of indirection. Instead of
call-normal and call-kernel, the two instructions were call-same-seg,
with a 9-bit index into PEP as above; and call-cross-seg, with a 9-bit
index into another (XEP) table with each entry containing a segment id
(including process/kernel) plus a 9-bit PEP index within that segment.
The 'dynamic call' instruction, used for C function-pointer calls,
(now) takes in effect a XEP entry -- 6-bit (or maybe now 7?) to select
process/kernel segment and 9-bit index within that segment.
- formerly david.thompson1 || achar(64) || worldnet.att.net