From: Robert Dubner <[email protected]>
Date: Wed, 23 Sep 2026 11:27:31 -0400
Subject: [PATCH] cobol: Update dynamic linking search paths.

These changes modify the policies used to search for .so files when
accessing dynamically-linked programs.

gcc/cobol/ChangeLog:

        * gcobol.1: Document the changes.
        * genapi.cc (function_pointer_from_name): Honor the
        >>CALL-CONVENTION.
        (parser_set_pointers): Repair the ZEROES bug.

libgcobol/ChangeLog:

        * libgcobol.cc (do_the_dl_thing): Implement the updated policies.
        (find_in_dirs): Use modified do_the_dl_thing().
        (__gg__function_handle_from_cobpath): Honor the call_convention.
        (function_handle_from_cobpath): Likewise.
        (__gg__function_handle_from_literal): Likewise.
        (__gg__function_handle_from_name): Likewise.
---
 gcc/cobol/gcobol.1     |  25 +--
 gcc/cobol/genapi.cc    |  31 +++-
 libgcobol/libgcobol.cc | 359 +++++++++++++++++++++++++++++++----------
 3 files changed, 311 insertions(+), 104 deletions(-)

diff --git a/gcc/cobol/gcobol.1 b/gcc/cobol/gcobol.1
index cdcb7146810..81dd64e76cf 100644
--- a/gcc/cobol/gcobol.1
+++ b/gcc/cobol/gcobol.1
@@ -1954,8 +1954,8 @@ precisely match the behavior of other \*[lang]
compilers.
 You have been warned.
 .
 .Sh ENVIRONMENT
-.Bl -tag -width GCOBOL_LIBRARY_PATH
-.It Ev GCOBOL_LIBRARY_PATH
+.Bl -tag -width COBPATH
+.It Ev COBPATH
 If defined, specifies the directory paths to be used by the
 .Nm
 runtime library,
@@ -1965,11 +1965,11 @@ Like
 .Ev LD_LIBRARY_PATH ,
 it may contain several directory names separated by a colon
 .Pq Ql \&: .
-.Ev GCOBOL_LIBRARY_PATH
+.Ev COBPATH
 is searched first, followed by
 .Ev LD_LIBRARY_PATH .
 Note that
-.Ev GCOBOL_LIBRARY_PATH does not change where the runtime linker looks
for
+.Ev COBPATH does not change where the runtime linker looks for
 .Pa libgcobol.so
 itself.
 How the runtime linker searches for
@@ -1978,18 +1978,21 @@ when the executable loads is controlled by
 .Xr ld.so 8 ,
 not libgcobol.
 .Pp
-Each directory is searched for a file whose name is based on the desired
-symbol.  The literal name, with
-.Ql ".so"
-appended, is tried first.  For each such file,
+Each directory is searched for files whose names are based on the desired
+symbol.  As an example, consider a CALL to the program-id "FooBar". When
the
+.Sy CALL-CONVENTION COBOL
+is operative, the symbol is forced to lowercase and an attempt is made to
find
+"FOOBAR.so", followed by an attempt to find "foobar.so".  When
+.Sy CALL-CONVENTION VERBATIM
+is operative, attempts are made to find "FOOBAR.so", then "FooBar.so, and
finally
+"foobar.so".  For each such file,
 .Xr dlopen 3
 is attempted, and if successful,
 .Xr dlsym 3 is used to search for the 
-symbol.  If either fails, a second attempt is made after converting
-the desired symbol to lowercase.
+symbol.
 .Pp
 When 
-.Ev GCOBOL_LIBRARY_PATH and 
+.Ev COBPATH and 
 .Ev LD_LIBRARY_PATH are not explicitly used, 
 binaries produced by
 .Nm
diff --git a/gcc/cobol/genapi.cc b/gcc/cobol/genapi.cc
index e3753fd7709..301fd6f82e2 100644
--- a/gcc/cobol/genapi.cc
+++ b/gcc/cobol/genapi.cc
@@ -806,6 +806,8 @@ function_pointer_from_name(const cbl_refer_t &name,
                          NULL);
   tree function_pointer_type = build_pointer_type(function_type);
 
+  cbl_call_convention_t call_convention = current_call_convention();
+
   if( name.field->type == FldPointer )
     {
     tree location;
@@ -857,6 +859,7 @@ function_pointer_from_name(const cbl_refer_t &name,
                    INT,
                    current_function->our_symbol_table_index),
                  gg_string_literal(name.field->data.original()),
+                 build_int_cst_type(INT, call_convention),
                  NULL_TREE));
     }
   else
@@ -872,6 +875,7 @@ function_pointer_from_name(const cbl_refer_t &name,
                  gg_get_address_of(name.field->var_decl_node),
                  refer_offset(name),
                  refer_size_source(name),
+                 build_int_cst_type(INT, call_convention),
                  NULL_TREE));
     }
 
