【问题标题】:Where is the error happening in this build此构建中发生的错误在哪里
【发布时间】:2021-09-27 07:55:33
【问题描述】:

我不是 C 程序员,所以我知道我在我的头上。

如何定位此错误发生的位置并确定它是什么?这在我看来是 make 命令的相关输出。

gmake --debug=jv
. . . 
Putting child 0x8006e6600 (/home/byrnejb_hll/Projects/AB4-1/aubit4glsrc/lib/libaubit4gl-1.5_3.so) PID 77727 on the chain.
Live child 0x8006e6600 (/home/byrnejb_hll/Projects/AB4-1/aubit4glsrc/lib/libaubit4gl-1.5_3.so) PID 77727 
/usr/local/bin/ld: keys.o:/home/byrnejb_hll/Projects/AB4-1/aubit4glsrc/incl/a4gl_libaubit4gl.h:1026: multiple definition of `abort_pressed'; fglwrap.o:(.bss+0xe0): first defined here
/usr/local/bin/ld: report.o: in function `A4GL_internal_open_report_file':
/usr/home/byrnejb_hll/Projects/AB4-1/aubit4glsrc/lib/libaubit4gl/report.c:764: warning: warning: tmpnam() possibly used unsafely; consider using mkstemp()
collect2: error: ld returned 1 exit status
Reaping losing child 0x8006e6600 PID 77727 
gmake[2]: *** [Makefile:211: /home/byrnejb_hll/Projects/AB4-1/aubit4glsrc/lib/libaubit4gl-1.5_3.so] Error 1
Removing child 0x8006e6600 PID 77727 from chain.
gmake[2]: Leaving directory '/usr/home/byrnejb_hll/Projects/AB4-1/aubit4glsrc/lib/libaubit4gl'
Reaping losing child 0x8006d2960 PID 77684 
gmake[1]: *** [Makefile:276: libaubit4gl-1.5_3.so] Error 2
Removing child 0x8006d2960 PID 77684 from chain.
gmake[1]: Leaving directory '/usr/home/byrnejb_hll/Projects/AB4-1/aubit4glsrc/lib'
Reaping losing child 0x8006e4200 PID 77600 
gmake: *** [Makefile:285: corecompile] Error 2
Removing child 0x8006e4200 PID 77600 from chain.

./lib/libaubit4gl.h 中的代码是:

1024 #ifdef OBJECTMODULE$
1025   char opts[100][80];^I^I/*menu options */$
1026   int abort_pressed;$
1027   int relxedit = 0, relyedit = 0;$
1028   int aborted;$
1029   long time_offset = 0;$
1030   int week_no = -1;$
1031   struct s_form_attr std_dbscr;$
1032 #else$
1033   extern char opts[10][80];^I/*menu options */$
1034   extern int abort_pressed;$
1035   extern int relxedit, relyedit;$
1036   extern int aborted;$
1037   extern long time_offset;$
1038   extern int week_no;$
1039   dll_import struct s_form_attr std_dbscr;$
1040 #endif$

这是唯一提到abort_pressed的地方。

fglwrap.c 中的代码,也是唯一使用abort_pressed 的地方

1450 void$
1451 A4GL_def_quit (void)$
1452 {$
1453 #ifdef DEBUG$
1454   A4GL_debug ("FIXME: DEFER QUIT NOT IMPLEMENTED - Setting quit mode");$
1455 #endif$
1456 }$
1457 $
1458 int abort_pressed=0;$
1459 $
1460 /**$
1461  *$
1462  * @todo Describe function$
1463  */$
1464 void$
1465 A4GL_set_abort (int a)$
1466 {$
1467 #ifdef DEBUG$
1468   A4GL_debug ("set_abort called with %d", a);$
1469 #endif$
1470   abort_pressed = a;$
1471 }$
1472 $

处理这个问题的正确方法是什么?我正在尝试让一个曾经可以工作的程序再次工作,但我不是作者。

