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

Conversation

florianschmidt1994
Copy link

@florianschmidt1994 florianschmidt1994 commented Jan 8, 2025

This PR adds a config option called COB_LOAD_GLOBAL which allows the flags of dlopen used in call.c to be changed between RTLD_GLOBAL and RTLD_LOCAL (see https://linux.die.net/man/3/dlopen)

Motivation

This flag is motivated by an ambition to be able to write a multi-threaded c program, that then loads libcob with dlmopen in one namespace per thread, effectively allowing us to call the same libcob / Cobol module from multiple threads in a "thread-safe" way. The image below shows the a very simplified intended call sequence

CanvasAsImage

Because there is a "bug" in dlopen that stops us from calling dlopen("module", RTLD_GLOBAL), this PR introduces an option to be able to change this flag to RTLD_LOCAL at runtime.

As at glibc 2.24, specifying the RTLD_GLOBAL flag when calling dlmopen() generates an error. Furthermore, specifying RTLD_GLOBAL when calling dlopen() results in a program crash (SIGSEGV) if the call is made from any object loaded in a namespace other than the initial namespace.

Docs

# 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
#          Example:  load_global true

Implementation

  • Change lt_dlopen from a macro definition to an actual function, which internally calls dlopen with the flags based on the configuration
  • Add cob_load_global / COB_LOAD_GLOBAL as a configuration
  • Add documentation to runtime.cfg about COB_LOAD_GLOBAL

@GitMensch
Copy link
Collaborator

Apart from the other PR's review to that branch, the change here makes the current scenario even more confusing.
I'd like to fix this in this PR (or up-front upstream).

After the changes:

  • instead of calling lt_dlopen we call a new static function cob_dlopen
  • this has the current code of this PR in for the case of dlopen usage
  • for the WIN32 variant it uses what is currently used there
  • for the libtool variant it uses lt_dlopenadvise() with an advise matching the runtime setting

@florianschmidt1994 can you please inspect the libtool variant (to test this you can simply undefine USE_LIBDL in config.h)?
I think I'll work on the cob_dlopen stuff late-evening.

@florianschmidt1994
Copy link
Author

florianschmidt1994 commented Jan 8, 2025

Yup, I'll check it out. Basically something like this, right?

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

	return dlopen(filename, flags);
	# else
	// TODO: initialise lt_dladvise
	lt_dlopenadvise(filename, /*advise here*/);
}

@florianschmidt1994
Copy link
Author

@GitMensch I've tried out your suggestion in the latest commit and temporarily defined lt_dlopen as cob_dlopen to try it out. I've also added some indentation to the macros, does that conform with your style or should I remove them again?

Copy link
Collaborator

@GitMensch GitMensch left a comment

Choose a reason for hiding this comment

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

nice update, we may be able to push that soon

Note: we still need the testcase addition - does not need to be something that benefit from this as this is hard to make portable - just do a simple CALL and CANCEL. We should have an easy one for COB_LOAD_CASE

ChangeLog Outdated Show resolved Hide resolved
ChangeLog Outdated Show resolved Hide resolved
libcob/call.c Outdated Show resolved Hide resolved
libcob/coblocal.h Outdated Show resolved Hide resolved
libcob/call.c Outdated
}

void* handle = lt_dlopenadvise(filename, advise);
lt_dladvise_destroy(&advise); // Clean up advise object
Copy link
Collaborator

Choose a reason for hiding this comment

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

please make the advise static and clean it up in call.c's cleanup function (depending on its preprocessor macro, of course), you can also init it in the init function - just set the advise flag here (the setting for both dlopen and lt_dlopen will be moved out to a config function later - you're welcome to add it (see screenio.c, this may be even explained in the video :-) and call it from the config side if this var or COB_LIBRARY_PATH was adjusted

config/runtime.cfg Show resolved Hide resolved
libcob/call.c Outdated Show resolved Hide resolved
@florianschmidt1994 florianschmidt1994 force-pushed the feature/add-dlopen-config-flag branch from f6aa350 to ab26e16 Compare January 9, 2025 09:31
Comment on lines +4312 to +4335
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], [], [])
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"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants