/* Specific flags and argument handling of the Cobol front-end.
Copyright (C) 2021-2025 Free Software Foundation, Inc.
This file is part of GCC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
. */
/* This file implements gcobol's language-specific option handling for the COBOL front
end. It is based on a similar file for the Fortran front end, which
itself was derived from the C front end. Specifically, it defines
lang_specific_driver(cl_decoded_option**, unsigned int*, int*)
for gcobol.
For GNU COBOL, we do the following to the argument list
before passing it to `gcc':
1. Make sure `-lgcobol -lm' is at the end of the list.
2. Make sure each time `-lgcobol' or `-lm' is seen, it forms
part of the series `-lgcobol -lm'.
#1 and #2 are not done if `-nostdlib' or any option that disables
the linking phase is present, or if `-xfoo' is in effect. Note that
a lack of source files or -l options disables linking.
The way this file builds the new argument list was rewritten to be easier to
maintain, and improve the way it decides to add or not add extra arguments,
etc. Several improvements were made in the handling of arguments, primarily
to make it more consistent with `gcc' itself. */
/*
* Number of extra output files that lang_specific_pre_link may generate.
* Unused.
*/
#include "cobol-system.h"
#include "coretypes.h"
#include "opt-suggestions.h"
#include "gcc.h"
#include "opts.h"
#include "tm.h"
#include "intl.h"
int lang_specific_extra_outfiles = 0;
#ifndef MATH_LIBRARY
#define MATH_LIBRARY "m"
#endif
#ifndef DL_LIBRARY
#define DL_LIBRARY "dl"
#endif
#ifndef STDCPP_LIBRARY
#define STDCPP_LIBRARY "stdc++"
#endif
#ifndef COBOL_LIBRARY
#define COBOL_LIBRARY "gcobol"
#endif
/* The original argument list and related info is copied here. */
static const struct cl_decoded_option *original_options;
/* The new argument list will be built here. */
static std::vectornew_opt;
// #define NOISY 1
static void
append_arg(const struct cl_decoded_option arg)
{
#ifdef NOISY
static int counter = 1;
fprintf( stderr,
">>>>>> #%2d Appending %4ld %s\n",
counter++,
arg.opt_index,
arg.orig_option_with_args_text);
#endif
new_opt.push_back(arg);
}
static void
append_option (size_t opt_index, const char *arg, int value)
{
/* Append an option described by OPT_INDEX, ARG and VALUE to the list
being built. */
struct cl_decoded_option decoded;
generate_option(opt_index, arg, value, CL_DRIVER, &decoded);
append_arg(decoded);
}
static void
add_arg_lib(const char *library, bool force_static ATTRIBUTE_UNUSED)
{
/* Append a libgcobol argument to the list being built. If
FORCE_STATIC, ensure the library is linked statically. */
#ifdef HAVE_LD_STATIC_DYNAMIC
if( force_static )
{
append_option (OPT_Wl_, LD_STATIC_OPTION, 1);
}
#endif
append_option (OPT_l, library, 1);
#ifdef HAVE_LD_STATIC_DYNAMIC
if( force_static )
{
append_option (OPT_Wl_, LD_DYNAMIC_OPTION, 1);
}
#endif
}
static void
append_rdynamic()
{
// This is a bit ham-handed, but I was in a hurry.
struct cl_decoded_option decoded = {};
decoded.opt_index = OPT_rdynamic;
decoded.orig_option_with_args_text = "-rdynamic";
decoded.canonical_option[0] = "-rdynamic";
decoded.canonical_option_num_elements = 1;
decoded.value = 1;
append_arg(decoded);
return;
}
static void
append_allow_multiple_definition()
{
append_option (OPT_Wl_, "--allow-multiple-definition", 1);
return;
}
static void
append_fpic()
{
// This is a bit ham-handed, but I was in a hurry.
struct cl_decoded_option decoded = {};
decoded.opt_index = OPT_rdynamic;
decoded.orig_option_with_args_text = "-fPIC";
decoded.canonical_option[0] = "-fPIC";
decoded.canonical_option_num_elements = 1;
decoded.value = 1;
append_arg(decoded);
return;
}
void
lang_specific_driver (struct cl_decoded_option **in_decoded_options,
unsigned int *in_decoded_options_count,
int *in_added_libraries ATTRIBUTE_UNUSED)
{
int argc = (int)*in_decoded_options_count;
struct cl_decoded_option *decoded_options = *in_decoded_options;
// This is the language in effect; it is changed by the OPT_x option.
// Start it out with the default of "none", which is the same as "cobol".
const char *language = "none";
/* The number of input and output files in the incoming arg list. */
int n_infiles = 0;
int n_outfiles = 0;
// The number of input files when the language is "none" or "cobol"
int n_cobol_files = 0;
// saw_OPT_no_main means "don't expect -main"
bool saw_OPT_no_main = false;
// The number of incoming OPT_main and OPT_main_ options seen
int n_mains = 0;
bool saw_OPT_c = false;
bool saw_OPT_shared = false;
bool saw_OPT_pic = false;
bool saw_OPT_PIC = false;
bool verbose = false;
// These flags indicate whether we need various libraries
bool need_libgcobol = true;
bool need_libmath = (MATH_LIBRARY[0] != '\0');
bool need_libdl = (DL_LIBRARY[0] != '\0');
bool need_libstdc = (STDCPP_LIBRARY[0] != '\0');
// bool need_libquadmath = (QUADMATH_LIBRARY[0] != '\0');
bool need_rdynamic = true;
bool need_allow_multiple_definition = true;
// Separate flags for a couple of static libraries
bool static_libgcobol = false;
bool static_in_general = false;
/* WEIRDNESS ALERT:
Sometime around August of 2024, changes were made to the GCC source code
that resulted in an "memory released twice" run-time error when a
std::unordered_map was destructed twice, which usually can't happen. But
it was happening in a gcobol-generated executable. Investigation revealed
that
gocobol ... libgcobol.a -lgcobol
resulted in __gg__alphabet_states being destructed twice.
This should not happen! In normal -shared code, including both libxxx.a
and -lxxx is perfectly legitimate and causes no problem, because the first
one to be encountered provides the globals. But something about the
extremely complex makefile for libgcobol was resulting in the double
destructor problem.
A couple of days of looking for a fix were unsuccessful.
So, I have added logic to this module to prevent the otherwise automatic
insertion of "-lgcobol" when there is an explicit "libgcobol.a" in the
parameters.
*/
int index_libgcobol_a = 0;
bool no_files_error = true;
#ifdef NOISY
int counter=1;
for(int i = 0; i < argc; i++)
{
fprintf( stderr,
">>>>>> #%2d Incoming: %4ld %s\n",
counter++,
decoded_options[i].opt_index,
decoded_options[i].orig_option_with_args_text);
}
fprintf (stderr, "\n");
#endif
// There is always the possibility that no changes to the options
// will be needed:
/* First pass through arglist.
If -nostdlib or a "turn-off-linking" option is anywhere in the
command line, don't do any library-option processing (except
relating to -x). */
for(int i = 1; i < argc; ++i)
{
if (decoded_options[i].errors & CL_ERR_MISSING_ARG)
{
continue;
}
if( strcmp( decoded_options[i].orig_option_with_args_text, "-###") == 0 )
{
no_files_error = false;
}
switch(decoded_options[i].opt_index)
{
case OPT_SPECIAL_input_file:
no_files_error = false;
n_infiles += 1;
if( strcmp(language, "none") == 0
|| strcmp(language, "cobol") == 0 )
{
n_cobol_files += 1;
}
if( strstr(decoded_options[i].orig_option_with_args_text, "libgcobol.a") )
{
// We have been given an explicit libgcobol.a. We need to note that.
index_libgcobol_a = i;
}
continue;
case OPT_shared:
saw_OPT_shared = true;
break;
case OPT_fpic:
saw_OPT_pic = true;
break;
case OPT_fPIC:
saw_OPT_PIC = true;
break;
case OPT_c:
// With this option, no libraries need be loaded
saw_OPT_c = true;
need_libgcobol = false;
need_libmath = false;
need_libdl = false;
need_libstdc = false;
// need_libquadmath = false;
need_rdynamic = false;
break;
case OPT_rdynamic:
need_rdynamic = false;
break;
case OPT_Wl_:
if( strstr(decoded_options[i].orig_option_with_args_text,
"--allow-multiple-definitions") )
{
need_allow_multiple_definition = false;
}
break;
case OPT_nostdlib:
case OPT_nodefaultlibs:
case OPT_r:
case OPT_S:
case OPT_fsyntax_only:
case OPT_E:
// With these options, no libraries need be loaded
need_libgcobol = false;
need_libmath = false;
need_libdl = false;
need_libstdc = false;
// need_libquadmath = false;
need_rdynamic = false;
break;
case OPT_static_libgcobol:
static_libgcobol = true;
need_libgcobol = true;
break;
case OPT_l:
n_infiles += 1;
if(strcmp(decoded_options[i].arg, MATH_LIBRARY) == 0)
{
need_libmath = false;
}
else if(strcmp(decoded_options[i].arg, DL_LIBRARY) == 0)
{
need_libdl = false;
}
else if(strcmp(decoded_options[i].arg, COBOL_LIBRARY) == 0)
{
need_libgcobol = false;
}
else if(strcmp(decoded_options[i].arg, STDCPP_LIBRARY) == 0)
{
need_libstdc = false;
}
break;
case OPT_o:
n_outfiles += 1;
break;
case OPT_nomain:
saw_OPT_no_main = true;
break;
case OPT_main:
case OPT_main_:
n_mains += 1;
break;
case OPT_print_search_dirs:
case OPT_print_file_name_:
case OPT_print_prog_name_:
case OPT_print_multi_lib:
case OPT_print_multi_directory:
case OPT_print_sysroot:
case OPT_print_multi_os_directory:
case OPT_print_multiarch:
case OPT_print_sysroot_headers_suffix:
no_files_error = false;
break;
case OPT_v:
no_files_error = false;
verbose = true;
break;
case OPT_x:
language = decoded_options[i].arg;
break;
case OPT__version:
no_files_error = false;
break;
case OPT__help:
/*
* $ man ./gcobol.1 | ./help.gen
*/
puts( "Options specific to gcobol: " );
puts(
" -main option uses the first PROGRAM of filename as the entry point for\n"
" the main() procedure. \n"
" -no_main \n"
" means that there is no -main, and the main() entry point is\n"
" provided by some other compilation or .o file\n"
" -findicator-column\n"
" describes the location of the Indicator Area in a COBOL file with\n"
" standard 80-column lines. \n"
" -ffixed-form\n"
" Use strict Reference Format in reading the COBOL input: 72-char‐\n"
" acter lines, with a 6-character sequence area, and an indicator\n"
" column. \n"
" -ffree-form\n"
" Force the COBOL input to be interpreted as free format. \n"
" -fmax-errors nerror\n"
" nerror represents the number of error messages produced. \n"
" -fflex-debug, -fyacc-debug\n"
" produce messages useful for compiler development. \n" );
/* Let gcc.cc handle this, as it has a really
cool facility for handling --help and --verbose --help. */
return;
default:
break;
}
}
if( saw_OPT_no_main && n_mains )
{
char ach[] = "\"-no-main\" and \"-main\" are incompatible";
fatal_error(input_location,"%s", ach);
}
bool suppress_main = saw_OPT_no_main
|| (saw_OPT_c && n_mains==0)
|| saw_OPT_shared;
if( no_files_error || ((n_outfiles != 0) && (n_infiles == 0)) )
{
fatal_error(input_location, "no input files");
}
/* If there are no input files, there is no need for any libraries. */
if( n_infiles == 0 )
{
need_libgcobol = false;
need_libmath = false;
need_libdl = false;
need_libstdc = false;
// need_libquadmath = false;
}
/* Second pass through arglist, transforming arguments as appropriate. */
append_arg(decoded_options[0]); /* Start with command name, of course. */
bool first_COBOL_file = true;
bool prior_main = false;
const char *entry_point = NULL;
// Reset the current language, in case it was changed during the first pass
language = "none";
for(int i = 1; i < argc; ++i)
{
if (decoded_options[i].errors & CL_ERR_MISSING_ARG)
{
append_arg(decoded_options[i]);
continue;
}
switch (decoded_options[i].opt_index)
{
case OPT_SPECIAL_input_file:
if( strcmp(language, "none") == 0
|| strcmp(language, "cobol") == 0 )
{
// This is a COBOL source code file
if( !suppress_main && n_mains==0 && first_COBOL_file )
{
// This is a case where the -c option is not present, and there
// were no -main switches. So, we are going to insert a -main switch
// in front of this, the first COBOL file
first_COBOL_file = false;
prior_main = true;
}
if( prior_main )
{
char ach[128];
if( entry_point )
{
strcpy(ach, entry_point);
}
else
{
strcpy(ach, decoded_options[i].arg);
}
append_option(OPT_main_, ach, 1);
prior_main = false;
entry_point = NULL;
}
}
append_arg(decoded_options[i]);
break;
case OPT_main:
if( prior_main )
{
char ach[] = "Multiple \"-main\" without a source file";
fatal_error(input_location, "%s", ach);
}
// This is a simple -main that needs to be followed by a COBOL file
prior_main = true;
break;
case OPT_main_: // Note the trailing underscore
if( prior_main )
{
char ach[] = "Multiple \"-main\" without a source file";
fatal_error(input_location, "%s", ach);
}
// This is -main= that needs to be followed by a COBOL file
entry_point = decoded_options[i].arg;
prior_main = true;
break;
case OPT_nomain:
append_arg(decoded_options[i]);
break;
case OPT_x:
language = decoded_options[i].arg;
append_arg(decoded_options[i]);
break;
case OPT_static_libgcobol:
#if !defined (HAVE_LD_STATIC_DYNAMIC)
// Allow the target to use spec substitution.
append_arg(decoded_options[i]);
#endif
// Else don't pass this one on to cobol1
break;
////#ifdef __x86_64__
//// case OPT_m32:
//// error ( "unrecognized command-line option %<-%s%>; "
//// "(32-bit executables cannot be generated)", "m32");
//// break;
////#endif
case OPT_static:
static_in_general = true;
break;
default:
append_arg(decoded_options[i]);
break;
}
}
/* As described above, we have empirically noticed that when the command line
explicitly specifies libgcobol.a as an input, a following -lgcobol causes
the "on exit" functions of the library to be executed twice. This can
cause trouble for c++ class destructors that expect to be run only once.
So, we rather hamhandedly prevent the inclusion of the default -lgcobol
parameter when a libgcobol.a was found to be present.
Note that if the user *explicitly* specifies both libgcobol.a and
-lgocobol, then he gets what he asked for, and the problem then belongs to
them.
*/
if( index_libgcobol_a )
{
need_libgcobol = false;
}
if( need_libgcobol )
{
add_arg_lib(COBOL_LIBRARY, static_libgcobol);
}
if( need_libmath)
{
add_arg_lib(MATH_LIBRARY, static_in_general);
}
if( need_libdl )
{
add_arg_lib(DL_LIBRARY, static_in_general);
}
if( need_libstdc )
{
add_arg_lib(STDCPP_LIBRARY, static_in_general);
}
if( saw_OPT_shared && !saw_OPT_pic && !saw_OPT_PIC )
{
append_fpic();
}
if( need_rdynamic )
{
append_rdynamic();
}
if( need_allow_multiple_definition && (n_infiles || n_outfiles) )
{
append_allow_multiple_definition();
}
if( prior_main )
{
char ach[] = "\"-main\" without a source file";
fatal_error(input_location, "%s", ach);
}
// We now take the new_opt vector, and turn it into an array of
// cl_decoded_option
size_t new_option_count = new_opt.size();
struct cl_decoded_option *new_options = XNEWVEC (struct cl_decoded_option, new_option_count);
for(size_t i=0; i