好好学习,天天向上,一流范文网欢迎您!
当前位置: >> 体会 >> 工作心得 内容页

函数符号的故事 STM32

有三个文件:

funcA.c:
void bar( ) {
}
void funcA () {
}
funcB.c:
void bar( ) {
}
void funcB () {
}
main.c
int main() {
? return 0;
}

编译:

$ gcc -o test main.c funcA.c funcB.c/

数学函数符号_函数符号 搞笑故事_函数符号的故事

usr/bin/ld: /tmp/cczNqgbd.o: 在函数 'bar' 中:

funcB.c:(.text+0x0):“bar”的多重定义;/tmp/ccoiI4Pf.o:funcA.c:(.text+0x0):首先在这里定义

collect2:错误:ld 返回 1 个退出状态

使用 C 文件编译时,由于 bar 函数具有重复的定义,因此会发生链接错误。

首先将 C 文件编译为 obj 文件是相同的错误。

$ gcc -c funcA.c

$ gcc -c funcB.c

$ gcc -o test main.c funcA.o funcB.o/

usr/bin/ld: /tmp/cczNqgbd.o: 在函数 'bar' 中:

funcB.c:(.text+0x0):“bar”的多重定义;/tmp/ccoiI4Pf.o:funcA.c:(.text+0x0):首先在这里定义

collect2:错误:ld 返回 1 个退出状态

如果删除 funcA.c 和 funcB.c 中重复定义的柱函数,则可以正常编译,而不会显示错误消息。

funcA 和 funcB 函数都链接到可执行文件中,尽管它们没有被调用。

$ gcc -o test main.c funcA.c funcB.c

$ 纳米 -g 测试

0000000000004010 B __bss_start

w __cxa_finalize@@GLIBC_2.2.5

0000000000004000 D __data_start

0000000000004000 W data_start

0000000000004008 D __dso_handle

0000000000004010 D _edata

0000000000004018 B _end

00000000000011c8 T _fini

0000000000001138 T 函数

0000000000001143 T 函数

w __gmon_start__

0000000000002000 R _IO_stdin_used

w _ITM_deregisterTMCloneTable

w _ITM_registerTMCloneTable

00000000000011c0 T __libc_csu_fini

0000000000001150 T __libc_csu_init

U __libc_start_main@@GLIBC_2.2.5

0000000000001129 T型主

0000000000001040 T _start

0000000000004010 D __TMC_END__

然后,我们恢复到原始的柱函数定义函数符号的故事,它在 funcA.c 和 funcB.c 中有柱函数定义。

数学函数符号_函数符号 搞笑故事_函数符号的故事

然后编译成一个对象

文件函数符号的故事,然后将 obj 文件的两个文件打包到一个静态库中,并使用静态库生成可执行文件。

由于 main 函数不调用两个文件中的函数,因此生成的可执行文件不包含两个 obj 文件的符号,这与上述使用源文件或 obj 文件的结果不同。

此外,funcA.o 和 funcB.o 中的重复柱函数定义不会报告错误,因为两个 obj 文件都不参与链接。

$ gcc -c funcA.c

$ gcc -c funcB.c

$ ar rcs func.a func*.o

$ nm func.a

funcA.o:0000000000000000

T bar

0000000000000017 T 函数

U _GLOBAL_OFFSET_TABLE_

U 把

funcB.o:0000000000000000

T bar

0000000000000017 T 函数

U _GLOBAL_OFFSET_TABLE_

U 把

$ gcc -o test main.c func.a

$ 纳米 -g 测试

0000000000004010 B __bss_start

w __cxa_finalize@@GLIBC_2.2.5

0000000000004000 D __data_start

0000000000004000 W data_start

0000000000004008 D __dso_handle

0000000000004010 D _edata

0000000000004018 B _end

00000000000011b8 T _fini

w __gmon_start__

0000000000002000 R _IO_stdin_used

w _ITM_deregisterTMCloneTable

w _ITM_registerTMCloneTable

00000000000011b0 T __libc_csu_fini

0000000000001140 T __libc_csu_init

U __libc_start_main@@GLIBC_2.2.5

0000000000001129 T型主

函数符号的故事_函数符号 搞笑故事_数学函数符号

0000000000001040 T _start

0000000000004010 D __TMC_END__

关于 obj 文件的打包注意事项:

$ ar rcs func.a func*.o

这是等效的

$ ar rcs func.a funcA.o funcB.o

