sfix.cpp 11.2 KB
Newer Older
1
/* #######################################################################
2
# Copyright (c) 2019-2020 COBOLworx Corporation
3 4 5 6 7 8 9 10 11 12 13
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#    * Redistributions of source code must retain the above copyright
#      notice, this list of conditions and the following disclaimer.
#    * Redistributions in binary form must reproduce the above copyright
#      notice, this list of conditions and the following disclaimer in
#      the documentation and/or other materials provided with the
#      distribution.
14
#    * Neither the name of the COBOLworx Corporation nor the names of its
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
#      contributors may be used to endorse or promote products derived
#      from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
############################################################################ */

rdubner's avatar
rdubner committed
31
#include <stdlib.h>
32 33
#include <iostream>
#include <map>
34
#include <set>
rdubner's avatar
rdubner committed
35
#include <sstream>
36 37 38 39 40 41 42 43 44 45 46 47
#include <string.h>

#include "profiler.h"
#include "params.h"
#include "utils.h"

using namespace std;

#define EQ ==
#define AND &&
#define OR ||

48
typedef vector<size_t> VINT;
49

rdubner's avatar
rdubner committed
50 51 52
map<string,int> GetInputFileMappings(const VSTRING &input_file, const string &primary_file)
    {
    PROFILER;
53 54
    map<string,int> retval;

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
    // Prime the pump with the primary COBOL source file name.  We absolutely
    // want this reference to be in there, but it might not actually be in the
    // .s file, as can happen when the primary COBOL source file doesn't
    // actually have any executable instructions in it, but is merely a
    // container for copybook includes.
    //
    // We'll start it with a value of negative one.  When (as is usually the
    // case) there is a .file reference to primary_file in the .s module,
    // the -1 will be replaced, and all will be well.
    //
    // Otherwise, FigureOutReplacements will end up replacing the -1 with
    // 1.  The generated .s file will have primary_file as ".file 1", which
    // will be peachy even if there aren't any actual ".loc 1" references.

    retval[primary_file] = -1;

rdubner's avatar
rdubner committed
71
    for(VSTRING::const_iterator it=input_file.begin();
72
            it!=input_file.end();
rdubner's avatar
rdubner committed
73 74
            it++)
        {
rdubner's avatar
rdubner committed
75
        string s = *it;
76
        s = Trim(s);
rdubner's avatar
rdubner committed
77 78
        if( s.find(".file ") != string::npos)
            {
79
            VSTRING tokens = Split(s,"\t ");
rdubner's avatar
rdubner committed
80 81
            if( tokens.size() >= 3 AND tokens[0] EQ ".file" AND isdigit(tokens[1][0]) )
                {
rdubner's avatar
rdubner committed
82
                int nfile = STOI(tokens[1]);
83 84
                string file = StripQuotes(tokens[2]);
                retval[file] = nfile;
rdubner's avatar
rdubner committed
85
                }
86 87
            }
        }
88
    return retval;
rdubner's avatar
rdubner committed
89
    }
90

rdubner's avatar
rdubner committed
91 92 93
VINT FindFunctionReferences(const VSTRING &input_file)
    {
    PROFILER;
94 95
    set<int>cheating;

96 97
    VINT retval;
    int nline = 0;
rdubner's avatar
rdubner committed
98
    for(VSTRING::const_iterator it=input_file.begin();
99
            it!=input_file.end();
rdubner's avatar
rdubner committed
100 101
            it++)
        {
rdubner's avatar
rdubner committed
102
        string s = *it;
rdubner's avatar
rdubner committed
103 104
        if( s.find(".globl") != string::npos)
            {
105
            VSTRING tokens = Split(s,"\t ");
rdubner's avatar
rdubner committed
106 107
            if( tokens[1] EQ ".globl" )
                {
108
                cheating.insert(nline+4);  // This is where our synthetic .loc will go
rdubner's avatar
rdubner committed
109
                }
110 111
            }

rdubner's avatar
rdubner committed
112 113
        if( s.find("@function") != string::npos)
            {
114
            VSTRING tokens = Split(s,"\t ");
rdubner's avatar
rdubner committed
115 116
            if( tokens[3] EQ "@function" )
                {
117
                cheating.insert(nline+3);  // This is where our synthetic .loc will go
rdubner's avatar
rdubner committed
118
                }
119 120
            }
        nline += 1;
rdubner's avatar
rdubner committed
121 122 123
        }
    for( set<int>::const_iterator it=cheating.begin(); it!=cheating.end(); it++)
        {
124
        retval.push_back(*it);
rdubner's avatar
rdubner committed
125
        }
126

127
    return retval;
rdubner's avatar
rdubner committed
128
    }
