Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > alt.os.development > #8248

Re: How to bind HLLs to OS calls

From "Rod Pemberton" <boo@fasdfrewar.cdm>
Newsgroups alt.os.development
Subject Re: How to bind HLLs to OS calls
Date 2015-06-24 22:15 -0400
Organization Aioe.org NNTP Server
Message-ID <op.x0rkjptdyfako5@localhost> (permalink)
References (2 earlier) <mluuql$4cc$1@dont-email.me> <op.x0g2zlo8yfako5@localhost> <mm9qnb$cnb$1@dont-email.me> <op.x0phzqyjyfako5@localhost> <mmeu1m$gi8$1@dont-email.me>

Show all headers | View raw


On Wed, 24 Jun 2015 14:49:42 -0400, James Harris  
<james.harris.1@gmail.com> wrote:
> "Rod Pemberton" <boo@fasdfrewar.cdm> wrote in message  
> news:op.x0phzqyjyfako5@localhost...
>> On Mon, 22 Jun 2015 16:22:19 -0400, James Harris
>> <james.harris.1@gmail.com> wrote:
>>> "Rod Pemberton" <boo@fasdfrewar.cdm> wrote in message  
>>> news:op.x0g2zlo8yfako5@localhost...
>>>> On Thu, 18 Jun 2015 13:24:52 -0400, James Harris  
>>>> <james.harris.1@gmail.com> wrote:
>>>>> "Rod Pemberton" <boo@fasdfrewar.cdm> wrote in message  
>>>>> news:op.x0ewr50gyfako5@localhost...
>>>>>> On Wed, 17 Jun 2015 11:05:30 -0400, James Harris  
>>>>>> <james.harris.1@gmail.com> wrote:

>>> I will put some explanatory comments below the code but basically  
>>> there  is to be a struct (declared in the header) where each member is  
>>> a  pointer to an OS function, and that structure ends up being called  
>>> "os"  in the body of the code. See the part "Issue some OS calls" for  
>>> how I am  thinking apps will call the OS.
>>
>> The idea seems clean to me.
>
> That's good to hear.
>
>> It's easier than a library.
>
> I have been trying to work out what you mean there but I am not sure.

A struct of functions is easier to do than compiling a library, yes?

>> You can
>> always add more to the end of the struct and keep #defines for the
>> number of OS calls available or the struct version.  I.e., to allow
>> for backward compatibility with future changes.
>
> I was thinking that the name of the struct would fully identify the set  
> of calls. I am not sure whether that is enough but nor am I sure that  
> adding a separate #define struct version would help.
>
> To illustrate, the real struct name would probably be determined by the  
> name of the OS (which I have no idea about yet). Say the os were to be  
> called JHOS. In that case the structure could be called something like
>
>   struct jhos_rv1_45
>
> where rv1 means release version 1, and 45 is the number of calls therein  
> or the highest call number or something like that where larger numbers  
> imply later revisions and the number can only increase with each  
> addition to rv1.

Can this info be obtained when/if needed without parsing "jhos_rv1_45"?

> Thus the struct type name would fully identify the version of the call  
> table, and jhos_rv1_45 would be exactly the same as the first elements  
> of jhos_rv1_49, which would be exactly the same as the first elements of  
> jhos_rv1_53, etc. That would mean that the _53 call table could be used  
> even for the _45 interface because an app compiled to use the _45  
> interface would need just the lower 45 or so entries from the _53 table.

Ok, so, no duplication, but overlay.

How do you stop a _53 app from calling a function a _45 OS doesn't have?

Any plans to prevent a _45 app from calling a function for a _53 OS?
I.e., hacking.

