Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Add a COB_LOAD_GLOBAL config option to modify dlopen behavior #209

Draft
wants to merge 16 commits into
base: gcos4gnucobol-3.x
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ NEWS - user visible changes -*- outline -*-
$b for executable basename, $d for date in YYYYMMDD format, $t for time
in HHMMSS format (before, only $$ was available for pid)

** introduction of the COB_LOAD_GLOBAL boolean flag, which determines whether
the symbols of dynamically loaded shared libraries are made available
globally to other shared objects or remain confined locally. Previously,
this behavior depended on the underlying platform, where the default was
often "global." The default behavior is now explicitly set to "local."

* New build features

** configure now uses pkg-config/ncurses-config to search for ncurses and
Expand Down
4 changes: 4 additions & 0 deletions config/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@

2024-12-16 Simon Sobisch <[email protected]>

* config/runtime.cfg: Add documentation for COB_LOAD_GLOBAL flag

2024-08-17 Ammar Almoris <[email protected]>

FR #474: add runtime configuration to hide cursor for extended screenio
Expand Down
12 changes: 12 additions & 0 deletions config/runtime.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,18 @@
# Default: false
# Example: PHYSICAL_CANCEL TRUE

# Environment name: COB_LOAD_GLOBAL
# Parameter name: load_global
# Purpose: tell the system loader to provide symbols in CALLed
# programs globally, allowing symbols to be found for linked
# libraries later
# Type: boolean
# Note: COBOL CALLs will always find symbols in already CALLed or
# pre-modules; this setting is mostly an advise to the system,
# not all systems are capable of loading libraries global/local
# Default: false
GitMensch marked this conversation as resolved.
Show resolved Hide resolved
# Example: load_global true

#
## File I/O
#
Expand Down
6 changes: 6 additions & 0 deletions libcob/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@

2025-01-08 Florian Schmidt <[email protected]>

* libcob/call.c, libcob/coblocal.h, libcob/common.c: Introduce a flag
COB_LOAD_GLOBAL (load_global) to toggle if the symbols of CALLed
programs should be made available in the global namespace

2024-12-11 Emilien Lemaire <[email protected]>

* fileio.c: fixed Bug #1032 by always using global thread-static variable
Expand Down
95 changes: 77 additions & 18 deletions libcob/call.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,6 @@ FILE *fmemopen (void *buf, size_t size, const char *mode);
#include <windows.h>
#include <io.h> /* for access */

static HMODULE
lt_dlopen (const char *x)
{
if (x == NULL) {
return GetModuleHandle (NULL);
}
return LoadLibrary(x);
}

static void *
lt_dlsym (HMODULE hmod, const char *p)
{
Expand Down Expand Up @@ -106,7 +97,6 @@ lt_dlerror (void)
/* note: only defined in configure when HAVE_DLFCN_H is true and dlopen can be linked */
#include <dlfcn.h>

#define lt_dlopen(x) dlopen(x, RTLD_LAZY | RTLD_GLOBAL)
#define lt_dlsym(x,y) dlsym(x, y)
#define lt_dlclose(x) dlclose(x)
#define lt_dlerror() dlerror()
Expand Down Expand Up @@ -171,6 +161,10 @@ static char *call_filename_buff;
static lt_dlhandle mainhandle;
#endif

#if !defined(_WIN32) && !defined(USE_LIBDL)
static lt_dladvise advise = NULL;
#endif

static size_t call_lastsize;
static size_t resolve_size = 0;
static unsigned int cob_jmp_primed;
Expand Down Expand Up @@ -289,6 +283,37 @@ static int last_entry_is_working_directory (const char *buff, const char *pstr)
return 0;
}

static void* cob_dlopen(const char* filename) {
#ifdef _WIN32
if (filename == NULL) {
return GetModuleHandle (NULL);
}
return LoadLibrary(filename);
#elif defined(USE_LIBDL)
int flags = cobsetptr->cob_load_global
? RTLD_LAZY | RTLD_GLOBAL
: RTLD_LAZY | RTLD_LOCAL;

return dlopen(filename, flags);
#else
if (advise != NULL) {
int error;
if (cobsetptr->cob_load_global) {
error = lt_dladvise_global(&advise);
} else {
error = lt_dladvise_local(&advise);
}

if (error) {
cob_runtime_warning("set link loader hint failed; %s", lt_dlerror());
}
}

void* handle = lt_dlopenadvise(filename, advise);
return handle;
#endif
}