129

rdubner's avatar
rdubner committed
130 131 132 133
map<int,int> FigureOutReplacements(const PARAMETERS &params,
                                   const map<string,int> &input_file_mappings)
    {
    PROFILER;
134 135 136 137
    map<int,int> retval;

    int next=2;
    for( map<string,int>::const_iterator it=input_file_mappings.begin();
138
            it != input_file_mappings.end();
rdubner's avatar
rdubner committed
139 140 141 142
            it ++)
        {
        if( it->first EQ params.c_filename )
            {
rdubner's avatar
rdubner committed
143 144 145
            // Skip over the cobc-generated .c file
            retval[it->second] = 0;
            continue;
rdubner's avatar
rdubner committed
146
            }
147
        if( it->first.substr(it->first.length()-2) EQ ".h"
rdubner's avatar
rdubner committed
148 149
                OR it->first.substr(it->first.length()-2) EQ ".c")
            {
rdubner's avatar
rdubner committed
150
            // Skip over any xxxx.h file
151 152
            retval[it->second] = 0;
            continue;
rdubner's avatar
rdubner committed
153 154 155
            }
        if( it->first EQ params.cbl_filename )
            {
rdubner's avatar
rdubner committed
156
            // Force the COBOL source file to be #1
157 158
            retval[it->second] = 1;
            continue;
rdubner's avatar
rdubner committed
159
            }
160
        retval[it->second] = next++;
rdubner's avatar
rdubner committed
161
        }
162 163

    return retval;
rdubner's avatar
rdubner committed
164
    }
165

rdubner's avatar
rdubner committed
166 167 168
VSTRING ReadEntireFile(string filename)
    {
    PROFILER;
169 170
    VSTRING retval;
    FILE *f = fopen(filename.c_str(), "r");
rdubner's avatar
rdubner committed
171 172 173 174 175
    if( !f )
        {
        cerr << "Couldn't the open input file " << filename << endl;
        exit(1);
        }
176 177

    char ach[16384];
rdubner's avatar
rdubner committed
178 179 180 181
    while(fgets(ach,sizeof(ach),f))
        {
        if(strlen(ach) AND ach[strlen(ach)-1] EQ '\n')
            {
182
            ach[strlen(ach)-1] = '\0';
rdubner's avatar
rdubner committed
183
            }
184
        retval.push_back(ach);
rdubner's avatar
rdubner committed
185
        }
186 187
    fclose(f);
    return retval;
rdubner's avatar
rdubner committed
188
    }
189 190


