这里的nm命令指的是GNU Linux版本,Ubuntu20.04,是name的缩写。
nm是一个命令行工具,用来列出object文件、库文件或可执行文件中的符号列表(name list, the symbol table of nlist structures)。
nm命令的输出结果为三列,symbol virtual address,symbol type和symbol name,即符号虚拟地址,类型和名字。
显示内容
举例:
testnm.c
#include <stdio.h>
int value;
static void func()
{
printf("value is %d.\n", value);
}
int main()
{
value = 1;
func();
return 0;
}
$ gcc -c testnm.c
$ nm testnm.o
0000000000000000 t func
U _GLOBAL_OFFSET_TABLE_
0000000000000024 T main
U printf
0000000000000004 C value
用一个字符表示类型,如果是小写字符,则是本地符号(local),如果是大写,则是外部符号(external)。类型符号表示的意义如下。
类型字符 | 含义 | Decription |
A | 全局不可变符号,比如原文件名。 | Global absolute symbol. |
a | 本地不可变符号 | Local absolute symbol. |
B | 全局bss段符号(未初始化的全局或static变量) | Global bss symbol. |
b | 本地bss段符号 | Local bss symbol. |
D | 全局变量(已初始化的全局或static变量)符号 | Global data symbol. |
d | 本地变量符号 | Local data symbol. |
T | 全局符号,比如全局函数名 | Global text symbol. |
t | 本地符号,比如文件内static函数名 | Local text symbol. |
U | 未定义符号 | Undefined symbol. |
。。。。。。 |
用途
这个命令看起来很酷,但只有合理的使用这个命令,这个命令才有存在的意义。那什么时候使用呢?
假设你有一个由许多不同object文件组成的可执行文件。现在假设在编译代码的时候,链接器给出了关于一个未解决的符号'temp'的错误。现在,如果代码规模太大,包括很多头文件,要找到代码中的符号'temp'的位置将成为一场噩梦。在这里,这个工具就起到了拯救作用。通过一些选项的配置,这个工具可以找到该符号所在的文件。
语法:
$ nm [options(s)] [file(s)]
列出参数[file(s)]的文件中的符号,可以给出多个文件,如果没有指定文件名,则默认文件是a.out。
各个选项的使用:(同一个选项存在短格式和长格式形式则一并列出)
1,列出object文件里的全局(extern)符号,包括函数和变量:
nm -g <path/to/file.o>
nm --extern-only <path/to/file.o>
举例:
$ nm -g testnm.o
U _GLOBAL_OFFSET_TABLE_
0000000000000024 T main
U printf
0000000000000004 C value
$ nm testnm.o
0000000000000000 t func
U _GLOBAL_OFFSET_TABLE_
0000000000000024 T main
U printf
0000000000000004 C value
2,列出obj文件内的未定义符号:
nm -u <path/to/file.o>
nm --undefined-only <path/to/file.o>
举例:
$ nm -u testnm.o
U _GLOBAL_OFFSET_TABLE_
U printf
3,列出obj文件中所有的符号,包括debug所使用的符号,否则默认情况下不显示调试用的符号。
nm -a <path/to/file.o>
nm --debug-syms <path/to/file.o>
举例:
$ nm -a testnm.o
0000000000000000 b .bss
0000000000000000 n .comment
0000000000000000 d .data
0000000000000000 r .eh_frame
0000000000000000 t func
U _GLOBAL_OFFSET_TABLE_
0000000000000024 T main
0000000000000000 r .note.gnu.property
0000000000000000 n .note.GNU-stack
U printf
0000000000000000 r .rodata
0000000000000000 a testnm.c
0000000000000000 t .text
0000000000000004 C value
4,列出符号表示,前面显示obj文件名。
$ nm -A <path/to/file.o>
$ nm --print-file-name <path/to/file.o>
这个选项在显示多个多个obj文件的符号时会需要。
举例:
$ nm -A testnm.o
testnm.o:0000000000000000 t func
testnm.o: U _GLOBAL_OFFSET_TABLE_
testnm.o:0000000000000024 T main
testnm.o: U printf
testnm.o:0000000000000004 C value
5,恢复C++符号的名称改变,使其具有更好可读性:
nm -C <path/to/file.o>
nm --demangle <path/to/file.o>
nm --demangle[=STYLE] <path/to/file.o>
将编译器级的low-level的符号名,转换成用户可理解的名字类型。可以指定obj文件所使用的名称改编风格,有auto(默认), gnu, lucid, arm, hp, edg, gnu-v3, java 和gnat。
注意,这个参数对C语言编译出来的obj文件是无效的,上面的C语言的例子,使用-C选项,输出内容并没有变化。
举例:
testnm.cpp
#include <iostream>
static int value;
static void func(int a, int b)
{
if(a > b){
std::cout<<"the value is " << value << std::endl;
}
}
int main()
{
value = 3;
func(2, 1);
return 0;
}
$ g++ -c testnm.cpp
$ nm testnm.o
U __cxa_atexit
U __dso_handle
U _GLOBAL_OFFSET_TABLE_
00000000000000cd t _GLOBAL__sub_I_main
0000000000000058 T main
0000000000000080 t _Z41__static_initialization_and_destruction_0ii
0000000000000000 t _ZL4funcii
0000000000000004 b _ZL5value
U _ZNSolsEi
U _ZNSolsEPFRSoS_E
U _ZNSt8ios_base4InitC1Ev
U _ZNSt8ios_base4InitD1Ev
U _ZSt4cout
U _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
0000000000000000 r _ZStL19piecewise_construct
0000000000000000 b _ZStL8__ioinit
U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
$ nm --demangle testnm.o
U __cxa_atexit
U __dso_handle
U _GLOBAL_OFFSET_TABLE_
00000000000000cd t _GLOBAL__sub_I_main
0000000000000058 T main
0000000000000080 t __static_initialization_and_destruction_0(int, int)
0000000000000000 t func(int, int)
0000000000000004 b value
U std::ostream::operator<<(int)
U std::ostream::operator<<(std::ostream& (*)(std::ostream&))
U std::ios_base::Init::Init()
U std::ios_base::Init::~Init()
U std::cout
U std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
0000000000000000 r std::piecewise_construct
0000000000000000 b std::__ioinit
U std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
可以看到上面的例子,除了前面的下划线去掉了,C++的函数名字也变得可读性更好。不同的编译器使用不同的名称改变风格(mangling styles),可以使用上面提到的改变风格的选项来选择一种适合当前编译器。
6,显示动态链接的符号。
nm -D <path/to/file.o>
nm --dynamic <path/to/file.o>
举例:
$ gcc -o testnm testnm.c
$ ldd testnm
linux-vdso.so.1 (0x00007ffd7dff0000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff2c819a000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff2c83a3000)
$ nm -D testnm
w __cxa_finalize
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U __libc_start_main
U printf
$ g++ -o testnmplus testnm.cpp
$ ldd testnmplus
linux-vdso.so.1 (0x00007ffd82bd0000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007ff7716a4000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff7714b2000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007ff771363000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff77189d000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007ff771348000)
$ nm -D testnmplus
U __cxa_atexit
w __cxa_finalize
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U __libc_start_main
U _ZNSolsEi
U _ZNSolsEPFRSoS_E
U _ZNSt8ios_base4InitC1Ev
U _ZNSt8ios_base4InitD1Ev
0000000000004040 B _ZSt4cout
U _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
这里编出来的obj文件,没有动态链接符号,而编出的可执行文件里才有。
这些动态链接的符号,只有在运行时(run time)才会解析。
7,更改输出内容的格式。
nm -f <FORMAT> <path/to/file.o>
nm --format=FORMAT <path/to/file.o>
可选的输出格式为bsd,sysv或posix,默认是bsd模式。
举例:
$ nm -f posix testnm.o
func t 0 24
_GLOBAL_OFFSET_TABLE_ U
main T 24 23
printf U
value C 4 4
$ nm --format posix testnm.o
func t 0 24
_GLOBAL_OFFSET_TABLE_ U
main T 24 23
printf U
value C 4 4
$ nm --format=posix testnm.o
func t 0 24
_GLOBAL_OFFSET_TABLE_ U
main T 24 23
printf U
value C 4 4
$ nm --format=sysv testnm.o
Symbols from testnm.o:
Name Value Class Type Size Line Section
func |0000000000000000| t | FUNC|0000000000000024| |.text
_GLOBAL_OFFSET_TABLE_| | U | NOTYPE| | |*UND*
main |0000000000000024| T | FUNC|0000000000000023| |.text
printf | | U | NOTYPE| | |*UND*
value |0000000000000004| C | OBJECT|0000000000000004| |*COM*
8,使用排序选项。
默认情况下,符号列表根据字母顺序进行排序,但你可以选择根据地址进行排序。
nm -n <path/to/file.o>
nm --numeric-sort <path/to/file.o>
举例:
$ nm -n testnm.o
U _GLOBAL_OFFSET_TABLE_
U printf
0000000000000000 t func
0000000000000004 C value
0000000000000024 T main
默认使用的是字母排序,也可以不使用排序功能。
nm -p <path/to/file.o>
nm --no-sort <path/to/file.o>
举例:
$ nm -p testnm.o
0000000000000000 t func
0000000000000004 C value
U _GLOBAL_OFFSET_TABLE_
U printf
0000000000000024 T main
$ nm testnm.o
0000000000000000 t func
U _GLOBAL_OFFSET_TABLE_
0000000000000024 T main
U printf
0000000000000004 C value
还可以按照符号占用空间的大小排序。
nm --size-sort <path/to/file.o>
如果想使用相反的顺序,使用-r选项。
nm -r <path/to/file.o>
nm --reverse-sort <path/to/file.o>
举例:
$ nm -n testnm.o
U _GLOBAL_OFFSET_TABLE_
U printf
0000000000000000 t func
0000000000000004 C value
0000000000000024 T main
$ nm -rn testnm.o
0000000000000024 T main
0000000000000004 C value
0000000000000000 t func
U printf
U _GLOBAL_OFFSET_TABLE_
9,只显示未定义的符号。
nm -u <path/to/file.o>
nm --undefined-only <path/to/file.o>
举例:
$ nm -u testnm.o
U _GLOBAL_OFFSET_TABLE_
U printf
如果在一个可执行文件中发现未定义的符号,可能是这个符号存在于动态链接库中(shared libraries)。
10,只显示已定义的符号。
nm --defined-only <path/to/file.o>
举例:
$ nm --defined-only testnm.o
0000000000000000 t func
0000000000000024 T main
0000000000000004 C value
11,显示符号占用空间的大小
nm -S <path/to/file.o>
nm --print-size <path/to/file.o>
举例:
$ nm testnm.o
0000000000000000 t func
U _GLOBAL_OFFSET_TABLE_
0000000000000024 T main
U printf
0000000000000004 C value
$ nm -S testnm.o
0000000000000000 0000000000000024 t func
U _GLOBAL_OFFSET_TABLE_
0000000000000024 0000000000000023 T main
U printf
0000000000000004 0000000000000004 C value
12,使用文件来指定选项。
nm命令还有一个功能,可以从一个文件里读取命令行的选项。
举例:
$ cat file
-S testnm.o
$ nm @file
0000000000000000 0000000000000024 t func
U _GLOBAL_OFFSET_TABLE_
0000000000000024 0000000000000023 T main
U printf
0000000000000004 0000000000000004 C value
$ nm -S testnm.o
0000000000000000 0000000000000024 t func
U _GLOBAL_OFFSET_TABLE_
0000000000000024 0000000000000023 T main
U printf
0000000000000004 0000000000000004 C value
13,使用案例
在当前文件夹的obj文件中,找寻包含func字符串的符号名称。
$ nm -A ./*.o | grep func
./hello2.o:0000000000000000 T func_1
./hello3.o:0000000000000000 T func_2
./hello4.o:0000000000000000 T func_3
./main.o: U func
./reloc.o: U func
./reloc.o:0000000000000000 T func1
./test1.o:0000000000000000 T func
./test.o: U func
上面介绍的各个功能选项可以组合使用来满足自己的需求。比如:
$ nm -g -S testnm.o
$ nm -gS testnm.o
U _GLOBAL_OFFSET_TABLE_
0000000000000024 0000000000000023 T main
U printf
0000000000000004 0000000000000004 C value
另外,输出内容里的符号地址,其基数(radix)是可变的,默认是16进制(hexadecimal),使用的参数是:
-t, --radix=RADIX // Use RADIX for printing symbol values
备注1:
可以在linux源码中查询到符号列表条目的类型定义nlist结构体:
struct nlist {
union {
char *n_name;
struct nlist *n_next;
long n_strx;
} n_un;
unsigned char n_type;
char n_other;
short n_desc;
unsigned long n_value;
};
备注2:
nm命令的简单help信息:
$ nm --help
Usage: nm [option(s)] [file(s)]
List symbols in [file(s)] (a.out by default).
The options are:
-a, --debug-syms Display debugger-only symbols
-A, --print-file-name Print name of the input file before every symbol
-B Same as --format=bsd
-C, --demangle[=STYLE] Decode low-level symbol names into user-level names
The STYLE, if specified, can be `auto' (the default),
`gnu', `lucid', `arm', `hp', `edg', `gnu-v3', `java'
or `gnat'
--no-demangle Do not demangle low-level symbol names
--recurse-limit Enable a demangling recursion limit. This is the default.
--no-recurse-limit Disable a demangling recursion limit.
-D, --dynamic Display dynamic symbols instead of normal symbols
--defined-only Display only defined symbols
-e (ignored)
-f, --format=FORMAT Use the output format FORMAT. FORMAT can be `bsd',
`sysv' or `posix'. The default is `bsd'
-g, --extern-only Display only external symbols
-l, --line-numbers Use debugging information to find a filename and
line number for each symbol
-n, --numeric-sort Sort symbols numerically by address
-o Same as -A
-p, --no-sort Do not sort the symbols
-P, --portability Same as --format=posix
-r, --reverse-sort Reverse the sense of the sort
--plugin NAME Load the specified plugin
-S, --print-size Print size of defined symbols
-s, --print-armap Include index for symbols from archive members
--size-sort Sort symbols by size
--special-syms Include special symbols in the output
--synthetic Display synthetic symbols as well
-t, --radix=RADIX Use RADIX for printing symbol values
--target=BFDNAME Specify the target object format as BFDNAME
-u, --undefined-only Display only undefined symbols
--with-symbol-versions Display version strings after symbol names
-X 32_64 (ignored)
@FILE Read options from FILE
-h, --help Display this information
-V, --version Display this program's version number
nm: supported targets: elf64-x86-64 elf32-i386 elf32-iamcu elf32-x86-64 pei-i386 pei-x86-64 elf64-l1om elf64-k1om elf64-little elf64-big elf32-little elf32-big pe-x86-64 pe-bigobj-x86-64 pe-i386 srec symbolsrec verilog tekhex binary ihex plugin
Report bugs to <http://www.sourceware.org/bugzilla/>.
备注3:
Ubuntu中的manual中关于符号意义的解释:
• The symbol type. At least the following types are used; others are, as well, depending on the object file format. If lowercase, the symbol is usually local; if uppercase, the symbol is
global (external). There are however a few lowercase symbols that are shown for special global symbols ("u", "v" and "w").
"A" The symbol's value is absolute, and will not be changed by further linking.
"B"
"b" The symbol is in the BSS data section. This section typically contains zero-initialized or uninitialized data, although the exact behavior is system dependent.
"C" The symbol is common. Common symbols are uninitialized data. When linking, multiple common symbols may appear with the same name. If the symbol is defined anywhere, the common
symbols are treated as undefined references.
"D"
"d" The symbol is in the initialized data section.
"G"
"g" The symbol is in an initialized data section for small objects. Some object file formats permit more efficient access to small data objects, such as a global int variable as opposed
to a large global array.
"i" For PE format files this indicates that the symbol is in a section specific to the implementation of DLLs. For ELF format files this indicates that the symbol is an indirect
function. This is a GNU extension to the standard set of ELF symbol types. It indicates a symbol which if referenced by a relocation does not evaluate to its address, but instead
must be invoked at runtime. The runtime execution will then return the value to be used in the relocation.
"I" The symbol is an indirect reference to another symbol.
"N" The symbol is a debugging symbol.
"n" The symbol is in the read-only data section.
"p" The symbol is in a stack unwind section.
"R"
"r" The symbol is in a read only data section.
"S"
"s" The symbol is in an uninitialized or zero-initialized data section for small objects.
"T"
"t" The symbol is in the text (code) section.
"U" The symbol is undefined.
"u" The symbol is a unique global symbol. This is a GNU extension to the standard set of ELF symbol bindings. For such a symbol the dynamic linker will make sure that in the entire
process there is just one symbol with this name and type in use.
"V"
"v" The symbol is a weak object. When a weak defined symbol is linked with a normal defined symbol, the normal defined symbol is used with no error. When a weak undefined symbol is
linked and the symbol is not defined, the value of the weak symbol becomes zero with no error. On some systems, uppercase indicates that a default value has been specified.
"W"
"w" The symbol is a weak symbol that has not been specifically tagged as a weak object symbol. When a weak defined symbol is linked with a normal defined symbol, the normal defined
symbol is used with no error. When a weak undefined symbol is linked and the symbol is not defined, the value of the symbol is determined in a system-specific manner without error.
On some systems, uppercase indicates that a default value has been specified.
"-" The symbol is a stabs symbol in an a.out object file. In this case, the next values printed are the stabs other field, the stabs desc field, and the stab type. Stabs symbols are
used to hold debugging information.
"?" The symbol type is unknown, or object file format specific.
备注4:
在Ubuntu里,使用PC平台的nm命令,也可以解析cross toolchain编译的程序。
$ cat testuart.c
include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main()
{
int fd;
fd = open("/dev/ttyS3", O_RDWR | O_NOCTTY | O_CLOEXEC);
printf("fd is %d.\n", fd);
return 0;
}
使用工具链进行编译:
X86:
$ gcc -o testuartgcc testuart.c
ARM:
$ /opt/poky/SDK220804V0/sysroots/x86_64-pokyXXXsdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-g++ -o testuartarm testuart.c --sysroot=/opt/poky/SDK220804V0/sysroots/cortexa7t2hf-neon-poky-linux-gnueabi/ -march=armv7ve -marm -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a7
MIPS:
$ ~/x2000br/buildroot/buildroot/output/host/usr/bin/mipsel-linux-gcc -o testuartmips testuart.c
得到可执行文件:
$ file testuartgcc
testuartgcc: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=7502376cbab0f5ea2a13197d89a126dffdc32876, for GNU/Linux 3.2.0, not stripped
$ file testuartarm
testuartarm: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, BuildID[sha1]=aeea22788a340f6862e60c02021fc5718dbb1d12, for GNU/Linux 3.2.0, with debug_info, not stripped
$ file testuartmips
testuartmips: ELF 32-bit LSB shared object, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-mipsn8.so.1, for GNU/Linux 5.4.0, not stripped
$ which nm
/usr/bin/nm
$ ll /usr/bin/nm
lrwxrwxrwx 1 root root 19 10月 20 2021 /usr/bin/nm -> x86_64-linux-gnu-nm*
使用X86平台, mips toolchain和arm toolchain的nm命令分别读取x86程序的符号列表:
ARM不能读,其他两个结果一样。
$ ~/x2000br/buildroot/buildroot/output/host/usr/bin/mipsel-linux-nm -g -n testuartgcc
w __cxa_finalize@@GLIBC_2.2.5
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U __libc_start_main@@GLIBC_2.2.5
U open@@GLIBC_2.2.5
U printf@@GLIBC_2.2.5
0000000000001080 T _start
0000000000001169 T main
00000000000011b0 T __libc_csu_init
0000000000001220 T __libc_csu_fini
0000000000001228 T _fini
0000000000002000 R _IO_stdin_used
0000000000004000 D __data_start
0000000000004000 W data_start
0000000000004008 D __dso_handle
0000000000004010 B __bss_start
0000000000004010 D _edata
0000000000004010 D __TMC_END__
0000000000004018 B _end
$ nm -g -n testuartgcc
w __cxa_finalize@@GLIBC_2.2.5
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U __libc_start_main@@GLIBC_2.2.5
U open@@GLIBC_2.2.5
U printf@@GLIBC_2.2.5
0000000000001080 T _start
0000000000001169 T main
00000000000011b0 T __libc_csu_init
0000000000001220 T __libc_csu_fini
0000000000001228 T _fini
0000000000002000 R _IO_stdin_used
0000000000004000 D __data_start
0000000000004000 W data_start
0000000000004008 D __dso_handle
0000000000004010 B __bss_start
0000000000004010 D _edata
0000000000004010 D __TMC_END__
0000000000004018 B _end
$ /opt/poky/SDK220804V0/sysroots/x86_64-pokyXXXsdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-nm -g -n testuartgcc
/opt/poky/SDK220804V0/sysroots/x86_64-pokyXXXsdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-nm: testuartgcc: file format not recognized
使用X86平台, mips toolchain和arm toolchain的nm命令分别读取mips程序的符号列表:
都可以读,内容一样。
$ nm -g -n testuartmips
w __cxa_finalize@GLIBC_2.2
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U __libc_start_main@GLIBC_2.34
U open@GLIBC_2.0
U printf@GLIBC_2.0
U __stack_chk_fail@GLIBC_2.4
U __stack_chk_guard@GLIBC_2.4
000005d0 T _init
00000660 T _ftext
00000660 T __start
00000840 T main
000009b0 T _fini
00000a00 R _IO_stdin_used
00011000 D __data_start
00011000 W data_start
00011000 D _fdata
00011010 D __RLD_MAP
00011074 B __bss_start
00011074 G _edata
00011074 B _fbss
00011090 B _end
$ ~/x2000br/buildroot/buildroot/output/host/usr/bin/mipsel-linux-nm -g -n testuartmips
w __cxa_finalize@GLIBC_2.2
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U __libc_start_main@GLIBC_2.34
U open@GLIBC_2.0
U printf@GLIBC_2.0
U __stack_chk_fail@GLIBC_2.4
U __stack_chk_guard@GLIBC_2.4
000005d0 T _init
00000660 T _ftext
00000660 T __start
00000840 T main
000009b0 T _fini
00000a00 R _IO_stdin_used
00011000 D __data_start
00011000 W data_start
00011000 D _fdata
00011010 D __RLD_MAP
00011074 B __bss_start
00011074 D _edata
00011074 B _fbss
00011090 B _end
$ /opt/poky/SDK220804V0/sysroots/x86_64-pokyXXXsdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-nm -g -n testuartmips
w __cxa_finalize@GLIBC_2.2
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U __libc_start_main@GLIBC_2.34
U open@GLIBC_2.0
U printf@GLIBC_2.0
U __stack_chk_fail@GLIBC_2.4
U __stack_chk_guard@GLIBC_2.4
000005d0 T _init
00000660 T _ftext
00000660 T __start
00000840 T main
000009b0 T _fini
00000a00 R _IO_stdin_used
00011000 D __data_start
00011000 W data_start
00011000 D _fdata
00011010 D __RLD_MAP
00011074 B __bss_start
00011074 D _edata
00011074 B _fbss
00011090 B _end
使用X86平台, mips toolchain和arm toolchain的nm命令分别读取arm程序的符号列表:
$ nm -g -n testuartarm
U abort@@GLIBC_2.4
U __aeabi_unwind_cpp_pr1@@GCC_3.5
w __cxa_finalize@@GLIBC_2.4
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U __libc_start_main@@GLIBC_2.34
U open@@GLIBC_2.4
U printf@@GLIBC_2.4
00000444 T _init
000004ad T _start
000005a8 T main
00000600 T _fini
00000608 R _IO_stdin_used
00011038 D __data_start
00011038 W data_start
0001103c D __dso_handle
00011040 B __bss_start
00011040 B __bss_start__
00011040 D _edata
00011040 D __TMC_END__
00011044 B __bss_end__
00011044 B _bss_end__
00011044 B __end__
00011044 B _end
$ ~/x2000br/buildroot/buildroot/output/host/usr/bin/mipsel-linux-nm -g -n testuartarm
U abort@@GLIBC_2.4
U __aeabi_unwind_cpp_pr1@@GCC_3.5
w __cxa_finalize@@GLIBC_2.4
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U __libc_start_main@@GLIBC_2.34
U open@@GLIBC_2.4
U printf@@GLIBC_2.4
00000444 T _init
000004ad T _start
000005a8 T main
00000600 T _fini
00000608 R _IO_stdin_used
00011038 D __data_start
00011038 W data_start
0001103c D __dso_handle
00011040 B __bss_start
00011040 B __bss_start__
00011040 D _edata
00011040 D __TMC_END__
00011044 B __bss_end__
00011044 B _bss_end__
00011044 B __end__
00011044 B _end
$ /opt/poky/SDK220804V0/sysroots/x86_64-pokyXXXsdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-nm -g -n testuartarm
U abort@@GLIBC_2.4
U __aeabi_unwind_cpp_pr1@@GCC_3.5
w __cxa_finalize@@GLIBC_2.4
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U __libc_start_main@@GLIBC_2.34
U open@@GLIBC_2.4
U printf@@GLIBC_2.4
00000444 T _init
000004ac T _start
000005a8 T main
00000600 T _fini
00000608 R _IO_stdin_used
00011038 D __data_start
00011038 W data_start
0001103c D __dso_handle
00011040 B __bss_start
00011040 B __bss_start__
00011040 D _edata
00011040 D __TMC_END__
00011044 B __bss_end__
00011044 B _bss_end__
00011044 B __end__
00011044 B _end
结论就是,对于nm命令,不一定要使用cross-toolchain的版本,用Ubuntu里自带的nm也一样用,结果一样。如果有问题,再换成toolchain里面的nm命令。
因为nm查询的是符号名称,这部分功能可能和芯片类型关系不大,各个不同的toolchain,编译出来的格式是类似的,大致上是使用的相同的框架来管理符号。
参考:
nm man | Linux Command Library
https://www.howtoforge.com/linux-nm-command/
10 Practical Linux nm Command Examples
IBM Documentation