【问题标题】:LLVM stdin/stdout/stderrLLVM 标准输入/标准输出/标准错误
【发布时间】:2012-06-03 02:04:44
【问题描述】:

如何在 LLVM 中声明 stdinstoutstderr(最好是 C 版本)?我正在尝试在我正在创建的玩具语言中使用一些 stdio 函数。一个这样的功能是fgets

char * fgets ( char * str, int num, FILE * stream );

为了使用它,我需要stdin。所以我编写了一些 LLVM API 代码来生成我找到的 FILE 的定义,并将stdin 声明为外部全局变量。代码生成了这个:

%file = type { i32, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, %marker*, %file*, i32, i32, i64, i16, i8, [1 x i8], i8*, i64, i8*, i8*, i8*, i8*, i64, i32, [20 x i8] }
%marker = type { %marker*, %file*, i32 }

@stdin = external global %file*

但是,当我运行生成的模块时,它给了我这个错误:

Undefined symbols for architecture x86_64:
"_stdin", referenced from:
    _main in cc9A5m3z.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status

显然,我写的没有用。所以我的问题是,我必须在 LLVM API 中写什么来声明 stdinstoutstderr 用于类似玩具语言编译器的 fgets 之类的函数?

【问题讨论】:

  • 您可以在 C 中编写帮助函数,该函数将返回 stdin/stdout/stderr 并将它们与您的程序链接。
  • 我会尝试,但如果可以的话,我更愿意使用 LLVM API 提供的功能。

标签: llvm


【解决方案1】:

如果有人感兴趣,我找到了我的问题的答案。经过一番激烈的搜索后,我找到了一种无需进行 C 扩展即可获得 stdin 流的方法:fdopen 并使 FILE 成为不透明的结构。

FILE* fdopen (int fildes, const char *mode)

当 fdopen 为文件描述符 (fildes) 传递 0 时,它返回 stdin 流。使用 LLVM API,我生成了以下 LLVM 程序集:

%FILE = type opaque
declare %FILE* @fdopen(i32, i8*)
@r = constant [2 x i8] c"r\00"

然后我可以使用这个调用语句检索stdin

%stdin = call %FILE* @fdopen(i32 0, i8* getelementptr inbounds ([2 x i8]* @r, i32 0, i32 0))

【讨论】:

  • 也许您还可以在回答中指出 C99 标准未指定“stderr”& co。成为一个全球性的外部人员。
【解决方案2】:

如果您使用putcharprintfgetsstrtolputsfflush 等函数,则不需要stdinstdout。我写了一个玩具编译器,这些对于字符串和整数的 I/O 来说已经足够了。 fflush 以 null 调用,stdout 被刷新。

%struct._IO_FILE = type opaque
declare i32 @fflush(%struct._IO_FILE*)
...
call i32 @fflush(%struct._IO_FILE* null)
...

【讨论】:

  • 这个答案基本上是长篇大论的“不要这样做!”
【解决方案3】:

它是特定于平台的。有时标准输入是不同符号名称的宏。

例如,在 Android 上,标准输入为 #define stdin (&__sF[0])

对于 Microsoft Visual C++,标准输入为 #define stdin (&__iob_func()[0])

因此,您确实需要查看您的平台 stdio.h 标头来弄清楚这一点。

【讨论】:

  • 那么,我需要在 LLVM API 中写什么来声明标准输入,例如在玩具语言编译器中?
  • 您需要查看您的 platfrom stdio.h 标头以找出用于 stdio 的符号/声明,以便它与您将使用的 C 运行时库正确链接。