inst v1.0.0
#1
inst v1.0.0
Dear community

In this post I'd like to give a more in-depth view on the new inst implementation. The goal is to provide a reference for those who wish to extend the source code with new features.

TL;DR: Download inst_bundle.tar, install the tardists and run /usr/local/inst/bin/inst

1. inst
=======


A quick summary on inst(1M) for those unaware: inst(1M) is the software installation tool used on SGI IRIX. It provides a command line interface, though a GUI is also available in form of swmgr(1M).

So how does inst integrate into an SGI IRIX system? Looking at its dependencies, it becomes clear that there's a lot happening inside of inst:

[Image: ldd.png]

Further analysis shows that it is written in C++ with some free functions that are familiar from other well known C libraries (free function is a term from C++ and refers to a non-member function):

Code:
...


[97]    |  82676544|      92|FUNC |GLOB |DEFAULT  |MIPS_TEXT|getDist__12iFileRepListCGv
[98]    |  82694568|    476|FUNC |GLOB |DEFAULT  |MIPS_TEXT|histDelta__5iHistSGPC25iForest__pt__10_8InstableT1PC12iInstProduct
[99]    |  82698300|    3336|FUNC |GLOB |DEFAULT  |MIPS_TEXT|upgradeHistory__5iHistGRC7iString
[100]  |  82701636|    4096|FUNC |GLOB |DEFAULT  |MIPS_TEXT|downgradeHistory__5iHistGRC7iString
[101]  |  82018480|    488|FUNC |GLOB |DEFAULT  |MIPS_TEXT|__ls__GR7ostreamRC15iFilePrereqIter
[102]  |  82026376|      52|FUNC |GLOB |DEFAULT  |MIPS_TEXT|clearAllErrors__9iInstArgsGv
[103]  |  82026568|      52|FUNC |GLOB |DEFAULT  |MIPS_TEXT|clearMountTable__9iInstArgsGv
[104]  |  82033776|      56|FUNC |GLOB |DEFAULT  |MIPS_TEXT|imageThatImplies__9iInstArgsCGi
[105]  |  82744728|    824|FUNC |GLOB |DEFAULT  |MIPS_TEXT|computeFileSystemIndex__9iInstFileGv
[106]  |  82746408|      92|FUNC |GLOB |DEFAULT  |MIPS_TEXT|getMachTag__9iInstFileCGv
[107]  |  82767940|    304|FUNC |GLOB |DEFAULT  |MIPS_TEXT|getChildCreatedate__12iInstProductCGv
[108]  |  82769560|      16|FUNC |GLOB |DEFAULT  |MIPS_TEXT|setRawFlag__12iInstProductGi
[109]  |  82769652|      20|FUNC |GLOB |DEFAULT  |MIPS_TEXT|clearFlag__12iInstProductGi
[110]  |  82770836|      20|FUNC |GLOB |DEFAULT  |MIPS_TEXT|isDefault__12iInstProductCGv
[111]  |  82770876|      64|FUNC |GLOB |DEFAULT  |MIPS_TEXT|isFeatureStream__12iInstProductCGv
[112]  |  82770940|      64|FUNC |GLOB |DEFAULT  |MIPS_TEXT|isMaintStream__12iInstProductCGv
[113]  |  82782840|    1164|FUNC |GLOB |DEFAULT  |MIPS_TEXT|maintToBase__12iInstProductGRC6iAPathR6iAPath
[114]  |  82060184|      64|FUNC |GLOB |DEFAULT  |MIPS_TEXT|machNodeType
[115]  |  82811412|      64|FUNC |GLOB |DEFAULT  |MIPS_TEXT|is32bit__5iMachCGv
[116]  |  82811792|      60|FUNC |GLOB |DEFAULT  |MIPS_TEXT|unknownTags__5iMachCGRC13iMachSelector
[117]  |  82062940|    1028|FUNC |GLOB |DEFAULT  |MIPS_TEXT|getMachTables__GRP9Subgr_MapRP8Mach_MapRP8Arch_MapRiN24
[118]  |  82841520|      52|FUNC |GLOB |DEFAULT  |MIPS_TEXT|getLastDelta__11iProdActionGv
[119]  |  82842132|    1064|FUNC |GLOB |DEFAULT  |MIPS_TEXT|log__11iProdActionCGv
[120]  |  82844536|      8|FUNC |GLOB |DEFAULT  |MIPS_TEXT|setupTasks__11iProdActionGv
[121]  |  82845488|    264|FUNC |GLOB |DEFAULT  |MIPS_TEXT|summary__11iProdActionCGv
[122]  |  82888900|    1804|FUNC |GLOB |DEFAULT  |MIPS_TEXT|downgradeHistory__12iProdDescMgrGRC7iStringiT1T2PPc
[123]  |  82078032|      56|FUNC |GLOB |DEFAULT  |MIPS_TEXT|prodmap