因此,使用 nm 命令,显示顺序是打包的顺序,首先是 funcA.o,然后是 funcB.o

那么如果我们在主函数中调用库函数呢?

主.c

int main()

{

funcA();

返回 0;

}

构建可执行文件:

$ gcc -o test main.c func.a

$ 纳米 -g 测试

0000000000001162 T 棒

0000000000004010 B __bss_start

w __cxa_finalize@@GLIBC_2.2.5

0000000000004000 D __data_start

0000000000004000 W data_start

0000000000004008 D __dso_handle

0000000000004010 D _edata

0000000000004018 B _end

0000000000001208 T _fini

0000000000001179 T 函数

w __gmon_start__

0000000000002000 R _IO_stdin_used

w _ITM_deregisterTMCloneTable

w _ITM_registerTMCloneTable

0000000000001200 T __libc_csu_fini

0000000000001190 T __libc_csu_init

U __libc_start_main@@GLIBC_2.2.5

0000000000001149 T型主

U puts@@GLIBC_2.2.5

数学函数符号_函数符号的故事_函数符号 搞笑故事

0000000000001060 T _start

0000000000004010 D __TMC_END__

它可以正确生成,并在调用的 funcA.o 中包含 funcA 函数,以及未使用的柱函数,即包含 funcA.o 的内容。

如果在 funcB.o 中未调用任何内容,则不包括该函数,因此不会重复柱函数。

再次修改主函数并调用柱函数。

主.c

int main()

{

funcA();

条();

返回 0;

}

构建可执行文件:

$ gcc -o test main.c func.a

$ 纳米 -g 测试

000000000000116c T 棒

0000000000004010 B __bss_start

w __cxa_finalize@@GLIBC_2.2.5

0000000000004000 D __data_start

0000000000004000 W data_start

0000000000004008 D __dso_handle

0000000000004010 D _edata

0000000000004018 B _end

0000000000001208 T _fini

0000000000001183 T 函数

w __gmon_start__

0000000000002000 R _IO_stdin_used

w _ITM_deregisterTMCloneTable

w _ITM_registerTMCloneTable

0000000000001200 T __libc_csu_fini

0000000000001190 T __libc_csu_init

U __libc_start_main@@GLIBC_2.2.5

0000000000001149 T型主

U puts@@GLIBC_2.2.5

0000000000001060 T _start

0000000000004010 D __TMC_END__

数学函数符号_函数符号的故事_函数符号 搞笑故事

结果与前面的示例类似,funcA.o 包含所有内容,包括调用的 funcA 和 bar 函数。

在 funcB.o 中,柱线的功能重要吗?

这与我们打包的顺序有关,如果我们先打包 funcB.o 文件,那么链接器在寻找 main 函数要调用的柱函数时会首先找到 funcB.o。

当您发现

调用的 funcA 函数,您可以找到 funcA.o。

然后两个文件都必须参与链接,在这种情况下,由于重复定义了 bar 函数,因此会发生错误。

$ gcc -c funcA.c

$ gcc -c funcB.c

$ ar rcs func.a funcB.o funcA.o

$ nm func.a

funcB.o:0000000000000000

T bar

0000000000000017 T 函数

U _GLOBAL_OFFSET_TABLE_

U 把

funcA.o:0000000000000000

T bar

0000000000000017 T 函数

U _GLOBAL_OFFSET_TABLE_

U 把

$ gcc -o test main.c func.a/

usr/bin/ld: func.a(funcA.o): 在函数 'bar' 中:

funcA.c:(.text+0x0):“bar”的多重定义;func.a(funcB.o):funcB.c:(.text+0x0):首先在这里定义

collect2:错误:ld 返回 1 个退出状态

总结

1. 生成可执行文件时,

如果源文件或 OBJ 文件参与,则在连接连接过程时,其内容将包含在可执行文件中。

2、如果是

一个静态库,它基于里面的OBJ文件,如果使用就包含在内,如果不使用则不包括在内。

3. 链接器将在静态库中查找引用的函数或变量的定义,以先找到者为准,找到后不会继续搜索。

查找的顺序(在案例中)

多个静态库,按一阶顺序排列。如果它位于静态库内,则它是 obj 文件的打包顺序。

4. 静态库中可以有重复的定义,只要它所在的 obj 文件没有同时使用。

关于使用静态库参数编译程序

也可以使用以下命令生成上述可执行程序:

$ ar rcs libfunc.a *.o

$ gcc -o test main.c -lfunc -L./