Search code examples
c++makefileg++duplicate-symbol

Makefile duplicate symbol


I want to take a brief moment that I'm aware SO is littered with these questions and I've looked at many:

I'm currently building a disassembler for a school project using C++ and I'm getting blasted with duplicate symbol errors when I run my make file. I fear my issue is a little more complicated than the examples cited above. I apologize for the duplicate question but I can't figure it out after hours of searching and attempting to fix the issue and I appreciate any constructive, complete, and positive feedback anyone has.

  • g++ version 3.4.6 (yes, I know, it's old – school server)
  • make version 3.81

Here's my makefile:

CC=g++
CFLAGS=-Wall -O0 -c

all: dasm

dasm: main.o optab.o record_tokenizer.o regex.o
    $(CC) -o $@ $^

clean:
    rm *.o dasm

It looks like Make is able to convert all files to their respective object files but the linking into the single executable is what's failing:

c++    -c -o main.o main.cpp
c++    -c -o optab.o optab.cpp
c++    -c -o record_tokenizer.o record_tokenizer.cpp
c++    -c -o regex.o regex.cpp
g++ -o dasm main.o optab.o record_tokenizer.o regex.o
duplicate symbol _optab in:
    main.o
    optab.o
duplicate symbol _optab in:
    main.o
    record_tokenizer.o
duplicate symbol _HEADER_RECORD_REGEX in:
    main.o
    record_tokenizer.o
duplicate symbol _END_RECORD_REGEX in:
    main.o
    record_tokenizer.o
duplicate symbol _TEXT_RECORD_REGEX in:
    main.o
    record_tokenizer.o
duplicate symbol _MOD_RECORD_REGEX in:
    main.o
    record_tokenizer.o
ld: 6 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [dasm] Error 1

I'm definitely not a C++ expert nor am I a Makefile guru so any help is appreciated. Here are the relevant files:

optab.h

#ifndef __dasm__optab__
#define __dasm__optab__

#include <map>
#include <string>

std::map<short, const char*> optab;

void init_optab();
const char* get_instruction(int n);

#endif

regex.h

#ifndef __dasm__regex__
#define __dasm__regex__

#include <regex.h>

bool match(const char* pattern, const char* string);

#endif

record_tokenizer.h

#ifndef __dasm__record_tokenizer__
#define __dasm__record_tokenizer__

#include <vector>
#include <cstdlib>
#include <sstream>
#include "regex.h"
#include "optab.h"

const char* HEADER_RECORD_REGEX = "^H[a-zA-Z|[:space:]]{6}[:digit:]{12}";
const char* END_RECORD_REGEX = "^E([:digit:]{6})?";
const char* TEXT_RECORD_REGEX = "^T[:digit:]{6}[0-1][0-E][:digit:]{,60}"; // TODO: improve on this
const char* MOD_RECORD_REGEX = "^M[:digit:]{8}";

struct Record {
    char record_type;
    std::string name;
    std::string address;
    std::vector<std::string> addresses; // used for T records only
};


bool is_valid_record(const char* record, const char* pattern);
const int to_i(std::string n);
const Record tokenize_record(std::string record);

#endif

main.cpp

#include <iostream>
#include "record_tokenizer.h"

using namespace std;

int main(int args, const char* argv[]) {
    if(args > 2) {
        std::cerr << "Usage: dasm sample.obj" << std::endl;
        exit(EXIT_FAILURE);
    }
    tokenize_record("T0000001E^050000^032003^3F^69101791^1BA0131BC0002F200A3B2FF40F102F014F0000");

    return 0;
}

Solution

  • This line in optab.h is a problem:

    std::map<short, const char*> optab;
    

    Every .cpp file that includes this .h file defines optab as a global variable. That would result in the symbol defined multiple times.

    Same errors will result from the lines:

    const char* HEADER_RECORD_REGEX = "^H[a-zA-Z|[:space:]]{6}[:digit:]{12}";
    const char* END_RECORD_REGEX = "^E([:digit:]{6})?";
    const char* TEXT_RECORD_REGEX = "^T[:digit:]{6}[0-1][0-E][:digit:]{,60}";
    const char* MOD_RECORD_REGEX = "^M[:digit:]{8}";
    

    in record_tokenizer.h.

    You can fix them by:

    1. By making them extern variables and defining them in only one .cpp file.

    2. By making them static variables.

    3. By making them const. This would work for the strings but not optab. I imagine you will need to change its value.

      const char* const HEADER_RECORD_REGEX =  ...;
      const char* const END_RECORD_REGEX = ...;
      const char* const TEXT_RECORD_REGEX = ...;
      const char* const MOD_RECORD_REGEX = ...;