home *** CD-ROM | disk | FTP | other *** search
/ Fish 'n' More 2 / fishmore-publicdomainlibraryvol.ii1991xetec.iso / fish / programming / libtool_463 / jimsexample / article < prev    next >
Text File  |  1991-03-09  |  15KB  |  296 lines

  1. For Amiga World Tech Journal. Nov 6, 1990. Set your editor's TAB width to 8
  2. characters (this file contains ASCII graphics).
  3.  
  4. From: Jim Fiore
  5. dissidents
  6. 730 Dawes Avenue
  7. Utica, NY 13502
  8. (315) 797-0343
  9.  
  10.  
  11.  
  12.             Shared Libraries for the Lazy
  13.  
  14. by Jim Fiore, dissidents
  15. BIX: jfiore
  16.  
  17. If you've done any significant programming on the Amiga, you are no doubt
  18. familiar with the concept of a shared library. Shared libraries are an
  19. integral part of the Amiga operating system. In essence, a shared library
  20. is a collection of routines which any application can utilize. Since the
  21. Amiga uses a multi-tasking operating system, it makes sense to offer library
  22. support as part of the operating system, rather than relying on typical link
  23. libraries. By doing so, applications using the functions do not require
  24. duplicates of the code. This saves system memory for other purposes (ie,
  25. other tasks, or perhaps larger data files in ram). Any collection of routines
  26. which might be used by several programs should be considered for conversion
  27. into a library. Classic examples of system libraries include the Intuition
  28. and Graphics libraries. In this way, graphics and user interface code is
  29. shared among several applications.
  30.  
  31. You may also wish to break up a larger program into a series of libraries
  32. instead of using overlays. In this way, libraries can be loaded as needed,
  33. keeping the core of the program relatively small. Finally, libraries can be a
  34. good way of sharing function capabilities with other people without sharing
  35. source code, or requiring a specific language or compiler. Given the
  36. pervasive use of libraries and their myriad advantages, learning how to
  37. create your own libraries is a useful skill. There have been a few techniques
  38. and examples shown in various places over the past few years. Personally, I
  39. have always found them lacking in a few specific areas. It seemed that you
  40. either had to maintain a large number of files in order to create the
  41. library, or the scheme was very much tied into a particular language or
  42. compiler. In this article, we'll look at what is perhaps the simplest way of
  43. creating a library. It doesn't require a lot of maintenance, and it can be
  44. used with a variety of languages, including assembly and C (both Manx and
  45. Lattice). All you will need to create (and ever modify in the future) is the
  46. functions of interest, and a function description file (sometimes referred to
  47. as ".fd files"). Our example will turn a set of ordinary C language functions
  48. into a shared library using the Manx C compiler, but you could just as easily
  49. use the Lattice compiler or assembly language.
  50.  
  51. Before we look at the example, it will help if we understand the structure of
  52. a shared library. A library may be broken into four main parts: a Library
  53. Node, a function jump table (often referred to as a vector table), the set of
  54. functions, and the global data for the library. In memory, a library looks
  55. something like this:
  56.  
  57.     ----------------------------
  58.  ------>|                          |
  59.  |    |        Functions         |
  60.  |  --->|                          |
  61.  |  |    ----------------------------
  62.  |  |
  63.  |  |    ----------------------------
  64.  |  ----|                          |
  65.  -------|       Vector Table       |
  66.     |                          |
  67.     ---------------------------- <---- The Library Base address
  68.     |                          |
  69.     |     Library structure    |
  70.     |                          |
  71.     ----------------------------
  72.     |                          |
  73.     |       Library data       |
  74.     |                          |
  75.     ----------------------------
  76.  
  77. Let's look at the Library structure first. A Library structure is defined as
  78. follows:
  79.  
  80. struct Library {
  81.     struct Node lib_Node;
  82.     UBYTE lib_Flags;
  83.     UBYTE lib_pad;
  84.     UWORD lib_NegSize;
  85.     UWORD lib_PosSize;
  86.     UWORD lib_Version;
  87.     UWORD lib_Revision;
  88.     APTR  lib_IdString;
  89.     ULONG lib_Sum;
  90.     UWORD lib_OpenCnt;
  91. };
  92.  
  93. The Flags field is used by Exec to keep track of what is happening with the
  94. library. The NegSize and PosSize fields indicate the size (in bytes) of the
  95. library, on either side of the library base. The Version and Revision fields
  96. are used to indicate future changes and updates to the library. The IdString
  97. is a pointer to a null-terminated ASCII string giving further information
  98. about the library. Sum is the library checksum which is used by Exec to
  99. ensure library integrity. The OpenCnt field holds the number of tasks which
  100. have opened the library so far. Every time a task calls OpenLibrary() for
  101. this library, this field is incremented. Every time the complementary
  102. CloseLibrary() function is called, this field is decremented. If the Open
  103. Count is zero, Exec may remove the library in order to free up ram.
  104.  
  105. The other major item of interest is the vector table. This is comprised of a
  106. group of 6 byte entries, one entry for each function in the library. Each
  107. entry consists of a jump instruction (2 bytes) followed by the absolute
  108. address of the function being called. Each function call then, is a multiple
  109. of 6 bytes behind the library base. For example, the seventh function in the
  110. table would be at location LibraryBase-42. These 6 bytes multiples are known
  111. as Library Vector Offsets, or LVOs, for short. Along with the normal
  112. application functions, there are four mandatory functions which all shared
  113. libraries must have. Consequently, the first application function is always
  114. the fifth one in the list, at position 30 (referred to as the Bias, in a .fd
  115. file). The four special functions are: Open, Close, Expunge and Reserved. The
  116. The Open and Close functions are called for each OpenLibrary() and
  117. CloseLibrary() call. The Expunge routine is used for final cleanup if the
  118. Open Count has reached zero and Exec has decided to remove the library. The
  119. Reserved function is for future use. Presently, all it should do is return 0.
  120.  
  121. In order to properly load a library, Exec needs a RomTag structure. In
  122. assembly, a RomTag looks something like this:
  123.  
  124. RomTag:
  125.     dc.w    $4AFC        ;Voodoo magic RomTag word.
  126.     dc.l    RomTag
  127.     dc.l    endRom
  128.     dc.b    NO_AUTO_INIT    ;Auto-initialize, or not.
  129.     dc.b    VERSION        ;Library version, as used in OpenLibrary().
  130.     dc.b    NT_LIBRARY    ;Type. This is a Library.
  131.     dc.b    PRIORITY
  132.     dc.l    Lib_Name    ;Pointer to Name string.
  133.     dc.l    Lib_Id        ;Pointer to ID string.
  134.     dc.l    Lib_Startup    ;Pointer to initialization routine.
  135. endRom
  136.  
  137. Normally, Priority is 0. NT_LIBRARY is defined as 9, and we'll be using
  138. a non auto-initialized library (0) since this saves space and reduces load
  139. time.
  140.  
  141. If you were building a library from scratch, besides the functions of
  142. interest, you would need to create, compile, assemble and link the RomTag,
  143. the Library structure, the function table, the initialization routine, and
  144. the Open, Close, and Expunge routines. Some of this may be reused from
  145. library to library, but there is still a reasonable amount of work involved.
  146. To circumvent this, you can use the LibTool utility. LibTool was created by
  147. Jeff Glatt of dissidents. (LibTool and its associated documentation and
  148. examples are copyrighted, but are freely redistributable. In other words,
  149. feel free to use it at any time, or give it to friends. You just can't sell
  150. it to anyone for profit.) LibTool will create all of the auxiliary items
  151. you'll need for your library (including pragmas and header files) from a
  152. single .fd file that you create. Using LibTool, you'll only need to make two
  153. files in order to create a library: the functions of interest, and the .fd
  154. file.
  155.  
  156. At this point, it's time to consider the code which will be turned into
  157. the library functions. There are a few rules which you should remember.
  158. We'll be using C, but these items are pertinent to any language. First,
  159. your code should be re-entrant. This means that it does not make use of
  160. global variables. All variables should either be held on the stack, or should
  161. be allocated as needed. The reason for this is that it is possible for
  162. several tasks to open and use a library simultaneously. If two tasks are
  163. calling the same function at about the same time, it is quite possible for
  164. one task to alter a global variable before the other task is finished with
  165. it. The results are chaotic at best. In contrast, it is perfectly acceptable
  166. to use global constants. Since constants are not altered by a function, there
  167. is no problem with one task stepping on another task. Examples of acceptable
  168. constants would be fixed strings and look-up tables (such as a sine wave
  169. table). The second item to remember is that if your library needs to use the
  170. functions contained in other libraries, these other libraries must be opened
  171. (and eventually closed) from within your library.  For example, you might
  172. wish to create a DrawBox() function based on the system functions Move() and
  173. Draw(). Since these two functions reside in the graphics library, graphics
  174. must be opened as part of your library's initialization. Graphics will then
  175. be closed as part of your library's expunge routine. The final item is that
  176. you should not use printf() style functions from within the library. The
  177. library will not have access to stdin, stdout, or stderr. The easy way around
  178. this is to have library functions which return error codes, which can then be
  179. interpreted by the calling program.
  180.  
  181. For our example, we're going to make a library which implements rectangular
  182. to polar and polar to rectangular conversions. The library will consist of
  183. four functions: Mag(), which takes the real and imaginary rectangular
  184. components and returns the polar vector's magnitude; Ang(), which takes the
  185. rectangular components and returns the vector's angle in degrees; Real(),
  186. which takes the polar magnitude and angle in degrees, and returns the real
  187. rectangular component, and Imaj() which takes the same polar arguments and
  188. returns the imaginary rectangular component. I chose these functions because
  189. they're reasonably useful, and fortunately, very quick to code. In any case,
  190. the you don't have to do much typing since all of the example code is found
  191. on disk.
  192.  
  193. These functions require the use of floating point math. We have several
  194. choices. For the sake of expediency, we'll use Motorola fast floating point.
  195. In order to make our functions, we'll need routines found in mathffp.library
  196. and mathtrans.library. Consequently, we have to open these two libraries as
  197. part of our initialization, and we need to close them as part of the expunge
  198. "clean up". If you look at the library code, AWlib.c, you can see our four
  199. short functions. We also have two other functions, myInit() and myFree().
  200. myInit() will be called as part of the initialization routine. It returns a
  201. BOOL value (TRUE or FALSE). All it does is open the required math libraries.
  202. If it can't open the libraries, myInit() returns FALSE. This will prevent our
  203. library from loading. myFree() is called as part of the expunge routine. It
  204. simply closes the libraries which were initially opened. If we needed to
  205. perform a certain amount of work each time the library was opened or closed,
  206. we could add our own custom functions there, as well.
  207.  
  208. Much of the drudgery of creating the library is going to be taken care of by
  209. LibTool, in conjunction with a specialized function description file. LibTool
  210. will create a library startup module which will contain the RomTag, the
  211. Library structure, the function table, the four mandatory functions, wedges
  212. into the various routines (eg, the references to our myInit() and myFree()
  213. functions), the proper version and revision numbers, strings, and the like.
  214. Also, this module will automatically open Exec, Dos, Intuition, and Graphics
  215. for you, since they are used so often (and are most likely already present in
  216. the system). Therefore, if you're only calling functions from these system
  217. libraries, chances are that you won't even need Init() and Free() routines.
  218. To aid in the construction of your applications which call your new library,
  219. LibTool can also create a header file, pragma statements, and C "glue"
  220. routines. The glue routines are used to pull C arguments off of the stack and
  221. stuff them into the proper registers for the library. It is possible to
  222. create libraries with LibTool that expect arguments on the stack. The
  223. resulting glue is smaller for C applications, but this does make it harder to
  224. access the library from other languages. Of course, the whole issue of glue
  225. routines is bypassed if pragmas are used. By using pragmas, the C compiler
  226. will move the arguments into the registers, and no glue is needed.
  227.  
  228. The .fd file is an extension of the ordinary .fd files which most Amiga
  229. programmers are familiar with. LibTool recognizes extra commands which allow
  230. it to create the complete Library Startup module. Here is our .fd file:
  231.  
  232. ##base AWBase     *the name of our base used by C glue code and C PRAGMAS
  233. ##name AW         *the name of our library (ie, aw.library)
  234. ##vers 1          *version #
  235. ##revs 0          *revision #
  236. ##init myInit     *to be called once (upon loading the lib)
  237. ##expu myFree     *to be called once (upon expunging the lib)
  238. ##libid  Amiga World TJ lib (ver 1.0)
  239. ##bias 30         *first function is always at an offset of -30 from lib base
  240. *Here are all of the lib functions callable by an application
  241. *They all return doubles
  242. ##ret double
  243. Mag( real, imaj )
  244. Ang( real, imaj )
  245. Real( mag, ang )
  246. Imaj( mag, ang )
  247. ##end
  248.  
  249. Notice that virtually everything you need to describe your library (and
  250. update it in the future) is found in this one file. Each specialized item is
  251. given its own command. Of particular interest are the ##init and ##expu
  252. commands which are followed by the names of our initialization and expunge
  253. routines. Commands are available for the previously mentioned Open and Close
  254. routines, if required (##open, ##clos).
  255.  
  256. Using Manx 5.0 from the CLI, you make the library as follows:
  257.  
  258. LibTool -cmho glue.asm AWlib.fd
  259.  
  260. This creates the library startup code (AWlib.src) using C style syntax (ie,
  261. it prepends an underscore to the function names), the C header file (AWlib.h)
  262. for the applications, and a C application glue file (glue.asm) which will be
  263. assembled, and then linked with the application.
  264.  
  265. as -cd -o LibStart.o AWlib.src
  266.  
  267. This assembles the library startup module. Note that we are using large code
  268. and data so that we don't have to worry about saving register A4, as you
  269. would with the small model.
  270.  
  271. cc -mcd0b -ff AWlib.c    
  272.  
  273. Here, we compile the library code, again using the large model. We are
  274. suppressing the standard Manx startup (.begin), and using fast floating point
  275. math.
  276.  
  277. ln -o libs:AW.library LibStart.o AWlib.o -lmfl -lcl
  278.  
  279. Finally, the library is created by linking together the needed parts. It is
  280. VERY important that LibStart.o be the first object module in the list.
  281.  
  282. The applications which call the library are compiled and linked in the
  283. standard manner. If you are using glue files (as we are here), glue.asm must
  284. be assembled and then linked with the application program. Our application,
  285. AWlib_app.c, simply opens the library, and calls each of the functions in 
  286. turn.
  287.  
  288. As mentioned earlier, LibTool can be used with the Lattice compiler, and with
  289. assembly language development systems. It can also be used for other
  290. purposes, including the creation of BASIC .bmap files, and in the
  291. construction of devices. Further details concerning LibTool are found on the
  292. disk, along with other examples.
  293.  
  294. Hopefully, this little foray has enticed you into creating some of your own
  295. shared libraries, and eased the programming burden as well. Have fun.
  296.