>>> The key intention here is that this is implemented as an array of  
>>> pointers (where each pointer has the same size), but from the C  
>>> perspective each pointer has a particuar type, and the compiler will  
>>> know what that type is. It can therefore use the type to ensure that  
>>> calls to each routine have the correct parameters.
>>>
>>> Being a struct, too, each member has its own name. Despite being
>>> [implemented] as an array, C programs can therefore call each OS
>>> function by name and the compiler will match the types.
>>
>> Well, an array would be indexable by an integer.  If you used a
>> union, you could possibly use both names and an index to each call.
>>
>> Alternately, if you used an array of function pointers, you
>> could do something like the following.  Then, you could access
>> the function pointers by index or by name.
>>
>> // tested code.
>> // Well, GCC 4.7.2 on Linux likes it ...
>>
>> #include <stdio.h>
>>
>> #define OS(n) os[ ## n ## ]
>> typedef void (*osfn)(void);
>>
>> // would be extern
>> void hello()
>> {
>>   printf("Hello ");
>> }
>>
>> // would be extern
>> void world()
>> {
>>   printf("World!\n");
>> }
>>
>>
>> // array of pointers to functions
>> // would be extern in your OS header
>> osfn os_table[]=
>> {
>>   hello,
>>   world,
>>   NULL
>> };
>>
>> #define os_qret() (*(os_table[0]))()
>> #define os_kret() (*(os_table[1]))()
>>
>> int main(void)
>> {
>>
>>   (*(os_table[0]))();
>>   (*(os_table[1]))();
>>   os_qret();
>>   os_kret();
>>
>> return(0);

>> [code snip]
>
> I see your point (and it's good to see use of ## which I never really  
> got to understand) but the trouble with an array is that a C compiler  
> will believe that every element has the same signature. With a struct,  
> on the other hand, each element has its own signature. That allows the  
> compiler to check that it is being called correctly.

That's the only time I've ever used ## .  I wasn't sure it was legal
for two to be used on the same line, but GCC's preprocessor didn't choke
on it.

## is the merge operator for macro's
# is the string operator for macro's, i.e., places quotes around value

>> Unfortunately, you can't use a '.' dot, in the #defines
>> for "os.qret" etc., AFAIK, but you could ensure a more
>> exclusive "namespace" with underscores, e.g., os_qret__();
>
> As a matter of personal taste do you prefer the form with underscores  
> over that with the "os." prefix?

No.

> I should say that in your version you have apparently chosen to fix the  
> "os_" part into the definition. In mine, "os." is something that the  
> programmer has chosen.

You could put in another #define.

> Also, with my approach, it would be possible to use a pointer to the  
> struct which might be handy of the version of the os calls table were to  
> be passed to a subroutine or if the programmer simply preferred to refer  
> to it by dereferencing a pointer. If the pointer was declared as
>
>   struct oscalls_v0dev *osp;
>
> Then, once the pointer had been initialised, OS calls could be made  
> using the pointer with such as
>
>   osp->kret();
>
> In this case, too, the programmer has chosen the "osp->" prefix rather  
> than the OS developer fixing it, putting the name in the hands of the  
> programmer and allowing the OS calls table version to change without the  
> programmer having to go through and update any call statements in his  
> source.

Sure.

>>>>> 2. Some OS calls may be satisfiable without taking the trip to  
>>>>> privleged  mode. In that case it makes sense to have the subroutine  
>>>>> which is  immediately called by the app just do the work necessary  
>>>>> and  return  without invoking the kernel.
>>>>
>>>> If you don't have to invoke the kernel, do these constitute an OS  
>>>> function?
>>>
>>> I would say so, yes. IMO any service routine that the OS provides is  
>>> an  OS function. How it provides it is not so important.
>>
>> If these are an OS function, should you provide them to the user?
>>
>> You just answered that it's acceptable to do so, but I'm thinking
>> about portability and safety, which seem to be of importance to you.
>
> [snip]
>
>> For [portability], using the functions would likely make the users
>> code non-portable since they are host specific functions.  Whereas,
>> if the user only had access to kernel functions, they would have to
>> code their own equivalent to the OS function in portable code.
>
> I am not sure I understand this.

If the kernel has functions which the app can call directly,
that set of functions or API is the core interface for the OS, yes?
(Well, I think so.)  So, is there any point in an additional layer
of functions between the the app and the kernel?  I.e., either these
functions should not be provided, or possibly, they should be moved
into the kernel.  Historical OSes, like Unix and Windows, have a set
of basic minimal functions, mostly file I/O, that all code can build
from.  So, let's say the kernel file open is "open" and the OS file
open is "xopen".  Is there a point to writing apps for "xopen", when
only OSes compliant with your design specifications can use that code?
I.e., Linux and Windows won't have "xopen", but will have Unix "open"
and C "fopen".  So, code written for your platform specifications
using "xopen" becomes non-portable.  In which case, isn't it better
to eliminate use of "xopen"? ...

> I was thinking that the OS would present to the apps a set of functions,  
> and that the same set would be made available regardless of platform.

Ok.  I think that answers the question.

IIUC, there is a requirement somewhere by you as to the set of
functions which is required to be implemented that apps can call.
These functions are used by the app, and the app won't directly
call the functions exported by the kernel.

So, app code for your platform will only be portable to platforms
compliant with your set of functions.

>>> Some functions may be able to do what they have to do without  
>>> privilege  such as functions to read a certain time clock.
>>
>> I can't immediately "see" how that would allow for software hacking,
>> but I immediately realize that could be used for hardware hacking.
>>
>> This issue may be way outside the scope of your needs at this point.
>>
>> E.g., an electrical engineer or someone familiar with both electronics
>> and programming could write code to read and write the hardware clock.
>> However, they've also installed hardware, logic circuitry, that
>> redirects the I/O to port address to the addresses they desire to hack.
>
> I am not sure an OS can be proof against custom hardware but in normal  
> cases only the kernel would allow port IO. Nothing in user mode would be  
> able to do port IO directly.
>

Even without custom hardware, it allows for information leakage via
"port knocking."  E.g., in the future, your OS is now used for a high
security platform.  A hacker is having problems getting restricted
information out of the computer.  The hacker programs the reading
of the ports to do Morse code or other patterns that can be recorded,
e.g., a resistor and high-speed led on bit 0 of the port address when
flashing can be recorded as cell phone video.

>>> I'll have to get back to you separately about the rest of the things  
>>> we  were discussing but I said I would comment on the performance of  
>>> calls  to the OS and branch predictability. Code speed is always  
>>> exciting,  right! I understand that x86_32 indirect near calls can be  
>>> branch  predicted. The apptest2 subroutine above compiles on x86_32  
>>> with -O2 to  the following (gcc AT&T format with destinations on the  
>>> right).
>>>
>>>   push   %ebp
>>>   mov    %esp,%ebp
>>>   sub    $0x8,%esp
>>>   call   *0x0
>>>   call   *0x4
>>>   xor    %eax,%eax
>>>   leave
>>>   ret
>>
>> LEAVE? ...
>>
>> Was there an ENTER somewhere?  Or, is there some missing prologue (UK)  
>> or prolog (US) code which would require LEAVE?

I must've been losing it at this point.  The PUSH and MOV are the ENTER.

>> Also, IIRC, LEAVE is "slow."  Avoid.
>
> I don't know about that.

Were you referring to "slowness" comment or the missing prolog comment?

> The use of LEAVE was a GCC choice, not mine. Is it possible that ENTER  
> can be slow but not LEAVE? Maybe LEAVE is faster than the equivalent. I  
> have seen it as compiler output before.

ENTER may be even slower.  ;-)  Yeah, I probably mixed them up.

