【问题标题】:tcc: Use C standard functions in assembly codetcc:在汇编代码中使用 C 标准函数
【发布时间】:2017-08-28 13:34:09
【问题描述】:

我有 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 版本的 NASMTCC 来生成 32 位 Windows 可执行文件。

【问题讨论】:

  • 也许 tcc 会破坏名称,您可以尝试使用 _printf。或者查看实际的 tcc 库或编译测试 C 程序并查看(反)程序集。
  • @Jester 装饰名称的不是 tcc,而是平台的 ABI。是的,Windows 的 ABI 确实修饰了名称。
  • @fuz 仅仅因为他在 Windows 上,并不意味着他正在使用 Windows ABI。提示:-f elf.
  • @kaetzacoatl 我明白了。 tcc 可以编译使用printf 的C 程序吗?你为什么首先使用 tcc?
  • 问题似乎是 TCC 在尝试链接时对 ELF 对象中的符号类型进行了分析。 NASM 使用 NO_TYPE 属性创建像 printf 这样的外部标签,但 TCC 似乎要求它们是 FUNCTION 类型。我碰巧编译了一个 C 文件(调用 printf)并将该对象与您的 NASM 对象进行了比较。唯一的区别是符号的类型。如果我使用十六进制编辑器手动更改类型,它能够正确找到printf

标签: c assembly x86 nasm tcc


【解决方案1】:

TCC 似乎需要有关外部链接的函数的特定类型信息,例如 printf。默认情况下,NASMELF 对象中创建对具有 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 的重写)。 大部分部分 NASMYASM 的工作方式相同。 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

【讨论】:

  • 感谢您的回答。现在我知道问题是什么了。我真的很想知道,为什么 nasm 不支持 extern printf:function 之类的东西,这可以解决问题。我会看看是否有可能在 tcc 方面做点什么,但恐怕这无济于事。可能我必须更改我的汇编器或编译器。
  • @kaetzacoatl 在 Nasm 和 Yasm 之间更改汇编程序非常容易。 Yasm 下载的链接在我的回答中。如果不更改为完全不同的工具链(如 gcc),这可能是最简单的事情。只需下载 yasm 可执行文件,将其重命名为 yasm.exe 并将其放在与 NASM 相同的目录中,这是最简单的。然后你可以在命令行上使用 yasm 而不是 nasm。
  • 我会将此作为错误报告给 tcc 团队。
  • @kaetzacoatl 我不知道为什么 NASM 在定义外部标签时不支持声明类型(使用extern)。在定义像 global main:function 这样的全局标签时是允许的,但不支持 extern printf:function
  • 正如@fuz 指出的那样,人们可以认为这是 TCC 的一个错误。您可以在他们的bug tracking system 上提交错误(并查看现有的未解决错误)
猜你喜欢
  • 1970-01-01
  • 2011-09-04
  • 1970-01-01
  • 2016-08-27
  • 2013-03-13
  • 2021-11-29
相关资源
最近更新 更多