/* resolves the actual library path used from
* COB_LIBRARY_PATH runtime setting
* "." as current working direktory [if not included already: prefixed]
Expand Down Expand Up @@ -560,9 +585,20 @@ add_to_preload (const char *path, lt_dlhandle libhandle, struct struct_handle *l
base_preload_ptr = preptr;
}
#else
COB_UNUSED (last_elem);
preptr->next = base_preload_ptr;
base_preload_ptr = preptr;
// Use the same logic as above in case the cob_load_global is set to local
if (!cobsetptr->cob_load_global) {
if (last_elem) {
last_elem->next = preptr;
} else {
preptr->next = NULL;
base_preload_ptr = preptr;
}
} else {
COB_UNUSED (last_elem);
preptr->next = base_preload_ptr;
base_preload_ptr = preptr;
}

#endif

if (!cobsetptr->cob_preload_str) {
Expand Down Expand Up @@ -595,6 +631,9 @@ cache_preload (const char *path)
/* Save last element of preload list */
if (!preptr->next) last_elem = preptr;
#endif
if(!cobsetptr->cob_load_global) {
if (!preptr->next) last_elem = preptr;
}
}

/* Check for duplicate in already loaded programs;
Expand Down Expand Up @@ -624,7 +663,7 @@ cache_preload (const char *path)
return 0;
}

libhandle = lt_dlopen (path);
libhandle = cob_dlopen (path);
if (!libhandle) {
cob_runtime_warning (
_("preloading from existing path '%s' failed; %s"), path, lt_dlerror());
Expand Down Expand Up @@ -870,7 +909,7 @@ cob_resolve_internal (const char *name, const char *dirent,
for (p = call_filename_buff; *p; ++p) {
*p = (cob_u8_t)toupper(*p);
}
handle = lt_dlopen (call_filename_buff);
handle = cob_dlopen (call_filename_buff);
if (handle != NULL) {
/* Candidate for future calls */
cache_dynload (call_filename_buff, handle);
Expand Down Expand Up @@ -913,7 +952,7 @@ cob_resolve_internal (const char *name, const char *dirent,
return NULL;
}
lt_dlerror (); /* clear last error conditions */
handle = lt_dlopen (call_filename_buff);
handle = cob_dlopen (call_filename_buff);
if (handle != NULL) {
/* Candidate for future calls */
cache_dynload (call_filename_buff, handle);
Expand Down Expand Up @@ -946,7 +985,7 @@ cob_resolve_internal (const char *name, const char *dirent,
call_filename_buff[COB_NORMAL_MAX] = 0;
if (access (call_filename_buff, R_OK) == 0) {
lt_dlerror (); /* clear last error conditions */
handle = lt_dlopen (call_filename_buff);
handle = cob_dlopen (call_filename_buff);
if (handle != NULL) {
/* Candidate for future calls */
cache_dynload (call_filename_buff, handle);
Expand Down Expand Up @@ -1558,6 +1597,17 @@ cob_exit_call (void)
#endif
#endif

#if !defined(_WIN32) && !defined(USE_LIBDL)
if (advise != NULL) {
int error = lt_dladvise_destroy(&advise);
if (error) {
const char * msg = lt_dlerror();
/* not translated as highly unlikely */
cob_runtime_warning (
"destroying link loader advise failed; %s", lt_dlerror());
}
}
#endif
}

/* try to load specified module from all entries in COB_LIBRARY_PATH
Expand Down Expand Up @@ -1645,12 +1695,21 @@ cob_init_call (cob_global *lptr, cob_settings* sptr, const int check_mainhandle)

lt_dlinit ();

#if !defined(_WIN32) && !defined(USE_LIBDL)
int error = lt_dladvise_init(&advise);
if (error) {
/* not translated as highly unlikely */
cob_runtime_warning (
"init link loader advise failed; %s", lt_dlerror());
}
#endif

#ifndef COB_BORKED_DLOPEN
/* only set main handle if not started by cobcrun as this
saves a check for exported functions in every CALL
*/
if (check_mainhandle) {
mainhandle = lt_dlopen (NULL);
mainhandle = cob_dlopen (NULL);
} else {
mainhandle = NULL;
}
Expand Down
1 change: 1 addition & 0 deletions libcob/coblocal.h
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ typedef struct __cob_settings {

/* call.c */
int cob_physical_cancel; /* 0 "= "logical only" (default), 1 "also unload", -1 "never unload" */
unsigned int cob_load_global; /* hint for dynamic linker to make symbols available 0=global, 1=local */
unsigned int name_convert;
char *cob_preload_str;
char *cob_library_path;
Expand Down
3 changes: 3 additions & 0 deletions libcob/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,9 @@ static struct config_tbl gc_conf[] = {
{"LOGICAL_CANCELS", "logical_cancels", NULL, NULL, GRP_HIDE, ENV_BOOL | ENV_NOT, SETPOS (cob_physical_cancel)},
{"COB_LIBRARY_PATH", "library_path", NULL, NULL, GRP_CALL, ENV_PATH, SETPOS (cob_library_path)}, /* default value set in cob_init_call() */
{"COB_PRE_LOAD", "pre_load", NULL, NULL, GRP_CALL, ENV_STR, SETPOS (cob_preload_str)},
#ifndef _WIN32
{"COB_LOAD_GLOBAL", "load_global", "0", NULL, GRP_CALL, ENV_BOOL, SETPOS(cob_load_global)},
#endif
{"COB_BELL", "bell", "0", beepopts, GRP_SCREEN, ENV_UINT | ENV_ENUMVAL, SETPOS (cob_beep_value)},
{"COB_DEBUG_LOG", "debug_log", NULL, NULL, GRP_HIDE, ENV_FILE, SETPOS (cob_debug_log)},
{"COB_DISABLE_WARNINGS", "disable_warnings", "0", NULL, GRP_MISC, ENV_BOOL | ENV_NOT, SETPOS (cob_display_warn)},
Expand Down
51 changes: 51 additions & 0 deletions tests/testsuite.src/run_misc.at
Original file line number Diff line number Diff line change
Expand Up @@ -4285,6 +4285,57 @@ AT_CHECK([COB_LOAD_CASE=UPPER ./caller], [0], [], [])

AT_CLEANUP

AT_SETUP([COB_LOAD_GLOBAL=true CALL/CANCEL works])
AT_KEYWORDS([runmisc CALL CANCEL COB_LOAD_GLOBAL])

AT_DATA([CALLEE.cob], [
IDENTIFICATION DIVISION.
PROGRAM-ID. callee.
PROCEDURE DIVISION.
EXIT PROGRAM.
])

AT_DATA([caller.cob], [
IDENTIFICATION DIVISION.
PROGRAM-ID. caller.
PROCEDURE DIVISION.
CALL "callee"
END-CALL.
CANCEL "callee".
STOP RUN.
])

AT_CHECK([$COMPILE_MODULE CALLEE.cob], [0], [], [])
AT_CHECK([$COMPILE caller.cob], [0], [], [])
AT_CHECK([COB_LOAD_GLOBAL=true ./caller], [0], [], [])

AT_CLEANUP

AT_SETUP([COB_LOAD_GLOBAL=false CALL/CANCEL still works])
AT_KEYWORDS([runmisc CALL CANCEL COB_LOAD_GLOBAL])

AT_DATA([CALLEE.cob], [
IDENTIFICATION DIVISION.
PROGRAM-ID. callee.
PROCEDURE DIVISION.
EXIT PROGRAM.
])

AT_DATA([caller.cob], [
IDENTIFICATION DIVISION.
PROGRAM-ID. caller.
PROCEDURE DIVISION.
CALL "callee"
END-CALL.
CANCEL "callee".
STOP RUN.
])

AT_CHECK([$COMPILE_MODULE CALLEE.cob], [0], [], [])
AT_CHECK([$COMPILE caller.cob], [0], [], [])
Comment on lines +4312 to +4335
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After initial testing - please combine those tests into one, possibly named "CALL/CANCEL and COB_LOAD_GLOBAL"

AT_CHECK([COB_LOAD_GLOBAL=false ./caller], [0], [], [])

AT_CLEANUP

AT_SETUP([ALLOCATE / FREE with BASED item (1)])
AT_KEYWORDS([runmisc])
Expand Down