3

将 fortran 代码编译成目标文件时:编译器如何确定符号名称?

当我使用内部函数“getarg”时,编译器会将其转换为名为“_getarg@12”的符号

我查看了外部库,发现里面的符号名称叫做“_getarg@16”,“getarg”末尾的“@[number]”是什么意思?

4

2 回答 2

4

_name@length是高度特定于 Windows 的名称修饰,应用于遵循stdcall(或__stdcall通过 C 中使用的关键字的名称)调用约定的例程名称,这是 Pascal 调用约定的变体。这是所有 Win32 API 函数都使用的调用约定,如果您查看 DLL 的导出表,KERNEL32.DLLUSER32.DLL会发现所有符号都是这样命名的。

_...@length装饰给出了例程参数占用的字节数。这是必要的,因为在stdcall调用约定中,是被调用者从堆栈中清理参数,而不是调用者,就像 C 调用约定的情况一样。func当编译器使用两个 4 字节参数生成一个调用时,它会将一个引用_func@8放入目标代码中。如果 realfunc碰巧有不同数量或大小的参数,它的修饰名称会有所不同,例如_func@12,因此会发生链接错误。这对于动态库 (DLL) 非常有用。想象一下,一个 DLL 被另一个版本替换,该版本func需要一个额外的参数。如果不是因为名字修饰(前置的技术术语_并添加@length到符号名称),程序仍然会func使用错误的参数调用,然后func会增加堆栈指针的字节数,而不是传递的参数列表的大小,从而破坏调用者。使用名称修改,加载程序根本不会启动可执行文件,因为它无法解析对_func@8.

在您的情况下,看起来外部库并不是真正打算与此编译器一起使用,或者您缺少一些编译指示或编译器选项。内在函数有getarg两个参数——一个整数和一个假定大小的字符数组(字符串)。一些编译器将字符数组大小作为附加参数传递。对于 32 位代码,这将导致 2 个指针和 1 个整数被传递,总共 12 个字节的参数,因此_getarg@12. 例如,_getarg@16可以是 64 位例程,其中字符串由某种描述符传递。

正如 IanH 在他的评论中提醒我的那样,这种命名差异的另一个原因可能是您调用getarg的参数比预期的要少。Fortran 具有“无原型”例程调用的这一特殊功能 - Fortran 编译器可以在不知道其签名的情况下生成对例程的调用,这与 C/C++ 中必须以函数原型的形式提供显式签名不同。这是可能的,因为在 Fortran 中,所有参数都是通过引用传递的,并且指针的大小始终相同,无论它们指向的实际类型如何。在这种特殊情况下,stdcall名称修饰扮演了非常粗糙的参数检查机制的角色。如果不是为了修饰(例如在带有 GNU Fortran 的 Linux 上没有使用这种装饰,或者默认调用约定是cdecl) 可以调用具有与预期不同数量的参数的例程,并且链接器会愉快地将目标代码链接到一个可执行文件中,该可执行文件很可能在运行时崩溃。

于 2012-11-18T17:08:08.017 回答
2

这完全取决于实现。你没有说,你使用哪个编译器。对于不同的整数或字符类型,(非标准)内在函数可以存在于更多版本中。对于更多的计算机体系结构(例如 32 位和 64 位),还可以有更多版本的运行时库。

于 2012-11-18T07:41:51.633 回答