I'm sure Undefined Symbols/References feels like a topic that's been beaten to death here, but I've got a weird issue. See my previous question for some background info.
I was working on a small program, and it was compiling fine, no warnings or errors or anything. Then, I updated CMake, and I got these errors:
Undefined symbols for architecture x86_64:
"std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::basic_string(char const*)", referenced from:
GetName() in n2-af52d6.o
Expression() in n2-af52d6.o
Term() in n2-af52d6.o
GetNum() in n2-af52d6.o
"std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::basic_string()", referenced from:
GetName() in n2-af52d6.o
GetNum() in n2-af52d6.o
"std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::operator+=(char)", referenced from:
GetName() in n2-af52d6.o
GetNum() in n2-af52d6.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I thought this was odd, since these errors seemed to be coming from the standard library itself.
Here's the program, in it's entirety:
//
// ncc.cpp
// Nathanael Epps
//
#include <string>
#include <iostream>
#include <sstream>
typedef std::string string;
static char look = '\0';
static const string Cmt = "## ";
static const string Tab = "\t";
static const string TabTab = Tab + Tab;
static const string TabTabTab = TabTab + Tab;
static void GetChar()
{
look = getchar();
while (look == ' ' || look == '\t')
look = getchar();
}
template <class T>
static string ToString(T x)
{
std::stringstream ss;
ss << x;
return ss.str();
}
static void Error(string mssg)
{
fprintf(stderr, "\n%s", mssg.c_str());
}
static void Abort(string mssg)
{
Error(mssg);
exit(0);
}
static void Expected(string expected)
{
Abort(expected + " expected.\n");
}
static void Match(char x)
{
if (x == look)
GetChar();
else
Expected(string("\"") + x + "\"");
}
static bool IsAlpha(char c)
{
c = toupper(c);
return 'A' <= c && c <= 'Z';
}
static bool IsDigit(char c)
{
return '0' <= c && c <= '9';
}
static bool IsAlNum(char c)
{
return IsAlpha(c) || IsDigit(c);
}
static string GetName()
{
if (!IsAlpha(look))
Expected("Alpha");
std::string name;
while (IsAlNum(look)) {
name += look;
GetChar();
}
return name;
}
static string GetNum()
{
if (!IsDigit(look))
Expected("Integer");
string num;
while (IsDigit(look)) {
num += look;
GetChar();
}
return num;
}
static void EmitLn(string s)
{
printf("\t%s\n", s.c_str());
}
static void EmitLn(char c)
{
printf("\t%c\n", c);
}
static void WriteLn(string s)
{
printf("%s\n", s.c_str());
}
static void Init()
{
GetChar();
}
static void Identifier()
{
auto name = GetName();
if (look == '(')
{
Match('(');
Match(')');
EmitLn("movq" + Tab + "$0, %rax");
EmitLn("callq" + Tab + "_" + name + TabTab + Cmt + "Leaves result in %rax");
}
else
{
EmitLn("movq" + Tab + name + "(%rip), %rax");
}
}
static void Expression();
static void Factor()
{
if (look == '(')
{
Match('(');
Expression();
Match(')');
}
else if (IsAlpha(look))
{
Identifier();
}
else
{
EmitLn("movq" + Tab + "$" + GetNum() + ", %rax");
}
}
static void Multiply()
{
Match('*');
Factor();
EmitLn("popq" + Tab + "%rbx");
EmitLn("imulq" + Tab + "%rbx, %rax");
EmitLn("movq" + Tab + "$0, %rdx");
}
static void Divide()
{
Match('/');
Factor();
// Right now, stack has top number
// %rax has bottom number
// move the bottom number to %rbx
EmitLn("movq" + Tab + "%rax, %rbx");
// move the top number to %rdx:%rax
EmitLn("popq" + Tab + "%rax");
EmitLn("cqto" + TabTabTab + Cmt + "%rax --> %rdx:%rax");
// do division
EmitLn("idivq" + Tab + "%rbx");// + TabTab + Cmt + "leaves result in %rax");
// clear %rdx
EmitLn("movq" + Tab + "$0, %rdx");// + Tab + Cmt + "clear %rdx");
}
static void Term()
{
Factor();
while (look == '*' || look == '/')
{
EmitLn("pushq" + Tab + "%rax");
if (look == '*')
Multiply();
else if (look == '/')
Divide();
else
Expected("Mult/Div Op");
}
}
static void Add()
{
Match('+');
Term();
EmitLn("popq" + Tab + "%rbx");
EmitLn("addq" + Tab + "%rbx, %rax");
}
static void Sub()
{
Match('-');
Term();
EmitLn("popq" + Tab + "%rbx");
EmitLn("subq" + Tab + "%rbx, %rax");
EmitLn("neg " + Tab + "%rax");
}
static bool IsAddop(char c)
{
return c == '+' || c == '-';
}
static void Expression()
{
if (IsAddop(look))
EmitLn("movq" + Tab + "$0, %rax");
else
Term();
while (IsAddop(look))
{
EmitLn("pushq" + Tab + "%rax");
if (look == '+')
Add();
else if (look == '-')
Sub();
else
Expected("Add/Sub Op");
}
}
static void Assignment()
{
auto name = GetName();
Match('=');
Expression();
//EmitLn("movq" + Tab + "%rax, " + name + "(%rip)");
EmitLn("movq" + Tab + "%rax, (variable " + name + ")");
}
static string NewLabel()
{
static unsigned count = 0;
return "L" + ToString(count++);
}
static void PostLabel(string lname)
{
WriteLn(lname + ":");
}
static void Condition()
{
EmitLn("<condition>");
}
static void Other()
{
EmitLn(look);
GetChar();
}
static void Block();
static void DoIf()
{
Match('i');
string l = NewLabel();
Condition();
EmitLn("jne" + Tab + l);
Block();
Match('e');
PostLabel(l);
}
static void Block()
{
while (look != 'e')
{
if (look == 'i')
DoIf();
else
Other();
}
}
static void Program()
{
Block();
if (look != 'e')
Expected("e");
EmitLn(Cmt + "Clean up stack, and such");
EmitLn("retq");
GetChar();
}
int main()
{
Init();
(void) Assignment;
Program();
if (look != '\n')
Expected("Newline");
return EXIT_SUCCESS;
}
However, removing the static
qualifier from the functions makes the errors vanish. (Also, if you hadn't figured it out, the program was a small compiler of sorts.)
What gives?
Edit:
I'd usually link by simply typing
g++ ncc.cpp -std=c++14 -Wall -o ncc
Running g++ --version
gives this:
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/include/c++/4.2.1
Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin17.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
This question is not a duplicate of this question because that answer is about static variables declared at the file scope causing undefined symbols in the files themselves and my question is about static functions declared at the file scope causing undefined references in the standard library.