...

Estimations of source lines of code are one of the best indicators to measure complexity of an application.

The following formula was applied:

SLOCs = DIS_LINES_OF_ALL_C++_METHODS * 1,25

DIS_LINES_OF_ALL_C++_METHODS: Number of disassembled lines of binary output using dis(1).
1,25: A correcting factor that compensates for divergence from the real SLOCs. This factor includes comments and is based on experimental results from my own source code.

When applied to inst(1M):

SLOCs = DIS_LINES_OF_ALL_C++_METHODS=465.105 * 1,25 = 581.381,25

Good.


2. Hook system
==============


With this amount of lines of code, a pure reimplementation is not feasable. A different approach is necessary.

There is one saying in business that goes like this: EEE = Embrace, Extend, Extinguish.

This is applicable in different situations, for example when you are trying to tackle on a problem that is way beyond your possibilities. You can try to adapt to it (Embrace), modify it a bit at a time (Extend) and finally, by repeating the previous step, completely change it (Extinguish).

That's the approach I need here.

My EEE is called "Hook system". A hook system is a concept used in software architecture to describe a system that consists logically of three parts:

1. A system (allowing hooks)
2. The hook that connects into the system
3. Something that is bound to the hook

On different OSs this concept is implemented with a different semantic, but logically they are all composed of these three parts (at least). As a reference for such systems, consult:

SGI IRIX - Any page about routines for registering interrupt, bus handling, error or driver
https://www.irix7.com/techpubs/007-0911-210.pdf

Windows et. al. - Any of the points listed as example uses
https://learn.microsoft.com/en-us/window...bout-hooks

Back to inst, a hook system would be needed to hook (2.) a user defined function (3.) to the libinst.so's function (1.). The implemented semantic would result in a callback of the user defined function whenever libinst.so's function would be called.

That way, if an extension needs to access only a certain number of functions in libinst.so, it would just need to hook its own user defined function(s) into these libinst.so functions.

In this scenario, there are minimum two possible techniques of implementing a hook system.


2.1. Patching
-------------


Patching means here to patch-in jump instructions into the system libinst.so's functions at the beginning and end (into their prologue and epilogue, for example). These would jump to functions outside of libinst.so to call the hooked functions:

[Image: Patch.png]

The advantages of this approach are stability and flexibility.

The disadvantages are the effort needed (some functions would require unique modifications) and that it would require a whole extra copy of libinst.so, because you would need an address space where the patched-in jump instructions can jump to the same address every time you load the library. There are some workarounds to this too, but judging only by these disadvantages it is a second best approach, if at all.


2.2. Static prelinking
----------------------


Static prelinking is a technique that allows to shadow the real libinst.so functions with user defined ones by linking the final executable in a way that the system runtime loader, rld, loads the user defined ones before the real ones, thereby shadowing them. Neat.

[Image: Prebinding.png]

In the upper example, a normal invocation of inst(1M) is shown. It would call into libinst.so simply using the needed function name.

In the lower example, an invocation of inst v1.0.0 using static prelinking is shown: inst v1.0.0 would call first into libinstw.so (the trailing 'w' stands for wrapper) which contains, ideally, all the function names in system libinst.so. Libinstw.so would be linked first to inst v1.0.0 such that the runtime linker would first load this library (and use the contained symbols for further symbol resolution, effectively shadowing libinst.so if it was subsequently loaded).

In a second step, the function in libinstw.so, also called the wrapper function, would then delegate to the real function in the system libinst.so. If you ask yourself how it's possible to call a shadowed function in this situation, then have a look at man dlopen(3C).


