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.81Here'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;
}
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:
By making them extern
variables and defining them in only one .cpp
file.
By making them static
variables.
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 = ...;