0

I want to display local (non-external) symbols in a C-program using nm on macOS.

I've read the man nm, and it essentially gives me the following options for displaying symbol information:

nm -m (Mach-O symbols)
nm -g (external symbols)
nm -a (all symbols) 

However, for the main.c program below, I'd expect nm -a to also output foo, since it's defined as a local symbol (internal linkage) by using the static keyword:

nm -a main
0000000100000000 T __mh_execute_header
0000000100000f60 T _bar
0000000100000f30 T _main
                 U _printf
                 U dyld_stub_binder

But, foo is not listed among the symbols. How can I make nm list all symbols (including local ones)?

main.c (compiled as clang main.c -o main):

#include <stdio.h>

int main(int argc, char *argv[]) {
    printf("main");
}

static void foo() {
    printf("foo");
}

extern void bar() {
    printf("baz");
}
Shuzheng
  • 11,288
  • 20
  • 88
  • 186
  • regarding: `int main(int argc, char *argv[]) {` When compiling, always enable the warnings. This statement will cause the compiler to output two warning messages about unused parameters. Also, the compiler will output a warning message about `foo` being defined but not used. (typically, unused items are eliminated by the compiler) – user3629249 Sep 15 '19 at 21:04
  • How do you enable all warnings? `-W`? – Shuzheng Sep 16 '19 at 04:40
  • Usually, `-W` disables all warnings. Suggest reading: [clang compiler options](https://clang.llvm.org/docs/UsersManual.html) especially the chapter: `Command Line Options` Per the manual, the option: `-Weverything` Enables all diagnostics. – user3629249 Sep 16 '19 at 20:29

2 Answers2

2

You're not finding it because it isn't there -- look at the disassembly (objdump -d).

Compilers routinely eliminate unused static functions even at -O0. To keep the foo function you can try making it both used and nontrivial (so it doesn't get inlined).

E.g., for:

#include <stdio.h>

int main(int argc, char *argv[]) {
    printf("main");
}

static void foo() {
    for(int i=0;i<100;i++)
    printf("foo");
}

extern void bar() {
    printf("baz");
    foo();
}

I get:

0000000000000000 T bar
0000000000000000 t foo
0000000000000000 T main
                 U printf

with clang on Linux. You should get similar results on MacOS.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • Thanks, don't you also get `__mh_execute_header` and `dyld_stub_binder` in the output? – Shuzheng Sep 15 '19 at 10:43
  • Also, why are yours symbols not prefixed with `_`, e.g. `_bar`? – Shuzheng Sep 15 '19 at 10:43
  • @Shuzheng That's all MacOS-specific stuff. (Underscore prefixes are common elsewhere too, but it's a rather obsolete convention that started with the original Unix to avoid name clashes with then pre-existing assembly routines. Linux doesn't do that.) – Petr Skocik Sep 15 '19 at 11:07
  • 1
    Thanks for answering my question – Shuzheng Sep 15 '19 at 16:56
1

To add up on @PSkocik excellent answer you can ensure the function will be emitted in the final binary with:

static void foo() __attribute__((used));

static void foo() {
    printf("foo");
}
Kamil.S
  • 5,205
  • 2
  • 22
  • 51