There is a similar technique in almost any UNIX from the 90's onwards that allows you to do just this, but using an environment variable directed at the runtime linker, rld. See rld(5) for more details.

The difference is the time of symbol binding. In static prelinking, symbols are bound at static link time (using ld). The second technique binds symbols at runtime (using rld).

NOTE: In the context I use these terms here, the difference between static prelinking and just plain old static linking is its intended use: static prelinking links statically against a third library to shadow another one at runtime.


2.3. Improving wrapper functions
--------------------------------


Using static prelinking, it is possible to implement a simple hook system, but this can be improved upon a bit.

From my personal experience using various hook systems, there is one shortcoming that immediately comes to mind and that would greatly ease some tasks (to be fair, I'm talking about relatively rare ones). It is the lack of an "epilogue function": Most hook systems just allow to set or register a hook that binds a prologue function for a specific task. But they don't allow to bind functions for the exact point in time when the task has just finished. And sometimes, this is desirable.

Since this feature can be very helpful and doesn't differ too much from the prologue function, the hook system for inst v1.0.0 supports both of them.

In addition, it is possible to suppress the call to the real function or register your own to be called instead.

This is the kind of flexibility that makes a developer's heart go BUM BUM.


To implement this double call before and after the system function, the wrapper function in libinstw.so would just need to call first the prologue function, then the system libinst.so function, and finally the epilogue function. In the source code, prologue functions are called pre functions, and epilogue functions are called post functions. Both are stored in the file prepost.c (conveniently named):

[Image: Prepost.png]

All wrapper functions in libinstw.so make a copy of all the argument registers on entry (this is done in assembly) and then call the internal wrapper() helper function that handles the invokations of pre, system libinst.so and post functions, along with argument passing, which is detailed in point 2.6 Function arguments (see below).

Step 3 is greyed to emphasize that this step is not specific to one wrapper function (A::get() in this illustration) but rather applies to all wrapper functions: i.e., if there were classes named A, B and C in the system libinst.so, all their member functions would get a corresponding wrapper function in libinstw.so and thus all of them would call into wrapper().


2.4. Hooks table
----------------


At the core of the hook system is the hooks table. It stores the three function pointers (pre, system libinst.so and post functions) along with other information. You may consult the source code for those details.


2.5. Hooking up (also: registration)
------------------------------------


For a wrapper function to call any of the three function pointers, it must be hooked up first into the hooks table.

The function that accomplishes this is invoked in reg.c and is called register_wrapper_function():

Code:
if (register_wrapper_function(0,
                              (void *) iResource_destroy_void_4739_pre,
                              (void *) LIBINSTW_DEFAULT_INTERNAL,
                              (void *) iResource_destroy_void_4739_post) < 1)
{
                return 0;      // ERROR: Couldn't register hook functions for iResource::destroy(void)
}

The first parameter is an index into the hooks table. Every function that can be hooked up already has an entry in this table because the hooks_table.cxx and reg.c files are generated together in add_new_wrappers.pl.

This is discussed later.


2.6. Function arguments
-----------------------


There is one last hurdle to overcome: function arguments.

First, why is this a problem? Because...

* There are many functions (in total 4975, including free functions)
* They have potentially different number of arguments

Ideally, a single centralized function would dispatch arguments to the corresponding pre, system libinst.so or post functions. Let's see if this is somehow possible.

There are two relevant layers where argument passing takes place:

1. C++ or language layer
2. ABI or binary layer

On the C++ or language layer, each function has to be handled separately or, at least, all those functions with the same arguments. This was exactly one of the reasons listed above of why this should be solved differently: it is time consuming and bloats the source code with function argument handling code, so it isn't efficient either.

On the ABI level, however, there is an interesting convention that is part of the ABI:

Calling convention. More specifically: function calling convention, which includes the required mechanisms to pass arguments from the caller to the callee and back again.

On SGI IRIX/MIPS, function arguments (the first 8 arguments) are passed using 8 registers, numbered r4-r11 (symbolically named a0-a7).

Return values are passed back using two registers, r2-r3 (symbolically named v0-v1).

And what happens with functions that need more than 8 parameters I hear you thinking?

They are passed on the stack.

And if a function returns data that's larger than two 64 bit registers (v0, v1) ?

Then only one pointer (passed in a0, and returned in v0) is passed to the function and back again. This pointer contains the address of the destination memory block.

Since there are no functions with more than 8 parameters in libinst.so, that's one down. Passing data back is, again, handled by one single register, v0. Another one that bites the dust.

This leaves an elegant solution very close at hand, if it wasn't for a little detail.

To touch code at assembly level, it must be coded at assembly level. This holds true for SGI MIPSpro C/C++ where no asm() construct is available to my knowledge.


So, let's dig a bit deeper.


The following illustration shows the normal SGI MIPSpro C/C++ (version 7.4.4) compiler driver phases (left):

[Image: CC_Phases.png]

It is beyond the scope of this write-up to explain each phase in detail, but the avid reader may consult the splendid documentation on irix7:

https://www.irix7.com/techpubs.html

The right side shows where exactly the driver must be intercepted to modify assembly code, before it is processed any further.

What must be done is to change the file with all wrapper functions (wrappers.cxx) in its assembly phase, wrappers.s, and then just continue the compile process with the modified file.

This modification on assembly level and subsequent linking is automated with two scripts called asm.pl and LCC.ksh, respectively. The relevant part in the Makefile is:

Code:
wrappers.s: wrappers.cxx
# wrappers.s must be run through asm.pl to generate
# modified wrappers.s usable for linking into libinstw.so
CC -S -v -g3 -O0 -shared wrappers.cxx
./asm.pl wrappers.s

...

libinstw.so: libinstw.cxx wrappers.s asm.s
mv wrappers.s wrappers.s.bak
gmake wrappers.s
CC -g3 -O0 -shared -c libinstw.cxx -o libinstw.o # libinstw.cxx -> libinstw.s
./LCC.ksh # libinstw.s -> libinstw.o -> ... -> libinstw.so

So, to get libinstw.so, wrappers.cxx is first compiled up to the asm phase, then modified using asm.pl, and then linked into libinstw.so in LCC.ksh. That's it!


3. Putting it all together
==========================


The logical components of the source code for inst v1.0.0 are presented next:

[Image: inst_source_3.png]

The distinction between C and C++ comes from one of the requirements of the bounty. The code should compile and be compliant with C89, so the code base has been split into the part handling the C++ side of libinst.so, and the C part, which is what meets the requirement.

All pre and post functions are on the C side of life, this is where you would put your own code into.

The arrows indicate three files that need to be mirrored from C++ to C and slightly modified. Their modifications are trivial but saved some time.


3.1 add_new_wrappers.pl
-----------------------


Most of the time for this project was used to actually parse the function signatures from libinst.so. This parsing was essential to creating wrappers.hxx, the most important file of the project. It contains the class type definitions of all class types used in the 4975 functions, plus it places them according to their interdependencies. Some minor tweaks had to be done manually to round this up.

With the class type definitions in place, any libinst.so function can be intercepted or wrapped in wrappers.cxx just by including appropriate definitions in wrappers.cxx for the wrapper function itself, hooks_table.cxx for the corresponding hook entry, prepost.[ch] for the pre and post functions and finally reg.c where the registration function is called to hook the pre and post functions into libinstw.so.

The following image shows the big picture:

[Image: add_new_wrappers.png]

The two pairs of files to the left and right of the add_new_wrappers.pl script are temporary files used to generate the final hooks_table.cxx and wrappers.cxx files, respectively.

To use this script, it needs the signatures of the functions in libinst.so that should be wrapped and included in the above files. These signatures are found in the decompiler/ subdirectory in the files named funcs.all (all C/C++ function signatures), funcs.final (all C++ function signatures), and funcs.final.lite (the reduced version that was actually used).

The last file resulted from the fact that some of the functions were not necessary from a functional point of view but were expensive in terms of performance. Examples include all constructors, destructors, operators plus some functions that made the final inst v1.0.0 too slow.

Using the above script and modifying the funcs.* file, any function can be wrapped.

Note that there were some functions that did not follow the expected ABI calling convention (manual or automated optimizations?) and would therefore require some more work.

Nonetheless, these few functions were pretty useless and interceptable in prior function invocations or they could also be replaced entirely in reg.c with a user defined one.


4. How to build
===============


If a different number of signatures is required, first invoke add_new_wrappers.pl:

Code:
bash# ./add_new_wrappers.pl decompiler/funcs.modified > out

The out file contains the same generated output but is more easily debuggable.

Once the output files are generated, just:

Code:
bash# make

The executable and the library can be generated separately using targets inst and libinstw.so.


5. New flags
============


5.1 Tracelogging
----------------


Using wrapper, pre and post functions means that execution can be tightly "controlled" at any point in time. I found it useful to implement a new feature to facilitate debugging extensions or general execution of inst.

The flag is called the tracelog flag, enabled by -t or -T. The syntax is:

Code:
bash# inst -t/-T /path/to/tracelog/file

When invoked with tracelogging enabled, inst logs all invocations of wrapper functions just before calling the pre function. It also synchronizes writes with the tracelog file such that it does not return unless the name of the function was written to the file.

This slows down the execution of inst, but helps to pinpoint where exactly a function is not being invoked and therefore quickly find functions that are incompatible with the hooks system. There were very few of them, but they would not have been found without this tracelogging feature.

With -t, only function names are logged. With -T, function names and their argument registers are logged.


5.2 bsdtar support
------------------


To support bsdtar, a new flag has been added, -b:

Code:
bash# inst -b -f /path/to/source_0 [-f /path/to/source_N ...]

When used, any subsequent -f flag to specify a source distribution that contains the pathname of a file, is passed first through bsdtar with the -x flag. This allows to use any format supported by bsdtar with inst v1.0.0 from the command line.

The executable is found using the name bsdtar as typed in a shell, thus using PATH. If any other bsdtar executable should be used, the INST_BSDTAR environment variable can be set to point to this executable before invoking inst.

Both flags are documented in the manpage for inst v1.0.0.


6. Extensions
=============


There are three approaches to extension writing for inst v1.0.0:

1. Stage 1
2. Stage 2
3. Hybrid

As depicted in the very first image, inst is invoked from an inst.c file that calls into the libinstw.cxx file to finally invoke the system libinst.so function.

Stage 1 would be inst.c. It allows to add any code that wishes to manipulate command line
arguments before passing them over to libinstw.cxx/libinst.so.

Stage 2 refers to the pre, libinst.so, and post functions. Any combination of them can be used to implement the extension. Arguments to the system libinst.so function are passed to the pre and post functions too, so this gives you full access to everything a function has to offer.

The third one is a combination of both stages.

For an example of Stage 1 extension, look at the bsdtar extension in inst.c.


These approaches allow you to, basically, implement anything.


EDIT_0: Corrected indentation.
EDIT_1: Added missing image in 2.3 Improving wrapper functions (thanks to Robespierre!). For links to git repositories, see posts below (thanks to Raion!).
(This post was last modified: 11-24-2022, 09:28 AM by TruHobbyist.)
TruHobbyist
Developer

Trade Count: (0)
Posts: 195
Threads: 21
Joined: May 2018
Find Reply
11-23-2022, 09:04 PM
#2
RE: inst v1.0.0
Thanks for your hard work. A lot of the low-level hacks that you have had to do to get this working are gonna take a while to sink in; but I am glad to see we have an inst that we can eventually extend further. libinst.so may be a future RE target but it's a massive lib from what our conversations came up with. I appreciate how insanely detailed and thorough this all is.

I'm the system admin of this site. Private security technician, licensed locksmith, hack of a c developer and vintage computer enthusiast. 

https://contrib.irixnet.org/raion/ -- contributions and pieces that I'm working on currently. 

https://codeberg.org/SolusRaion -- Code repos I control

Technical problems should be sent my way.
Raion
Chief IRIX Officer

Trade Count: (9)
Posts: 4,249
Threads: 535
Joined: Nov 2017
Location: Eastern Virginia
Website Find Reply
11-23-2022, 09:50 PM
#3
RE: inst v1.0.0
Links to the code:

https://codeberg.org/IRIXNet-Development/openinst

Perl Module:

https://gitea.irixce.org/TruHobbyist/Inst.pm-1.0.0

This will be incorporated into IRIXCE eventually. I thank Tru for doing this because it enables us to remove the old /sbin/tar and add bsdtar as the default system tar. I will be looking into/testing the inst code extensively before we jump the gun, but I'm excited.

I'm the system admin of this site. Private security technician, licensed locksmith, hack of a c developer and vintage computer enthusiast. 

https://contrib.irixnet.org/raion/ -- contributions and pieces that I'm working on currently. 

https://codeberg.org/SolusRaion -- Code repos I control

Technical problems should be sent my way.
(This post was last modified: 02-23-2025, 03:34 AM by Raion.)
Raion
Chief IRIX Officer

Trade Count: (9)
Posts: 4,249
Threads: 535
Joined: Nov 2017
Location: Eastern Virginia
Website Find Reply
11-23-2022, 10:24 PM
#4
RE: inst v1.0.0
(11-23-2022, 09:04 PM)TruHobbyist Wrote:  Note that there were some functions that did not follow the expected ABI calling convention (manual or automated optimizations?) and would therefore require some more work.

The O32 ABI is more strict. Every argument to a function must have space on the stack in addition to being passed in registers. The stack location for saving every register is strictly defined.
The N32 and 64 ABIs are much more relaxed. Stack space is not required for arguments in registers. If registers must be saved, the save location is up to the function.
These ABIs also allow more flexibility in how functions interact with the link editor. Any instruction can be marked with a relocation tag to be filled in later when objects are linked together.
When IPA (interprocedural analysis) is used, the compiler can reason about possible call graphs between functions. A function "B" that is only called by one other function "A" (for example), may be able to share stack space or temporary registers with "A", or be inlined directly into it. This is in addition to the ABI rules for non-leafs (functions which call others), leafs which use stack space, and leafs which do not use stack space.

Quote:To touch code at assembly level, it must be coded at assembly level. This holds true for SGI MIPSpro C/C++ where no asm() construct is available to my knowledge.

MIPSpro does not support an 'asm' extension. The recommended way to write assembly is to first make an outline of the function in C with whatever argument and return types are needed, compile with cc -S, and then edit the emitted "out.s" file, to be assembled with as.

Quote:To implement this double call before and after the system function, the wrapper function in libinstw.so would just need to call first the prologue function, then the system libinst.so function, and finally the epilogue function. In the source code, prologue functions are called pre functions, and epilogue functions are called post functions. Both are stored in the file prepost.c (conveniently named).

One of the common uses for wrapping functions is to change what arguments they receive. In your framework, how is this accomplished? Does the dispatcher put the arguments on its stack where a prologue callee can modify them? What about epilogues that wish to modify the return values?

Personaliris O2 Indigo2 R10000/IMPACT Indigo2 R10000/IMPACT Indigo2 Indy   (past: 4D70GT)
robespierre
refector peritus

Trade Count: (0)
Posts: 640
Threads: 3
Joined: Nov 2020
Location: Massholium
Find Reply
11-24-2022, 05:05 AM
#5
RE: inst v1.0.0
Is the strictness of the o32 ABI a reason why there's two linkers for MIPSPro? One handles both 'new' ABIs, the other is dedicated to o32.

A weird aspect of MIPSPro from my relatively amateur experience is that you're limited to using the cc driver with o32 and thus c89. I suspect that MIPSPro as we know it is partially composed of IDO-era holdovers for o32 and was only rewritten/reworked for n32/n64 ABIs. I could very well be incorrect here however so I'm not stating this as fact but only a theory.

I'm the system admin of this site. Private security technician, licensed locksmith, hack of a c developer and vintage computer enthusiast. 

https://contrib.irixnet.org/raion/ -- contributions and pieces that I'm working on currently. 

https://codeberg.org/SolusRaion -- Code repos I control

Technical problems should be sent my way.
Raion
Chief IRIX Officer

Trade Count: (9)
Posts: 4,249
Threads: 535
Joined: Nov 2017
Location: Eastern Virginia
Website Find Reply
11-24-2022, 05:16 AM
#6
RE: inst v1.0.0
(11-24-2022, 05:16 AM)Raion Wrote:  Is the strictness of the o32 ABI a reason why there's two linkers for MIPSPro? One handles both 'new' ABIs, the other is dedicated to o32.
I don't know at that level of detail; this may be a question only the authors can answer.
The new ABIs were needed to support the added features of the R4000 and later processors, like 64 bit GPRs (even in 32 bit address space for N32), 32 double-precision FPRs instead of 16, and multiprocessing stuff.

Quote:A weird aspect of MIPSPro from my relatively amateur experience is that you're limited to using the cc driver with o32 and thus c89. I suspect that MIPSPro as we know it is partially composed of IDO-era holdovers for o32 and was only rewritten/reworked for n32/n64 ABIs. I could very well be incorrect here however so I'm not stating this as fact but only a theory.
Again, I don't know, but it might have required more work to support some c99 features like variable length arrays on the older ABI.

Personaliris O2 Indigo2 R10000/IMPACT Indigo2 R10000/IMPACT Indigo2 Indy   (past: 4D70GT)
(This post was last modified: 11-24-2022, 05:43 AM by robespierre.)
robespierre
refector peritus

Trade Count: (0)
Posts: 640
Threads: 3
Joined: Nov 2020
Location: Massholium
Find Reply
11-24-2022, 05:43 AM
#7
RE: inst v1.0.0
(11-24-2022, 05:05 AM)robespierre Wrote:  
Quote:To implement this double call before and after the system function, the wrapper function in libinstw.so would just need to call first the prologue function, then the system libinst.so function, and finally the epilogue function. In the source code, prologue functions are called pre functions, and epilogue functions are called post functions. Both are stored in the file prepost.c (conveniently named).

One of the common uses for wrapping functions is to change what arguments they receive. In your framework, how is this accomplished? Does the dispatcher put the arguments on its stack where a prologue callee can modify them? What about epilogues that wish to modify the return values?
With a little twist to make this more efficient codewise: wrapper functions don't modify their arguments directly, they handle only two things:

1. Save their arguments
2. Call into an internal helper function called wrapper() which is the "dispatcher" that invokes pre, system libinst.so and post functions

This dispatcher restores the saved arguments prior to calling pre, system libinst.so and post functions. That way, the system libinst.so does not even notice that a pre function was invoked just before or a post function right after it.

The programmer in turn, can access the exact same arguments of the system libinst.so function from the pre and post functions.

Refer also to point 2.3. Improving wrapper functions where I forgot to add an image and some explanatory text about this. This has been corrected.

Regarding epilogues (post functions) that wish to modify return values after the system libinst.so call:

Code:
...


                        ret = (*pre_routine)((unsigned long long *) params_pre,
                                              &internal_ret);
                        if ((ret == 0) ||
                            (ret != 1))
                        {
                                // NOTE: Custom return values from pre routine


                                return internal_ret;
                        }


                        // NOTE: Restore parameters
                        //
                        //  Restore parameters saved on entering the routine.


                        __asm_restore_parameters(params);


                        (*internal_routine)();


                        __asm_save_retval();


                        ret = (*post_routine)((unsigned long long *) params_pre,
                                              &internal_ret);
                        if ((ret == 0) ||
                            (ret != 1))
                        {
                                // NOTE: Custom return values from post routine


...
The post function (post_routine in the above code) receives the internal_ret variable which contains the return value(s) returned from the system libinst.so function in registers v0 and v1.

(11-23-2022, 10:24 PM)Raion Wrote:  Links to the code:

https://gitea.irixce.org/TruHobbyist/inst-1.0.0

Perl Module:

https://gitea.irixce.org/TruHobbyist/Inst.pm-1.0.0

This will be incorporated into IRIXCE eventually. I thank Tru for doing this because it enables us to remove the old /sbin/tar and add bsdtar as the default system tar. I will be looking into/testing the inst code extensively before we jump the gun, but I'm excited.
Thanks for the links! I totally forgot to include them.
(This post was last modified: 11-24-2022, 09:53 AM by TruHobbyist.)
TruHobbyist
Developer

Trade Count: (0)
Posts: 195
Threads: 21
Joined: May 2018
Find Reply
11-24-2022, 09:52 AM


Forum Jump:


Users browsing this thread: 1 Guest(s)