rdubner's avatar
rdubner committed
191 192
int main(int argc, char **argv)
    {
193 194 195
    PROFILER;

    PARAMETERS params = GetParameters(argc, argv);
rdubner's avatar
rdubner committed
196 197
    if(!params.quiet)
        {
198
        cout << "sfix version " << VERSION << endl;
rdubner's avatar
rdubner committed
199
        }
200

201
    VSTRING input_file = ReadEntireFile(params.s_input_filename.WholePath());
202 203 204
    map<string,int>input_file_mappings = GetInputFileMappings(
            input_file
            , params.cbl_filename);
205
    map<int,int>replacements = FigureOutReplacements(params,input_file_mappings);
206
    VINT function_references = FindFunctionReferences(input_file);
207 208 209 210 211 212 213 214

    // Let's hang around here and do the hard work:

    stringstream ss;

    // Start the file off with our filename:
    ss << "\t.file\t\"" << params.cbl_filename << "\"" << endl;

215
    // And then, let's put the modified list of filenames right at the beginning:
216

217 218 219
    for(map<string,int>::const_iterator it=input_file_mappings.begin();
            it != input_file_mappings.end();
            it++
rdubner's avatar
rdubner committed
220 221
       )
        {
222 223
        int oldnum = it->second;
        int newnum = replacements[oldnum];
rdubner's avatar
rdubner committed
224 225
        if( newnum )
            {
226
            ss << "\t.file " << newnum << " \"" << it->first << "\"" << endl;
rdubner's avatar
rdubner committed
227
            }
228
        }
229

230
    int next_function_reference_index = 0;
231 232 233

    // Start at 1, rather than zero; we need to get rid of the original
    // "  .file blah.c " line.
rdubner's avatar
rdubner committed
234 235
    for( size_t i=1; i<input_file.size(); i++ )
        {
236 237
        // We have learned that the GDB trapping can get confused if there isn't
        // a .loc reference in the first few lines after a function declaration.
238

239 240 241
        // When we created the function_references array, we added offset we
        // need to put a .loc into the right place.  So, when i = function_references,
        // it's time to put a .loc in.
242

243
        if(next_function_reference_index < (int)function_references.size()
rdubner's avatar
rdubner committed
244 245
                && i EQ function_references[next_function_reference_index] )
            {
246 247 248
            // We have found a match.
            next_function_reference_index += 1;

249 250 251
            // We are going to look ahead for the next valid .loc, and put
            // that same loc in right here.  The duplications don't seem to
            // cause GDB any problem.
252 253

            size_t ii = i+4;
rdubner's avatar
rdubner committed
254 255
            while(ii < input_file.size() )
                {
256
                VSTRING tokens = Split(input_file[ii]," \t");
rdubner's avatar
rdubner committed
257 258
                if( tokens.size() > 1 AND tokens[1] EQ ".loc" )
                    {
259
                    // We need to replace the old file number with the new one
rdubner's avatar
rdubner committed
260
                    int old_num = STOI(tokens[2]);
261
                    int new_num = replacements[old_num];
rdubner's avatar
rdubner committed
262 263
                    if( new_num != 0 )
                        {
264
                        // Build the replacement line:
rdubner's avatar
rdubner committed
265
                        int new_line = STOI(tokens[3])-1;
266 267 268 269

                        ss << "\t.loc";
                        ss << " " << new_num ;
                        ss << " " << new_line ;
270
                        size_t index = 5;
rdubner's avatar
rdubner committed
271 272
                        while( index < tokens.size() )
                            {
273 274
                            ss << " ";
                            ss << tokens[index++];
rdubner's avatar
rdubner committed
275
                            }
276 277 278
                        ss << endl;

                        break;
rdubner's avatar
rdubner committed
279
                        }
280
                    }
281 282

                ii += 1;
rdubner's avatar
rdubner committed
283
                }
284
            }
285 286

        VSTRING tokens = Split(Trim(input_file[i])," \t");
rdubner's avatar
rdubner committed
287 288 289 290
        if( !tokens.empty() )
            {
            if( tokens[0] EQ ".file" )
                {
291 292
                // We've already taken care of all .file lines
                continue;
rdubner's avatar
rdubner committed
293 294 295
                }
            if( tokens[0] EQ ".loc" )
                {
296
                // We need to replace the old file number with the new one
rdubner's avatar
rdubner committed
297
                int oldnum = STOI(tokens[1]);
298
                int newnum = replacements[oldnum];
rdubner's avatar
rdubner committed
299 300
                if( newnum != 0 )
                    {
301 302 303 304 305 306
                    // Rebuild the .loc line
                    ss << "\t";
                    ss << tokens[0];
                    ss << " ";
                    ss << newnum;
                    size_t index = 2;
rdubner's avatar
rdubner committed
307 308
                    while( index < tokens.size() )
                        {
309 310
                        ss << " ";
                        ss << tokens[index++];
rdubner's avatar
rdubner committed
311
                        }
312
                    ss << endl;
rdubner's avatar
rdubner committed
313
                    }
314
                continue;
rdubner's avatar
rdubner committed
315
                }
316 317
            }

rdubner's avatar
rdubner committed
318 319
        if( input_file[i].find("endbr64") EQ string::npos )
            {
320 321 322 323 324 325 326 327
            // When gcc went from version 8 to version 9, the compiler started
            // inserting endbr64 assembly language instructions.  It's safer, says
            // the documentation.
            //
            // They screw up our efforts to trick GDB into doing our bidding -- GDB loses
            // track of what lines to display after subroutine calls -- so I took the easy
            // way out and just removed the suckers.
            ss << input_file[i] << endl;
rdubner's avatar
rdubner committed
328
            }
329
        }
330

331
    FILE *f = fopen(params.s_output_filename.WholePath().c_str(),"w");
rdubner's avatar
rdubner committed
332 333
    if( !f )
        {
334 335
        cerr << "Couldn't open input file " << params.s_output_filename.WholePath() << endl;
        exit(1);
rdubner's avatar
rdubner committed
336
        }
337 338 339
    fprintf(f,"%s",ss.str().c_str());
    fclose(f);
    return 0;
rdubner's avatar
rdubner committed
340
    }
341