2

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.

Dovahkiin
  • 946
  • 14
  • 25
  • Marking those functions static in a plan C context makes them exclusively visible within the same translation unit. Much like an _anonymous namespace_ does in c++. – πάντα ῥεῖ Jul 09 '18 at 01:18
  • I know what the static keyword does, I just don't get how that leads to undefined symbols when non-static functions won't @πάνταῥεῖ – Dovahkiin Jul 11 '18 at 10:53
  • Also, the question pointed to doesn't answer my question :/ @πάνταῥεῖ – Dovahkiin Jul 11 '18 at 10:54
  • No, because that's about static variables affecting visibility, and this question is about static functions causing undefined symbols in the standard library. @πάνταῥεῖ – Dovahkiin Jul 11 '18 at 11:00
  • Please edit your question to provide a [MCVE] including how you compile each source file and you're linking everything together. I am pretty sure your problem is covered by the duplicate. – πάντα ῥεῖ Jul 11 '18 at 11:01
  • That question is about static variables declared at the file scope causing undefined symbols in the object files themselves, my question is about static functions causing undefined symbols in the standard library. @πάνταῥεῖ – Dovahkiin Jul 11 '18 at 11:37
  • I don't see the problem when compiling and linking your code with gcc-7 or clang 4.2.1 on Ubuntu. – Eelke Jul 11 '18 at 16:29
  • Perhaps this is MacOS being weird again - if so, people should only attempt to reproduce it there. – o11c Jul 11 '18 at 16:47
  • Does `g++ ncc.cpp -std=c++14 -Wall -o ncc` work well? does it only fail from CMake? – Daniel Jul 13 '18 at 18:59
  • I should've been more clear, using CMake generated makefiles and typing the command manually both fail. – Dovahkiin Jul 13 '18 at 19:09

1 Answers1

1

There are two implementations of the C++ standard library in Mac, can you try adding this flag?

-stdlib=libstdc++

If that doesn't work, then the other way around:

-lstdc++

Credits to this other post.

Daniel
  • 21,933
  • 14
  • 72
  • 101