【问题讨论】:

  • 唯一的实际错误是“'abort_pressed'的多重定义”。 abort_pressed 是什么?你知道声明定义的区别吗?也许你真的应该在头文件中声明abort_pressed
  • /usr/local/bin/ld: keys.o:/home/byrnejb_hll/Projects/AB4-1/aubit4glsrc/incl/a4gl_libaubit4gl.h:1026: multiple definition of 'abort_pressed'; fglwrap.o:(.bss+0xe0): first defined here 表示abort_pressed 定义在a4gl_libaubit4gl.h 的第1026 行以及fdlwrap.o 的某个位置(似乎它没有用调试符号编译,所以它没有给出源代码的行号)。
  • 请不要添加带有行号或编辑器格式字符的代码sn-ps。
  • 现在解决您的问题:查找定义了OBJECTMODULE 的源文件。我敢打赌它是在fglwrap.c 之外的另一个文件中定义的。这意味着您确实有多个abort_pressed 定义。一次在fglwrap.c 中,然后在另一个定义了OBJECTMODULE 的文件中。

标签: c freebsd


【解决方案1】:

是的,看起来“abort_pressed”有多个定义,这些定义源自“.h”文件(头文件)。您不应该在 .h 文件中定义变量,只需声明它们。如果你只包含一个 .h 文件一次,你可以摆脱这个,但如果你包含它两次,你会得到这个错误。

查看声明和定义之间的区别,并将定义移出 .h 文件。这是用 C 编码的最佳实践,即使您只需要将 .h 文件包含在一个地方

至于发生的机制,您的编译器会将所有 .h 文件复制并粘贴到您包含它们的任何位置,即。 "#include "header.h" 会将 header.h 的所有代码粘贴到任何地方。您可以多次声明内容,即声明一个函数 "math" -

void math(uint8_t val);

这样的东西可以根据需要多次写入您的代码,因此当它们被多次粘贴时,您的编译器不会感到困惑。但是,当您定义事物时,即。定义一个变量“globalFlags”

uint32_t globalFlags;

这会使编译器感到困惑,因为它在内存中创建了一个值“globalFlags”,可以通过关键字“globalFlags”访问该值。如果您尝试创建第二个版本,编译器会感到困惑,因为它不知道要访问两个内存地址中的哪一个,因为“globalFlags”适用于两者。所以它会抛出一个错误,并告诉你修复它

【讨论】:

  • 更准确地说,链接器在将所有模块链接在一起时检测到手头的情况。它不迷茫,它发现了一个错误,并愉快地报告了它。
【解决方案2】:

这是遗留代码的常见问题。头文件或源文件都定义了一个他们应该只声明的变量。

这来自于传统的 UNIX 链接模型; C 最初没有纯引用,也没有 extern 关键字,就像当时的其他语言一样。

另外,在上下文中,早期的 C 没有原型,结构成员名称没有命名空间(这就是为什么像 BSD 套接字这样的旧接口中的结构成员带有前缀),基本上它无法区分每个其他的,但从他们的名字来看,当时真的很原始。

因此,变量将被放置在目标文件中的(特殊)公共部分中,而不是引用,并且链接器将通过查看它来合并任何重复项;如果我没记错的话,请遵循 FORTRAN 使用的方法。

链接器仍然是它的一部分,它处理目标文件并尝试将公共部分中的符号解析为其他东西,最后剩下的任何东西都放在 BSS 中(未初始化/零数据)。

编译器会将任何临时声明(即没有初始化值)放在这个公共部分中。

GCC 默认遵循 C 的这种方法,直到 GCC 10,当它更改为用于 C++ 的相同方法时,即直接将它们放在 BSS 中;一件公平的事情,因为:

  • 30 多年来,我们有更好的方法来处理这个问题
  • 生成的机器代码并不总是最优的
  • unix 模型与 C 标准有点矛盾
  • 无法判断重复的定义是有意还是无意(所以这是一种不好的做法)

但是,GCC 仍然可以编译未修改的遗留代码,如果您不能或不会修改它,只需切换:-fcommon

【讨论】:

  • 我试过这个,记住我正在学习:gmake clean; ./configure --with-build-cflags=-fcommon ; gmake。结果与之前获得的结果相同:/usr/local/bin/ld: keys.o:/home/byrnejb_hll/Projects/AB4-1/aubit4glsrc/incl/a4gl_libaubit4gl.h:1026: multiple definition of 'abort_pressed'; fglwrap.o:(.bss+0xe0): first defined here。我是否误解了编译器标志是如何传递给 gcc 的?
  • 只需要在调用configure脚本时设置CFLAGS变量即可;例如:CFLAGS="-O2 -march=native -fcommon" ./configure
猜你喜欢
  • 2014-05-02
  • 2017-08-05
  • 2023-03-30
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 1970-01-01
  • 2014-12-15
  • 2017-06-17
相关资源
最近更新 更多