@@ -13601,12 +13605,27 @@ parser_set_pointers( size_t ntgt, cbl_refer_t
*tgts, cbl_refer_t source )
         && (source.field->type == FldAlphanumeric
             || source.field->type == FldLiteralA))
       {
-      // This is something like SET varp TO ENTRY "ref".
-      tree function_pointer = function_pointer_from_name(source,
-
COBOL_FUNCTION_RETURN_64);
-      gg_memcpy(qualified_data_location(tgts[i]),
-                gg_get_address_of(function_pointer),
-                sizeof_pointer);
+      if( strcmp(source.field->name, "ZEROES") == 0 )
+        {
+        // This is an extra-special corner case.  No, it's worse than a
corner
+        // case.  It's a pointy case.  The caller is essentially setting
the
+        // pointer to the figurative constant ZEROES.  But ZERO is both
an
+        // alphanumeric and a numeric, depending on context.  Fine.  It's
+        // weird.  Let's just set the destination to zeroes, and be done
with
+        // it.
+        gg_memset(qualified_data_location(tgts[i]),
+                  integer_zero_node,
+                  sizeof_pointer);
+        }
+      else
+        {
+        // This is something like SET varp TO ENTRY "ref".
+        tree function_pointer = function_pointer_from_name(source,
+
COBOL_FUNCTION_RETURN_64);
+        gg_memcpy(qualified_data_location(tgts[i]),
+                  gg_get_address_of(function_pointer),
+                  sizeof_pointer);
+        }
       }
     else
       {
diff --git a/libgcobol/libgcobol.cc b/libgcobol/libgcobol.cc
index 4332d7ddee0..9890c7dda88 100644
--- a/libgcobol/libgcobol.cc
+++ b/libgcobol/libgcobol.cc
@@ -10901,70 +10901,39 @@ __gg__set_program_list( int program_id,
 static std::unordered_map<std::string, void *> already_found;
 static void *
 do_the_dl_thing(const char *directory,
-                const char *unmangled_name,
-                const char *mangled_name)
+                const std::string &file,
+                const std::string &name)
   {
-  // Using the GnuCOBOL convention, this code looks first for a file
-  // named unmangled_name.so.  It checks for a function unmangled_name
-  // in that file.  When not found, it checks for mangled_name.
   void *retval = NULL;
-  void *handle;
-  std::string file;
+  std::string path;
   if( directory )
     {
-    file = directory;
-    file += '/';
+    path = directory;
+    path += '/';
     }
-  file += unmangled_name;
-  file += ".so";
-  handle = dlopen(file.c_str(), RTLD_LAZY|RTLD_NODELETE );
+  path += file;
+  void * handle = dlopen(path.c_str(), RTLD_NOW|RTLD_NODELETE );
   if( handle )
     {
-    retval = dlsym(handle, unmangled_name);
-    if( retval )
-      {
-      already_found[unmangled_name] = retval;
-      }
-    }
-  if( !retval )
-    {
-    file.clear();
-    if( directory )
-      {
-      file = directory;
-      file += '/';
-      }
-    file += mangled_name;
-    file += ".so";
-    handle = dlopen(file.c_str(), RTLD_LAZY|RTLD_NODELETE );
-    if( handle )
-      {
-      retval = dlsym(handle, mangled_name);
-      if( retval )
-        {
-        already_found[mangled_name] = retval;
-        }
-      }
+    retval = dlsym(handle, name.c_str());
+    dlclose(handle);
     }
   return retval;
   }
 
 static
 void *
-find_in_dirs(const char *dirs, char *unmangled_name, char *mangled_name)
-  {
-  std::unordered_map<std::string, void *>::const_iterator it =
-                    already_found.find(unmangled_name);
-
-  if( it != already_found.end() )
-    {
-    return it->second;
-    }
-  it = already_found.find(mangled_name);
-  if( it != already_found.end() )
-    {
-    return it->second;
-    }
+find_in_dirs( const char *dirs,
+              const std::string &fileUPPER,
+              const std::string &fileMiddle,
+              const std::string &filelower,
+              const std::string &unmangled_name,
+              const std::string &mangled_name)
+  {
+  // We know fileUPPER and unmangled_name are not empty.
+  // The others might be empty.
+  assert( !fileUPPER.empty() );
+  assert( !unmangled_name.empty() );
 
   void *retval = NULL;
   if( dirs )
@@ -10982,62 +10951,272 @@ find_in_dirs(const char *dirs, char
*unmangled_name, char *mangled_name)
         {
         p += 1;
         }
-      // directory is the next one for us to check:
-      DIR *dir = opendir(directory.c_str());
-      if( dir )
+      if( directory.empty() )
+        {
+        break;
+        }
+
+      retval = do_the_dl_thing(directory.c_str(),
+                               fileUPPER,
+                               unmangled_name);
+      if( retval )
+        {
+        goto bugout;
+        }
+      if( !mangled_name.empty() )
         {
         retval = do_the_dl_thing(directory.c_str(),
-                                 unmangled_name,
+                                 fileUPPER,
                                  mangled_name);
-        closedir(dir);
+        if( retval )
+          {
+          goto bugout;
+          }
+        }
+
+      if( !fileMiddle.empty() )
+        {
+        retval = do_the_dl_thing(directory.c_str(),
+                                 fileMiddle,
+                                 unmangled_name);
+        if( retval )
+          {
+          goto bugout;
+          }
+        if( !mangled_name.empty() )
+          {
+          retval = do_the_dl_thing(directory.c_str(),
+                                   fileMiddle,
+                                   mangled_name);
+          if( retval )
+            {
+            goto bugout;
+            }
+          }
+        }
+      if( !filelower.empty() )
+        {
+        retval = do_the_dl_thing(directory.c_str(),
+                                 filelower,
+                                 unmangled_name);
+        if( retval )
+          {
+          goto bugout;
+          }
+        if( !mangled_name.empty() )
+          {
+          retval = do_the_dl_thing(directory.c_str(),
+                                   filelower,
+                                   mangled_name);
+          if( retval )
+            {
+            goto bugout;
+            }
+          }
         }
       }
     }
   else
     {
     retval = do_the_dl_thing(nullptr,
-                             unmangled_name,
-                             mangled_name);
+                             fileUPPER,
+                             unmangled_name);
+    if( retval )
+      {
+      goto bugout;
+      }
+    if( !mangled_name.empty() )
+      {
+      retval = do_the_dl_thing(nullptr,
+                               fileUPPER,
+                               mangled_name);
+      if( retval )
+        {
+        goto bugout;
+        }
+      }
+    if( !fileMiddle.empty() )
+      {
+      retval = do_the_dl_thing(nullptr,
+                               fileMiddle,
+                               unmangled_name);
+      if( retval )
+        {
+        goto bugout;
+        }
+      if( !mangled_name.empty() )
+        {
+        retval = do_the_dl_thing(nullptr,
+                                 fileMiddle,
+                                 mangled_name);
+        if( retval )
+          {
+          goto bugout;
+          }
+        }
+      }
+    if( !filelower.empty() )
+      {
+      retval = do_the_dl_thing(nullptr,
+                               filelower,
+                               unmangled_name);
+      if( retval )
+        {
+        goto bugout;
+        }
+      if( !mangled_name.empty() )
+        {
+        retval = do_the_dl_thing(nullptr,
+                                 filelower,
+                                 mangled_name);
+        if( retval )
+          {
+          goto bugout;
+          }
+        }
+      }
     }
