2

我有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 一起工作?

编辑:我正在使用NASMTCC的 Windows 版本来生成 32 位 Windows 可执行文件。

4

1 回答 1

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

如果您要为您的对象转储ELFhello.o标头,您看起来类似于以下内容:

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.oNASM默认使用NOTYPE属性而不是FUNCTION定义标签。


使用 YASM 而不是 NASM

我不知道有什么方法可以解决NASM中的问题,因为我不知道有一种方法可以强制它在定义为的符号上使用FUNCTION类型而不是NOTYPEextern。我在十六进制编辑器中更改了类型,它链接并按预期运行。

一种替代方法是下载YASM (对NASM的重写)。在大多数情况下, NASMYASM的工作方式相同。YASM的命令行主要与NASM兼容,因此您应该能够将其用作直接替代品。YASM有一个额外的功能,允许您使用type指令指定符号的类型:

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
于 2017-08-28T17:25:17.557 回答