6

MEGAEDIT 3000

我发现了未定义引用的原因。我检查了我的 .o 文件中的符号,它们只是丢失了。只是不在那里。没有明显的原因。我已经检查了妥协的 .o 文件的来源,但一切似乎都很好。我认为它可能是那些讨厌的#ifdef ... #endif块之一,像熊陷阱一样巧妙地隐藏在代码行中,等待它的受害者。但我找不到任何东西。在将一些函数定义移动到文件的最后并将它们包含在#ifdef ... #endif适合 Linux 的块中之后,符号神奇地出现了,一切都很好。感谢您的时间。


过时了,不读。

我最近被分配到一个大项目,我的工作是让它在 Linux 平台上运行。不幸的是,在我之前编写整个项目的程序员做得很差,通过包含B3D_Base.h几乎每个头文件所在的文件来创建大量循环依赖项#included,到项目中的几乎每个头文件和源文件(这使得 170 多个文件与#include "B3D_Base.h"线)。这在 Windows 和 Linux 上的编译期间都没有引起任何问题。但是,这会在 Linux 上的链接阶段导致大量“未定义引用”错误,即使所有目标文件和库都已链接。

意识到ld在链接期间对所有目标文件进行单次迭代的限制,我在问:

有没有办法强制ld通过对象和库进行多次迭代,可能解决所有 700 多个未定义的引用错误,或者是我唯一的机会实际链接项目只是用#include "B3D_Base.h"正确的包含替换所有行到所需的标题?

附录1:

这是我应该在 Linux 上运行的混乱示例:

有一个头文件B3D_Base.h,包含在其中B3D_Loading3.h,其中包含 class B3D_LOADING3. 文件B3D_Loading3.cppB3D_Base.h包含,Base 中不包含内存管理器类。在项目的许多其他部分中B3D_LOADING3 g_Loading3;声明并初始化+使用了一个实例。B3D_Base.h一切都编译得很好,但是在链接方面,我遇到了很多'undefined reference to g_Loading3'错误。还有更多这样的错误,抱怨对类实例和函数的未定义引用。干净的代码就这么多。

附录#2:

根据 kfsone 的要求,我将提供一个 SSCCE 示例:

B3D_Base.h

... (many other includes)

#include "B3D_Loading3.h"
extern B3D_LOADING3 g_Loading3;

... (includes go on)

B3D_Loading3.h/B3D_Loading.cpp

a full definition of class B3D_LOADING3, but no real declaration of g_Loading3

AW_Game.cpp(使用 g_Loading3 的文件之一)

#include "B3D_Base.h"
... (some lines)
extern B3D_LOADING3 g_Loading3; 

感谢 kfsone 让我彻底检查代码,我设法找出问题所在: g_Loading3 到处都定义为extern,因此没有正确定义。我所需要的只是extern从一个文件中删除关键字。修复了我 ~ 30 个链接错误。谢谢,kfsone。

附录#3:

但是对类函数的未定义引用仍然存在问题。经过一些代码扫描后,我认为由于过度使用#ifdef ... #endif子句(可移植性),甚至没有定义函数,因此可能是我的错,忽略了仅在某些被动 ifdef 块中定义的函数。我将尝试查看此问题,稍后报告是否有任何变化。

附录#3 报告:

即使在正确定义函数之后,未定义引用列表也没有缩小。因此,这个问题仍有待讨论。

附录#3.1

我将提供一些示例代码。

Foo.h

#ifndef FOO_H
#define FOO_H

class Foo
{
    unsigned int    m_size;
    bool            m_lock;
    friend class ASDF;

public:
    unsigned int    m_id;

    void    Release() {delete this;}
    int     Lock(UINT OffsetToLock, UINT SizeToLock, VOID ** ppbData, DWORD Flags);
    int     Unlock();
};

// other class declarations to follow

#endif

Foo.cpp

#include "Foo.h"
// other includes and function definitions to follow

int Foo::Lock(UINT OffsetToLock, UINT SizeToLock, VOID ** ppbData, DWORD Flags)
{
    if(!m_lock)
    {
        // some locking code
    }
    return 0;
}