+  bugout:
   return retval;
   }
 
-extern "C"
-void *
-__gg__function_handle_from_cobpath( char *unmangled_name, char
*mangled_name)
+static void *
+function_handle_from_cobpath( const char *unmangled_name_,
+                              const char *mangled_name_,
+                              int         call_convention)
   {
-  void *retval;
+  void *retval = NULL;
 
-  // We search for a function.  We check first for the unmangled name,
and then
-  // the mangled name.  We do this first for the executable, then for .so
-  // files in GCOBOL_LIBRARY_PATH, and then we allow dlopen to use its
default
-  // behavior.
+  /* We attempt to look up a function FooBar.
+     We first look for FooBar in the function.
+     We then look for foobar in the function.
+     We then scan the directories in COBPATH.  In each directory, we look
for
+     the function name in 
+        FOOBAR.so
+        FooBar.so
+        foobar.so
+     We then allow dlopen to use its defaults to find
+        FooBar
+        foobar
+     We then return NULL, indicating failure. */
 
-  static void *handle_executable = NULL;
-  if( !handle_executable )
-    {
-    handle_executable = dlopen(NULL, RTLD_NOW);
-    }
-  retval = dlsym(handle_executable, unmangled_name);
-  if( !retval )
+  std::string unmangled_name = unmangled_name_;
+  std::string mangled_name   = mangled_name_;
+
+  if( call_convention == 'N' )
     {
-    retval = dlsym(handle_executable, mangled_name);
+    // This is Native COBOL
+    // Use the mangled, lowercase name.
+    unmangled_name = mangled_name;
+    mangled_name.clear();
     }
-  if( !retval )
+  else 
     {
-    const char *COBPATH = getenv("GCOBOL_LIBRARY_PATH");
-    retval = find_in_dirs(COBPATH, unmangled_name, mangled_name);
+    // This is Verbatim, C-style.
+    mangled_name.clear();
     }
-  if( !retval )
+
+  std::string fileUPPER;
+  std::string fileMiddle;
+  std::string filelower;
+  const char *COBPATH;
+
+  static std::unordered_map<std::string, void *> already_searched;
+  std::unordered_map<std::string, void *>::const_iterator it =
+                          already_searched.find(unmangled_name);
+  if( it != already_searched.end() )
     {
-    const char *COBPATH = getenv("LD_LIBRARY_PATH");
-    retval = find_in_dirs(COBPATH, unmangled_name, mangled_name);
+    // We've already searched for this one, so return the result from the
+    // original attempt.
+    retval = it->second;
     }
-  if( !retval )
+  else
     {
-    retval = find_in_dirs(nullptr, unmangled_name, mangled_name);
-    }
+    // Look for unmangled in the executable
+    static void *handle_executable = NULL;
+    if( !handle_executable )
+      {
+      handle_executable = dlopen(NULL, RTLD_NOW);
+      }
+
+    retval = dlsym(handle_executable, unmangled_name.c_str());
+    if( retval )
+      {
+      goto bugout;
+      }
+
+    // Look for mangled_name in the executable, provided it is not the
same as
+    // unmangled name:
+    if( mangled_name == unmangled_name )
+      {
+      mangled_name.clear();
+      }
+    else
+      {
+      retval = dlsym(handle_executable, mangled_name.c_str());
+      if( retval )
+        {
+        goto bugout;
+        }
+      }
+
+    // We are going to be looking for a .so file with our entry point in
it.
+    // Build up the three filename roots:
+    fileUPPER.clear();
+    for(size_t i=0; i<unmangled_name.size(); i++)
+      {
+      char ch = unmangled_name[i];
+      fileUPPER += (ch >= 'a' && ch <= 'z') ? ch - 'a' + 'A' : ch;
+      }
+    fileUPPER += ".so";
+
+    fileMiddle = unmangled_name;
+    fileMiddle += ".so";
 
+    filelower.clear();
+    for(size_t i=0; i<unmangled_name.size(); i++)
+      {
+      char ch = unmangled_name[i];
+      filelower += (ch >= 'A' && ch <= 'Z') ? ch - 'A' + 'a' : ch;
+      }
+    filelower += ".so";
+
+    // Do some extra work here to avoid extra work later.
+    if( fileMiddle == fileUPPER )
+      {
+      fileMiddle.clear();
+      }
+    if( filelower == fileMiddle )
+      {
+      filelower.clear();
+      }
+
+    // We need to search through the COBPATH directories:
+
+    COBPATH = getenv("COBPATH");
+    if( COBPATH )
+      {
+
+      retval = find_in_dirs(COBPATH,
+                            fileUPPER,
+                            fileMiddle,
+                            filelower,
+                            unmangled_name,
+                            mangled_name);
+      if( retval )
+        {
+        goto bugout;
+        }
+      }
+    retval = find_in_dirs(nullptr,
+                          fileUPPER,
+                          fileMiddle,
+                          filelower,
+                          unmangled_name,
+                          mangled_name);
+    bugout:
+    already_searched[unmangled_name] = retval;
+    }
   return retval;
   }
 
