Undefined symbols for architecture arm64
Ryan Carsten Schmidt
ryandesign at macports.org
Sat Mar 15 03:56:36 UTC 2025
I see you already received a reply while I was writing my response but I may as well send it in case it explains anything in a way that is more understandable.
On Mar 14, 2025, at 17:14, Kenneth Wolcott wrote:
> I was compiling a C example of a Rosetta Code task recently..
>
> https://rosettacode.org/wiki/Vampire_number#C
>
> Here's my clang error(s):
> clang -lm -o ./vampire_number ./vampire_number.c
> Undefined symbols for architecture arm64:
> "_dtally", referenced from:
> _fangs in vampire_number-2cd7e6.o
> _fangs in vampire_number-2cd7e6.o
> _fangs in vampire_number-2cd7e6.o
> "_max", referenced from:
> _fangs in vampire_number-2cd7e6.o
> "_min", referenced from:
> _fangs in vampire_number-2cd7e6.o
> "_ndigits", referenced from:
> _fangs in vampire_number-2cd7e6.o
> ld: symbol(s) not found for architecture arm64
> clang: error: linker command failed with exit code 1 (use -v to see invocation)
All of the undefined symbols are for functions that use the "inline" keyword:
> inline xint max(xint a, xint b) { return a > b ? a : b; }
> inline xint min(xint a, xint b) { return a < b ? a : b; }
> inline int ndigits(xint x)
> {
> int n = 0;
> while (x) n++, x /= 10;
> return n;
> }
>
> inline xint dtally(xint x)
> {
> xint t = 0;
> while (x) t += 1<<((x%10) * 6), x /= 10;
>
> return t;
> }
The "inline" keyword was introduced to the C language standard in c99. In c99 and later, it tells the compiler what code to use *if* it chooses to inline the function, but it *does not* tell the compiler *to* inline the function. Some compilers treat it as a *suggestion* to inline the function but they might ignore that suggestion. If the compiler chooses *not* to inline the function, then it will look for a non-inline definition of the function. Since this code doesn't contain a non-inline definition of the function, the result is an undefined symbol error.
One solution is to remove the "inline" keyword, e.g.:
xint max(xint a, xint b) { return a > b ? a : b; }
xint min(xint a, xint b) { return a < b ? a : b; }
etc. The compiler can still choose to inline the function.
Another solution is to keep the "inline" keyword (because you want to make the suggestion) and to tell the compiler that you want the same code to be used if the function is not inlined. There are two ways to do that: one is to precede "inline" with the keyword "extern" (meaning you want the function to be reachable from outside this translation unit (i.e. outside of this source code file)) and the other is to precede it with the keyword "static" (meaning you will only be using this function within this translation unit / source code file). There are some caveats to both "extern" and "static" when a project consists of more than one translation unit, as discussed more in the wikipedia article linked below. Since this project is only a single source code file, "static" is probably the best choice, e.g.:
static inline xint max(xint a, xint b) { return a > b ? a : b; }
static inline xint min(xint a, xint b) { return a < b ? a : b; }
etc. It would also be reasonable to precede all of the other functions other than main with "static" since they are not being called from other translation units either.
Another solution that doesn't require any changes to the code is to compile in gnu89 mode. Although "inline" was added to the standard in c99, it appeared as an extension in gcc compilers in gnu89 mode, which was the default mode along time ago, but per the wikipedia article below "inline" behaves differently in gnu89 than it does in c99 and later. In gnu89, "inline" is equivalent to c99's "extern inline". You can downgrade to this mode by adding the compiler flag "-std=gnu89", e.g.:
clang -std=gnu89 -o vampire_number vampire_number.c
Another "solution" that doesn't require any changes to the code but which is fragile is to tell the compiler to inline functions, for example by using an optimization flag:
clang -O3 -o vampire_number vampire_number.c
This (by itself) is fragile because if the compiler still chooses not to inline the function, you'll be back to an undefined symbol error. Different compilers, or different versions of a compiler, might make different choices about whether to inline functions at a particular optimization level. Adding an optimization flag in addition to one of the other fixes suggested above is fine.
There's a lot of good information about inline functions here:
https://en.wikipedia.org/wiki/Inline_function
More information about the macports-users
mailing list