巴兹.cpp

#include "Foo.h"  // although included, defined and declared, causes undef reference

Foo *m_pFoo;
// several lines later
m_pFoo->Lock(0,0,(void)&Asdf,NULL); // <-- compiles fine, causes undefined reference
4

3 回答 3

2

没有任何示例,真的不清楚您的问题可能出在哪里,除了它不是头文件的顺序 - 它是那些头文件中的内容,以及随后链接模块的顺序。

GCC/LD 应该只抱怨对引用的东西的未定义引用,而不仅仅是声明的。

// t1.h
#pragma once
#include "t2.h"

class Foo1 {};
class Foo3 {}; // This is going to be our undef ref

extern Foo1 g_foo1_a;
extern Foo1& foo1_a();
extern Foo1 g_foo1_b;
extern Foo1& foo1_b();
extern Foo2 g_foo2;
extern Foo3 g_foo3;

// Uncomment this line to generate an undef ref
//static Foo3* g_foo3p = &g_foo3;


// t2.h
#pragma once
#include "t1.h"

class Foo2 {};

extern Foo2 g_foo2;

// t1.cpp
#include "t1.h"
#include "t2.h"

#if defined(_MSC_VER) && _MSC_VER > 9000
Foo3 g_foo3; // gcc won't produce one of these.
#endif

Foo1& foo1_b() { return g_foo1_b; }

int main(int argc, const char** argv)
{
}

// t2.cpp
#include "t1.h"

Foo1 g_foo1_b;

// Makefile
all:
    g++ -Wall -o t1.o -c t1.cpp
    g++ -Wall -o t2.o -c t2.cpp
    g++ -Wall -o t t1.o t2.o
    g++ -Wall -o t t2.o t1.o

编译时没有任何 undef 引用,直到您取消注释静态,此时您会同时收到编译警告(未使用的变量)和未定义的引用警告,因为实际上没有人实例化 g_foo3。

所以 - 虽然我意识到你不能给我们你的确切代码,但如果你想解决这个问题,你可能需要提出一个SSCCE。就像不是一样,在复制问题的过程中,您可能会弄明白,这就是为什么通常需要在这里提问的原因。

于 2013-07-20T19:55:15.070 回答
2

值得检查的一件事是确定它是否是死代码剥离问题。因此,我过去曾遇到过“未定义的引用”链接器错误。

如果不使用,MS 编译器将删除代码。默认情况下,GCC 不会这样做,如果该代码使用未定义的符号,则会出现错误。由于 MS 编译器删除了这些调用,因此不会出现任何错误。为了查看这是否是您的问题,请将这些标志添加到您的编译标志中(对于 C++ 和 C,如果您也有 C 源代码):

-fdata-sections -ffunction-sections

这个标志到你的链接标志:

-Wl,--gc-部分

(当然,请确保您使用g++的是链接,而不是ld.)

除此之外,我唯一的建议是使用适当的构建系统。我强烈推荐CMake。它简单、可靠,您应该能够快速学习它。为包含 100 多个源文件的 300k LOC 项目创建基于 CMake 的构建花了我不到 3 个小时(当时这是我第一次使用 CMake。)

于 2013-07-22T11:38:00.893 回答
1

-l在 Linux 上,除非您想学习深奥的 ld 参数,否则您将需要多个参数副本。列出所有库三次(是的,3 次):

 -la -lb -lc -la -lb -lc -la -lb -lc

确保满足库之间的所有依赖关系。请注意,数字3与库的数量无关。某些版本ld-load_all-(不那么冗长的解决方案来解决这个问题。

原因是链接器从右到左处理未定义的符号。当它第一次看到-la它时,它只会从 liba.a 中提取基本 exec 所需的 .o 文件。当它看到-lb它加载基础和 liba 所需的那些。如果 libb 需要某些 liba,那么除非您第二次列出 liba 等,否则您就不走运了。

于 2013-07-20T19:12:25.560 回答