Usermode/ld-acess - Fix dynamic linking quirk (STB_WEAK and R_COPY)
[tpg/acess2.git] / Usermode / Libraries / ld-acess.so_src / loadlib.c
index 8583141..1c750a8 100644 (file)
-/*\r
- AcessOS 1 - Dynamic Loader\r
- By thePowersGang\r
-*/\r
-#include "common.h"\r
-\r
-#define DEBUG  0\r
-\r
-#if DEBUG\r
-# define DEBUGS(v...)  SysDebug(v)\r
-#else\r
-# define DEBUGS(v...)  \r
-#endif\r
-\r
-// === PROTOTYPES ===\r
-Uint   IsFileLoaded(char *file);
- int   GetSymbolFromBase(Uint base, char *name, Uint *ret);\r
-\r
-// === GLOABLS ===\r
-#define        MAX_LOADED_LIBRARIES    64\r
-#define        MAX_STRINGS_BYTES       4096\r
-struct {\r
-       Uint    Base;\r
-       char    *Name;\r
-}      gLoadedLibraries[MAX_LOADED_LIBRARIES];\r
-char   gsLoadedStrings[MAX_STRINGS_BYTES];\r
-char   *gsNextAvailString = gsLoadedStrings;\r
-//tLoadLib     *gpLoadedLibraries = NULL;\r
-\r
-// === CODE ===\r
-Uint LoadLibrary(char *filename, char *SearchDir, char **envp)\r
-{\r
-       char    sTmpName[1024] = "/Acess/Libs/";\r
-       Uint    iArg;\r
-       void    (*fEntry)(int, int, char *[], char**);\r
-       \r
-       DEBUGS("LoadLibrary: (filename='%s', envp=0x%x)\n", filename, envp);\r
-       \r
-       // Create Temp Name\r
-       strcpy(&sTmpName[12], filename);\r
-       DEBUGS(" LoadLibrary: sTmpName='%s'\n", sTmpName);\r
-       \r
-       if( (iArg = IsFileLoaded(sTmpName)) )\r
-               return iArg;\r
-       \r
-       // Load Library\r
-       iArg = SysLoadBin(sTmpName, (Uint*)&fEntry);\r
-       if(iArg == 0) {\r
-               DEBUGS("LoadLibrary: RETURN 0\n");\r
-               return 0;\r
-       }\r
-       \r
-       DEBUGS(" LoadLibrary: iArg=0x%x, iEntry=0x%x\n", iArg, fEntry);\r
-       \r
-       // Load Symbols\r
-       fEntry = (void*)DoRelocate( iArg, envp, sTmpName );\r
-       \r
-       // Call Entrypoint\r
-       DEBUGS(" LoadLibrary: '%s' Entry 0x%x\n", filename, fEntry);\r
-       fEntry(iArg, 0, NULL, envp);\r
-       \r
-       DEBUGS("LoadLibrary: RETURN 1\n");\r
-       return iArg;\r
+/*
+ AcessOS 1 - Dynamic Loader
+ By thePowersGang
+*/
+#include "common.h"
+#include <stdint.h>
+#include <stdbool.h>
+#include <acess/sys.h>
+
+#define DEBUG  0
+
+#if DEBUG
+# define DEBUGS(v...)  SysDebug(v)
+#else
+# define DEBUGS(v...)  
+#endif
+
+#define MAX_QUEUED_ENTRYPOINTS 8
+
+// === IMPORTS ===
+extern const struct {
+       void    *Value;
+       char    *Name;
+}      caLocalExports[];
+extern const int       ciNumLocalExports;
+extern char    **gEnvP;
+extern char    gLinkedBase[];
+
+// === TYPES ===
+typedef void   tLibEntry(void *, int, char *[], char**);
+
+// === PROTOTYPES ===
+void   *IsFileLoaded(const char *file);
+
+// === GLOABLS ===
+tLoadedLib     gLoadedLibraries[MAX_LOADED_LIBRARIES];
+char   gsLoadedStrings[MAX_STRINGS_BYTES];
+char   *gsNextAvailString = gsLoadedStrings;
+struct sQueuedEntry {
+       void    *Base;
+       tLibEntry       *Entry;
+}      gaQueuedEntrypoints[MAX_QUEUED_ENTRYPOINTS];
+ int   giNumQueuedEntrypoints;
+//tLoadLib     *gpLoadedLibraries = NULL;
+
+// === CODE ===
+void ldacess_DumpLoadedLibraries(void)
+{
+       for( int i = 0; i < MAX_LOADED_LIBRARIES; i ++ )
+       {
+               if(gLoadedLibraries[i].Base == 0)       break;  // Last entry has Base set to NULL
+               _SysDebug("%p: %s",
+                       gLoadedLibraries[i].Base,
+                       gLoadedLibraries[i].Name
+                       );
+       }
 }
-\r
-/**\r
- * \fn Uint IsFileLoaded(char *file)\r
- * \brief Determine if a file is already loaded\r
- */\r
-Uint IsFileLoaded(char *file)\r
-{\r
-        int    i;\r
-       DEBUGS("IsFileLoaded: (file='%s')", file);\r
-       for( i = 0; i < MAX_LOADED_LIBRARIES; i++ )\r
-       {\r
-               if(gLoadedLibraries[i].Base == 0)       break;  // Last entry has Base set to NULL\r
-               DEBUGS(" strcmp('%s', '%s')", gLoadedLibraries[i].Name, file);\r
-               if(strcmp(gLoadedLibraries[i].Name, file) == 0) {\r
-                       DEBUGS("IsFileLoaded: Found %i (0x%x)", i, gLoadedLibraries[i].Base);\r
-                       return gLoadedLibraries[i].Base;\r
-               }\r
-       }\r
-       DEBUGS("IsFileLoaded: Not Found");\r
-       return 0;\r
-}\r
-\r
-/**\r
- * \fn void AddLoaded(char *File, Uint base)\r
- * \brief Add a file to the loaded list\r
+
+/**
+ * \brief Call queued up entry points (after relocations completed) 
  */
-void AddLoaded(char *File, Uint base)
-{\r
-        int    i, length;\r
-       char    *name = gsNextAvailString;\r
-       \r
-       DEBUGS("AddLoaded: (File='%s', base=0x%x)", File, base);\r
-       \r
-       // Find a free slot\r
-       for( i = 0; i < MAX_LOADED_LIBRARIES; i ++ )\r
-       {\r
-               if(gLoadedLibraries[i].Base == 0)       break;\r
-       }\r
-       if(i == MAX_LOADED_LIBRARIES) {\r
-               SysDebug("ERROR - ld-acess.so has run out of load slots!");\r
-               return;\r
-       }\r
-       \r
-       // Check space in string buffer\r
-       length = strlen(File);\r
-       if(&name[length+1] >= &gsLoadedStrings[MAX_STRINGS_BYTES]) {\r
-               SysDebug("ERROR - ld-acess.so has run out of string buffer memory!");\r
-               return;\r
-       }\r
-       \r
-       // Set information\r
-       gLoadedLibraries[i].Base = base;\r
-       strcpy(name, File);\r
-       gLoadedLibraries[i].Name = name;\r
-       gsNextAvailString = &name[length+1];\r
-       DEBUGS("'%s' (0x%x) loaded as %i\n", name, base, i);\r
+void CallQueuedEntrypoints(char **EnvP)
+{
+       while( giNumQueuedEntrypoints )
+       {
+               giNumQueuedEntrypoints --;
+               const struct sQueuedEntry       *qe = &gaQueuedEntrypoints[giNumQueuedEntrypoints];
+               //_SysDebug("Calling EP for %p", qe->Base);
+               qe->Entry(qe->Base, 0, NULL, EnvP);
+       }
+}
+
+const char *FindLibrary(char *DestBuf, const char *SoName, const char *ExtraSearchDir)
+{      
+       // -- #1: Executable Specified
+       if(ExtraSearchDir)
+       {
+               strcpy(DestBuf, ExtraSearchDir);
+               strcat(DestBuf, "/");
+               strcat(DestBuf, SoName);
+               if(file_exists(DestBuf))        return DestBuf;
+       }
+       
+       // -- #2: System
+       strcpy(DestBuf, SYSTEM_LIB_DIR);
+       strcat(DestBuf, SoName);
+       if(file_exists(DestBuf))        return DestBuf;
+       
+       // -- #3: Current Directory
+       if(file_exists(SoName)) return SoName;
+       
+       return NULL;
+}
+
+/**
+ */
+void *LoadLibrary(const char *SoName, const char *SearchDir, char **envp)
+{
+       char    sTmpName[1024];
+       void    *base;
+       
+       DEBUGS("LoadLibrary: (SoName='%s', SearchDir='%s', envp=%p)", SoName, SearchDir, envp);
+       
+       // Create Temp Name
+       const char *filename = FindLibrary(sTmpName, SoName, SearchDir);
+       if(filename == NULL) {
+               DEBUGS("LoadLibrary: RETURN 0");
+               return 0;
+       }
+       DEBUGS(" LoadLibrary: filename='%s'", filename);
+       
+       if( (base = IsFileLoaded(filename)) )
+               return base;
+
+       DEBUGS(" LoadLibrary: SysLoadBin()");   
+       // Load Library
+       tLibEntry       *fEntry;
+       base = _SysLoadBin(filename, (void**)&fEntry);
+       if(!base) {
+               DEBUGS("LoadLibrary: RETURN 0");
+               return 0;
+       }
+       
+       DEBUGS(" LoadLibrary: iArg=%p, fEntry=%p", base, fEntry);
+       
+       // Load Symbols
+       fEntry = DoRelocate( base, envp, filename );
+       if( !fEntry ) {
+               return 0;
+       }
+       
+       // Call Entrypoint
+       // - TODO: Queue entrypoint calls
+       if( giNumQueuedEntrypoints >= MAX_QUEUED_ENTRYPOINTS ) {
+               SysDebug("ERROR - Maximum number of queued entrypoints exceeded on %p '%s'",
+                       base, SoName);
+               return 0;
+       }
+       gaQueuedEntrypoints[giNumQueuedEntrypoints].Base  = base;
+       gaQueuedEntrypoints[giNumQueuedEntrypoints].Entry = fEntry;
+       giNumQueuedEntrypoints ++;
+       
+       DEBUGS("LoadLibrary: RETURN success");
+       return base;
+}
+
+/**
+ * \fn Uint IsFileLoaded(char *file)
+ * \brief Determine if a file is already loaded
+ */
+void *IsFileLoaded(const char *file)
+{
+        int    i;
+       DEBUGS("IsFileLoaded: (file='%s')", file);
+
+       // Applications link against either libld-acess.so or ld-acess.so
+       if( strcmp(file, "/Acess/Libs/libld-acess.so") == 0
+        || strcmp(file, "/Acess/Libs/ld-acess.so") == 0 )
+       {
+               DEBUGS("IsFileLoaded: Found local (%p)", &gLinkedBase);
+               return &gLinkedBase;
+       }
+
+       for( i = 0; i < MAX_LOADED_LIBRARIES; i++ )
+       {
+               if(gLoadedLibraries[i].Base == 0)       break;  // Last entry has Base set to NULL
+               DEBUGS(" strcmp('%s', '%s')", gLoadedLibraries[i].Name, file);
+               if(strcmp(gLoadedLibraries[i].Name, file) == 0) {
+                       DEBUGS("IsFileLoaded: Found %i (%p)", i, gLoadedLibraries[i].Base);
+                       return gLoadedLibraries[i].Base;
+               }
+       }
+       DEBUGS("IsFileLoaded: Not Found");
+       return 0;
+}
+
+/**
+ * \fn void AddLoaded(char *File, Uint base)
+ * \brief Add a file to the loaded list
+ */
+void AddLoaded(const char *File, void *base)
+{
+        int    i, length;
+       char    *name = gsNextAvailString;
+       
+       DEBUGS("AddLoaded: (File='%s', base=%p)", File, base);
+       
+       // Find a free slot
+       for( i = 0; i < MAX_LOADED_LIBRARIES; i ++ )
+       {
+               if(gLoadedLibraries[i].Base == 0)       break;
+       }
+       if(i == MAX_LOADED_LIBRARIES) {
+               SysDebug("ERROR - ld-acess.so has run out of load slots!");
+               return;
+       }
+       
+       // Check space in string buffer
+       length = strlen(File);
+       if(&name[length+1] >= &gsLoadedStrings[MAX_STRINGS_BYTES]) {
+               SysDebug("ERROR - ld-acess.so has run out of string buffer memory!");
+               return;
+       }
+       
+       // Set information
+       gLoadedLibraries[i].Base = base;
+       strcpy(name, File);
+       gLoadedLibraries[i].Name = name;
+       gsNextAvailString = &name[length+1];
+       DEBUGS("'%s' (%p) loaded as %i", name, base, i);
        return;
-}\r
-\r
-/**\r
- * \fn void Unload(Uint Base)\r
- */\r
-void Unload(Uint Base)\r
-{      \r
-        int    i, j;\r
-        int    id;\r
-       char    *str;\r
-       for( id = 0; id < MAX_LOADED_LIBRARIES; id++ )\r
-       {\r
-               if(gLoadedLibraries[id].Base == Base)   break;\r
-       }\r
-       if(id == MAX_LOADED_LIBRARIES)  return;\r
-       \r
-       // Unload Binary\r
-       SysUnloadBin( Base );\r
-       // Save String Pointer\r
-       str = gLoadedLibraries[id].Name;\r
-       \r
-       // Compact Loaded List\r
-       j = id;\r
-       for( i = j + 1; i < MAX_LOADED_LIBRARIES; i++, j++ )\r
-       {\r
-               if(gLoadedLibraries[i].Base == 0)       break;\r
-               // Compact String\r
-               strcpy(str, gLoadedLibraries[i].Name);\r
-               str += strlen(str)+1;\r
-               // Compact Entry\r
-               gLoadedLibraries[j].Base = gLoadedLibraries[i].Base;\r
-               gLoadedLibraries[j].Name = str;\r
-       }\r
-       \r
-       // NULL Last Entry\r
-       gLoadedLibraries[j].Base = 0;\r
-       gLoadedLibraries[j].Name = NULL;\r
-       // Save next string\r
-       gsNextAvailString = str;\r
-}\r
-\r
+}
+
 /**
- \fn Uint GetSymbol(char *name)
+ * \fn void Unload(Uint Base)
+ */
+void Unload(void *Base)
+{      
+        int    i, j;
+        int    id;
+       char    *str;
+       for( id = 0; id < MAX_LOADED_LIBRARIES; id++ )
+       {
+               if(gLoadedLibraries[id].Base == Base)   break;
+       }
+       if(id == MAX_LOADED_LIBRARIES)  return;
+       
+       // Unload Binary
+       _SysUnloadBin( Base );
+       // Save String Pointer
+       str = gLoadedLibraries[id].Name;
+       
+       // Compact Loaded List
+       j = id;
+       for( i = j + 1; i < MAX_LOADED_LIBRARIES; i++, j++ )
+       {
+               if(gLoadedLibraries[i].Base == 0)       break;
+               // Compact String
+               strcpy(str, gLoadedLibraries[i].Name);
+               str += strlen(str)+1;
+               // Compact Entry
+               gLoadedLibraries[j].Base = gLoadedLibraries[i].Base;
+               gLoadedLibraries[j].Name = str;
+       }
+       
+       // NULL Last Entry
+       gLoadedLibraries[j].Base = 0;
+       gLoadedLibraries[j].Name = NULL;
+       // Save next string
+       gsNextAvailString = str;
+}
+
+/**
+ \fn Uint GetSymbol(const char *name)
  \brief Gets a symbol value from a loaded library
 */
-Uint GetSymbol(char *name)
-{\r
-        int    i;
-       Uint    ret;\r
-       for(i=0;i<sizeof(gLoadedLibraries)/sizeof(gLoadedLibraries[0]);i++)\r
-       {\r
-               if(gLoadedLibraries[i].Base == 0)       break;\r
+int GetSymbol(const char *name, void **Value, size_t *Size, void *IgnoreBase)
+{
+       //SysDebug("GetSymbol: (%s)");
+       for( int i = 0; i < ciNumLocalExports; i ++ )
+       {
+               if( strcmp(caLocalExports[i].Name, name) == 0 ) {
+                       *Value = caLocalExports[i].Value;
+                       if(Size)
+                               *Size = 0;
+                       //SysDebug("GetSymbol: Local %p+0x%x", *Value, 0);
+                       return 1;
+               }
+       }
+
+       bool have_weak = false; 
+       for(int i = 0; i < MAX_LOADED_LIBRARIES && gLoadedLibraries[i].Base != 0; i ++)
+       {
+               // Allow ignoring the current module
+               if( gLoadedLibraries[i].Base == IgnoreBase ) {
+                       //SysDebug("GetSymbol: Ignore %p", gLoadedLibraries[i].Base);
+                       continue ;
+               }
                
-               //SysDebug(" GetSymbol: Trying 0x%x, '%s'\n",\r
+               //SysDebug(" GetSymbol: Trying 0x%x, '%s'",
                //      gLoadedLibraries[i].Base, gLoadedLibraries[i].Name);
-               if(GetSymbolFromBase(gLoadedLibraries[i].Base, name, &ret))     return ret;\r
-       }\r
-       SysDebug("GetSymbol: === Symbol '%s' not found ===\n", name);\r
-       return 0;
+               void    *tmpval;
+               size_t  tmpsize;
+               int rv = GetSymbolFromBase(gLoadedLibraries[i].Base, name, &tmpval, &tmpsize);
+               if(rv)
+               {
+                       *Value = tmpval;
+                       *Size = tmpsize;
+                       if( rv == 1 ) {
+                               return 1;
+                       }
+                       have_weak = true;
+               }
+       }
+       if(have_weak) {
+               return 2;
+       }
+       else {
+               return 0;
+       }
 }
 
 /**
  \fn int GetSymbolFromBase(Uint base, char *name, Uint *ret)
  \breif Gets a symbol from a specified library
 */
-int GetSymbolFromBase(Uint base, char *name, Uint *ret)
+int GetSymbolFromBase(void *base, const char *name, void **ret, size_t *Size)
 {
-       if(*(Uint32*)base == (0x7F|('E'<<8)|('L'<<16)|('F'<<24)))
-               return ElfGetSymbol(base, name, ret);
-       if(*(Uint16*)base == ('M'|('Z'<<8)))
-               return PE_GetSymbol(base, name, ret);
+       uint8_t *hdr = base;
+       if(hdr[0] == 0x7F && hdr[1] == 'E' && hdr[2] == 'L' && hdr[3] == 'F')
+               return ElfGetSymbol(base, name, ret, Size);
+       if(hdr[0] == 'M' && hdr[1] == 'Z')
+               return PE_GetSymbol(base, name, ret, Size);
+       SysDebug("Unknown type at %p (%02x %02x %02x %02x)", base,
+               hdr[0], hdr[1], hdr[2], hdr[3]);
        return 0;
 }
 

UCC git Repository :: git.ucc.asn.au