我有 NASM 创建的简单汇编文件。我想将它们与tcc
相关联。为了进行调试,我想在汇编代码中使用printf()
。但是当我这样做时,tcc
会因tcc: undefined symbol 'printf'
而失败。
以下是重现错误的最小示例代码:
extern printf
hello: db "Hello world!",0
global main
main:
push hello
call printf
pop eax
ret
控制台:
nasm -felf hello.asm
tcc hello.o
tcc: undefined symbol 'printf'
当我使用gcc hello.o
时,一切正常,所以它必须是特定于tcc的问题。如何使用tcc?
编辑:我使用的是Windows版本的 NASM 和 TCC 来生成32位Windows可执行文件。
答案 0 :(得分:5)
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
请注意printf
中的符号hello.o
与上面simple.o
中的符号有何不同。 NASM 默认使用 NOTYPE 属性而不是 FUNCTION 定义标签。
我不知道如何解决 NASM 中的问题,因为我不知道如何强制它使用 FUNCTION 在定义为extern
的符号上键入而不是 NOTYPE 。我在十六进制编辑器中更改了类型,并将其链接并按预期运行。
另一种选择是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