> That said, I am puzzled by GCC's optimisation. Notice it has subtracted  
> 8 from ESP. At the moment I cannot see why.

Is this standard 32-bit x86 code?  That would seem to indicate that
it has pushed two 32-bit values.

My guesses would be:

1) the two "void func(void)" functions are popping one 32-bit value each
2) the posted code was changed, e.g., was "void func(int)" originally
3) the code places a barrier/fence to protect the return address
4) the stack direction is reversed and sizes are 64-bit ... (?)
5) something wrong with the code generator, e.g., off-by-one, or optimizer

If #1, you'll need to look into why they have a hidden parameter.

If #4, IDK WTF that's about, but then the "sub 8" compensates for
the **64-bit** push of 32-bit ebp perhaps zero-/sign-extended to 64-bits  
...


Rod Pemberton

-- 
It's time to put an end to gun violence!
Use a hammer ...

Back to alt.os.development | Previous | NextPrevious in thread | Next in thread | Find similar | Unroll thread


Thread

Re: How to bind HLLs to OS calls "Rod Pemberton" <boo@fasdfrewar.cdm> - 2015-06-18 02:10 -0400
  Re: How to bind HLLs to OS calls "Rod Pemberton" <boo@fasdfrewar.cdm> - 2015-06-19 06:19 -0400
    Re: How to bind HLLs to OS calls "James Harris" <james.harris.1@gmail.com> - 2015-06-22 21:22 +0100
      Re: How to bind HLLs to OS calls "Rod Pemberton" <boo@fasdfrewar.cdm> - 2015-06-23 19:24 -0400
        Re: How to bind HLLs to OS calls "Rod Pemberton" <boo@fasdfrewar.cdm> - 2015-06-24 22:15 -0400
    Re: How to bind HLLs to OS calls "James Harris" <james.harris.1@gmail.com> - 2015-06-24 07:55 +0100
      Re: How to bind HLLs to OS calls "Rod Pemberton" <boo@fasdfrewar.cdm> - 2015-06-24 21:00 -0400

csiph-web