TCC 似乎需要有关外部链接的函数的特定类型信息,例如 printf。默认情况下,NASM 在 ELF 对象中创建对具有 NOTYPE 属性的符号的引用。这似乎使 TCC 感到困惑,因为它似乎期望外部函数符号被标记为 FUNCTION 类型。
我通过简单的 C 程序发现了这一点:
#include <stdio.h>
int main()
{
printf ("hello\n");
}
并使用如下命令将其编译为目标文件(TCC 默认使用 ELF 对象):
tcc -c simple.c
这会生成simple.o。我碰巧使用 OBJDUMP 来显示汇编代码和 ELF 标头。我在代码中没有看到任何异常,但标题中的符号表显示了差异。如果您使用程序 READELF,您可以获得符号的详细转储。
readelf -s simple.o
Symbol table '.symtab' contains 5 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS simple.c
2: 00000000 7 OBJECT LOCAL DEFAULT 2 L.0
3: 00000000 26 FUNC GLOBAL DEFAULT 1 main
4: 00000000 0 FUNC GLOBAL DEFAULT UND printf
特别感兴趣的是printf 的符号表条目:
4: 00000000 0 FUNC GLOBAL DEFAULT UND printf
如果您要为 hello.o 对象转储 ELF 标头,您看起来会类似于以下内容:
readelf -s hello.o
Symbol table '.symtab' contains 6 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS hello.asm
2: 00000000 0 SECTION LOCAL DEFAULT 1
3: 00000000 0 NOTYPE LOCAL DEFAULT 1 hello
4: 00000000 0 NOTYPE GLOBAL DEFAULT UND printf
5: 0000000d 0 NOTYPE GLOBAL DEFAULT 1 main
注意hello.o 中的符号printf 与上面simple.o 中的符号有何不同。 NASM 默认使用 NOTYPE 属性而不是 FUNCTION 定义标签。
使用 YASM 代替 NASM
我不知道有什么方法可以解决 NASM 中的问题,因为我不知道如何强制它使用 FUNCTION 类型而不是 NOTYPE 在定义为 extern 的符号上。我在十六进制编辑器中更改了类型,它链接并按预期运行。
另一种方法是 download YASM(对 NASM 的重写)。 大部分部分 NASM 和 YASM 的工作方式相同。 YASM 的命令行大多与 NASM 兼容,因此您应该能够将其用作直接替代品。 YASM 有一个额外的功能,允许您使用type directive 指定符号的类型:
9.3.3. TYPE: Set symbol type
ELF’s symbol table has the capability of indicating whether a symbol is a
function or data. While this can be specified directly in the GLOBAL
directive (see Section 9.4), the TYPE directive allows specifying the
symbol type for any symbol, including local symbols.
The directive takes two parameters; the first parameter is the symbol
name, and the second is the symbol type. The symbol type must be either
function or object. An unrecognized type will cause a warning to be
generated. Example of use:
func:
ret
type func function
section .data
var dd 4
type var object
您只需为您使用的每个外部函数在汇编代码中添加一行额外的类型信息。您的汇编代码可以修改为:
extern printf
type printf function
hello: db "Hello world!",0
global main
main:
push hello
call printf
pop eax
ret
它应该编译和链接:
yasm -felf hello.asm -o hello.o
tcc hello.o -o hello.exe