将fortran代码编译为目标文件时:编译器如何确定符号名称?
当我使用内部函数“getarg”时,编译器将其转换为名为“_getarg @ 12”的符号
我查看了外部库,发现里面的符号名称叫做“_getarg @ 16”,“getarg”末尾的“@ [number]”有什么意义?
答案 0 :(得分:4)
_name@length
是高度特定于Windows的名称修改,应用于遵循stdcall
(或__stdcall
通过C中使用的关键字名称调用约定的例程的名称,a Pascal调用约定的变体。这是所有Win32 API函数使用的调用约定,如果查看KERNEL32.DLL
和USER32.DLL
等DLL的导出表,您会看到所有符号都是这样命名的。
_...@length
装饰给出了例程参数占用的字节数。这是必要的,因为在stdcall
调用约定中,被调用者从堆栈中清除参数而不是调用者,就像C调用约定一样。当编译器使用两个4字节参数生成对func
的调用时,它会在对象代码中引用_func@8
。如果真实func
恰好具有不同数量或大小的参数,则其装饰名称将是不同的,例如_func@12
因此会发生链接错误。这对动态库(DLL)非常有用。想象一下,DLL被另一个版本替换,其中func
需要一个额外的参数。如果不是名称修改(前缀为_
并将@length
添加到符号名称的技术术语),程序仍会使用错误的参数调用func
,然后func
将使用比传递的参数列表大小更多的字节来递增堆栈指针,从而打破调用者。在名称修改到位后,加载器根本无法启动可执行文件,因为它无法解析对_func@8
的引用。
在您的情况下,看起来外部库实际上不打算与此编译器一起使用,或者您缺少一些pragma或编译器选项。 getarg
内在函数有两个参数 - 一个整数和一个假定大小的字符数组(字符串)。一些编译器将字符数组大小作为附加参数传递。使用32位代码,这将导致传递2个指针和1个整数,总共12个字节的参数,因此_getarg@12
。例如,_getarg@16
可以是64位例程,其中字符串由某种描述符传递。
正如IanH在评论中提醒我的那样,这种命名差异的另一个原因可能是你使用比预期更少的参数调用getarg
。 Fortran具有“无原型”例程调用的这一特殊功能 - Fortran编译器可以在不知道其签名的情况下生成对例程的调用,这与C / C ++不同,后者必须以函数原型的形式提供显式签名。这是可能的,因为在Fortran中所有参数都通过引用传递,并且指针始终具有相同的大小,无论它们指向的实际类型如何。在这种特殊情况下,stdcall
名称修改起着非常粗略的参数检查机制的作用。如果它不是用于修改(例如在使用GNU Fortran的Linux上没有使用这样的装饰或者默认调用约定是cdecl
),那么可以调用具有与预期不同的参数数量的例程并且链接器将愉快地将目标代码链接到可执行文件中,然后很可能在运行时崩溃。
答案 1 :(得分:2)
这完全取决于实现。您没有说,您使用的是哪个编译器。对于不同的整数或字符种类,(非标准)内在函数可以存在于更多版本中。对于更多计算机体系结构(例如32位和64位),还可以有更多版本的运行时库。