mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 03:47:02 -05:00
572 lines
16 KiB
C++
572 lines
16 KiB
C++
/* Specific flags and argument handling of the Cobol front-end.
|
||
Copyright (C) 2021-2026 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
|
||
<http://www.gnu.org/licenses/>. */
|
||
|
||
/* 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 DL_LIBRARY
|
||
#define DL_LIBRARY "dl"
|
||
#endif
|
||
|
||
#ifndef STDCPP_LIBRARY
|
||
#define STDCPP_LIBRARY "stdc++"
|
||
#endif
|
||
|
||
#ifndef COBOL_LIBRARY
|
||
#define COBOL_LIBRARY "gcobol"
|
||
#endif
|
||
|
||
#define SPEC_FILE "libgcobol.spec"
|
||
|
||
/* 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::vector<cl_decoded_option>new_opt;
|
||
|
||
static bool need_libgcobol = true;
|
||
|
||
// #define NOISY 1
|
||
|
||
static void
|
||
append_arg(const 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
|
||
}
|
||
|
||
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;
|
||
|
||
// 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 verbose = false;
|
||
|
||
// These flags indicate whether we need various libraries
|
||
|
||
bool need_libdl = (DL_LIBRARY[0] != '\0');
|
||
bool need_libstdc = (STDCPP_LIBRARY[0] != '\0');
|
||
|
||
// 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( 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_c:
|
||
// Note -c specially.
|
||
saw_OPT_c = true;
|
||
// FALLTHROUGH
|
||
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_libdl = false;
|
||
need_libstdc = 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, 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_libdl = false;
|
||
need_libstdc = 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 )
|
||
{
|
||
const char *ach;
|
||
if (entry_point)
|
||
ach = entry_point;
|
||
else
|
||
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=<arg> 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:
|
||
#if defined (HAVE_LD_STATIC_DYNAMIC)
|
||
append_arg(decoded_options[i]);
|
||
static_in_general = true;
|
||
#endif
|
||
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( static_in_general )
|
||
{
|
||
// These two options interfere with each other.
|
||
static_libgcobol = false;
|
||
}
|
||
|
||
if( need_libgcobol )
|
||
{
|
||
add_arg_lib(COBOL_LIBRARY, static_libgcobol);
|
||
}
|
||
if( need_libdl )
|
||
{
|
||
add_arg_lib(DL_LIBRARY, false);
|
||
}
|
||
if( need_libstdc )
|
||
{
|
||
add_arg_lib(STDCPP_LIBRARY, false);
|
||
}
|
||
|
||
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<new_option_count; i++)
|
||
{
|
||
new_options[i] = new_opt[i];
|
||
}
|
||
|
||
#ifdef NOISY
|
||
verbose = true;
|
||
#endif
|
||
if( verbose && new_options != original_options ) // cppcheck-suppress knownConditionTrueFalse
|
||
{
|
||
fprintf(stderr, _("Driving: (" HOST_SIZE_T_PRINT_DEC ")\n"),
|
||
(fmt_size_t)new_option_count);
|
||
for(size_t i=0; i<new_option_count; i++)
|
||
{
|
||
fprintf(stderr,
|
||
" [%2" GCC_PRISZ "d] %4" GCC_PRISZ "d %s\n",
|
||
(fmt_size_t)i,
|
||
(fmt_size_t)new_options[i].opt_index,
|
||
new_options[i].orig_option_with_args_text);
|
||
}
|
||
fprintf (stderr, "\n");
|
||
}
|
||
|
||
*in_decoded_options_count = new_option_count;
|
||
*in_decoded_options = new_options;
|
||
}
|
||
|
||
/* Called before linking. Returns 0 on success and -1 on failure. */
|
||
int
|
||
lang_specific_pre_link (void)
|
||
{
|
||
if (need_libgcobol)
|
||
do_spec ("%:include(libgcobol.spec)");
|
||
|
||
return 0;
|
||
}
|