@@ -11106,7 +11285,8 @@ __gg__just_mangle_name( const cblc_field_t
*field,
 extern "C"
 void *
 __gg__function_handle_from_literal(int         program_id,
-                                   const char *literal)
+                                   const char *literal,
+                                   int         call_convention )
   {
   void *retval = NULL;
   static char ach_unmangled[1024];
@@ -11138,7 +11318,9 @@ __gg__function_handle_from_literal(int
program_id,
     }
   else
     {
-    retval = __gg__function_handle_from_cobpath(ach_unmangled,
ach_mangled);
+    retval = function_handle_from_cobpath(ach_unmangled,
+                                          ach_mangled,
+                                          call_convention);
     }
 
   return retval;
@@ -11149,7 +11331,8 @@ void *
 __gg__function_handle_from_name(int                 program_id,
                                 const cblc_field_t *field,
                                 size_t              offset,
-                                size_t              length )
+                                size_t              length,
+                                int                 call_convention )
   {
   void *retval = NULL;
   static char ach_name[1024];
@@ -11198,7 +11381,9 @@ __gg__function_handle_from_name(int
program_id,
     }
   else
     {
-    retval = __gg__function_handle_from_cobpath(ach_unmangled,
ach_mangled);
+    retval = function_handle_from_cobpath(ach_unmangled,
+                                          ach_mangled,
+                                          call_convention);
     }
 
   return retval;
-- 
2.34.1

Reply via email to