编译到 LLVM IR 的 Haskell 程序缺少 main

2023-12-02

下列的这个帖子关于Haskell程序的编译 对于 LLVM IR,我采用了相同的 Haskell 程序并尝试运行其生成的 LLVM IR 代码:

quicksort [] = []
quicksort (p:xs) = (quicksort lesser) ++ [p] ++ (quicksort greater)
  where
    lesser  = filter (<  p) xs
    greater = filter (>= p) xs

main = print(quicksort([5,2,1,0,8,3]))

我首先将其编译为 LLVM IR

$ ghc -keep-llvm-files main.hs

然后我将其转换为位码:

$ llvm-as main.ll

但是,当我尝试运行它时lli我收到以下关于缺少主程序的错误:

$ lli main.bc
'main' function not found in module.

难道我做错了什么?谢谢。

编辑:(来自 K.A. Buhr 的回答)

$ ls -l main*
main.hs
$ ghc -keep-llvm-files main.hs
[1 of 1] Compiling Main             ( main.hs, main.o )
Linking main ...
$ ls -l main*
main
main.hi
main.hs
main.ll
main.o
$ rm main main.hi main.o
$ llvm-as main.ll
$ llc main.bc -filetype=obj -o main.o
$ ghc -o main main.o
$ ./main
[0,1,2,3,5,8]

tl;dr.入口点(可能)被命名为ZCMain_main_closure,它是一个引用代码块的数据结构,而不是代码块本身。尽管如此,它仍然可以被 Haskell 运行时解释,并且它直接对应于函数的 Haskell“值”main :: IO ()在你的main.hs程序。

较长的答案涉及的内容比您想了解的有关链接程序的内容还要多,但事情是这样的。当你使用如下 C 程序时:

#include <stdio.h>
int main()
{
        printf("I like C!\n");
}

将其编译为目标文件gcc:

$ gcc -Wall -c hello.c

并检查目标文件的符号表:

$ nm hello.o
0000000000000000 T main
                 U printf

你会看到它包含符号的定义main以及对外部符号的(未定义)引用printf.

现在,你可能会想象main是该程序的“入口点”。哈哈哈哈!你的想法是多么天真和愚蠢啊!

事实上,真正的 Linux 专家知道程序的入口点不在目标文件中hello.o根本不。它在哪里?嗯,它在“C 运行时”,一个通过以下方式链接的小文件gcc当您实际创建可执行文件时:

$ nm /usr/lib/x86_64-linux-gnu/crt1.o
0000000000000000 D __data_start
0000000000000000 W data_start
0000000000000000 R _IO_stdin_used
                 U __libc_csu_fini
                 U __libc_csu_init
                 U __libc_start_main
                 U main
0000000000000000 T _start
$

请注意,该目标文件有一个不明确的参考main它将链接到您所谓的入口点hello.o。正是这个小存根定义了real入口点,即_start。您可以看出这是实际的入口点,因为如果您将程序链接到可执行文件中,您将看到_start符号和 ELF 入口点(这是当您调用时内核实际上首先将控制权转移到的地址)execve()你的程序)将一致:

$ gcc -o hello hello.o
$ nm hello | egrep 'T _start'
0000000000400430 T _start
$ readelf -h hello | egrep Entry
Entry point address:               0x400430

这就是说,程序的“入口点”实际上是一个相当复杂的概念。

当您使用 LLVM 工具链而不是 GCC 编译和运行 C 程序时,情况非常相似。这是设计使然,以保持一切与 GCC 兼容。你所谓的入口点hello.llfile 只是 C 函数main,而且这不是real你的程序的入口点。这仍然是由crt1.o stub.

现在,如果我们(最终)从谈论 C 转向谈论 Haskell,很明显,Haskell 运行时比 C 运行时复杂大约十亿倍,但它是构建在 C 运行时之上的。因此,当您以正常方式编译 Haskell 程序时:

$ ghc main.hs
stack ghc -- main.hs
[1 of 1] Compiling Main             ( main.hs, main.o )
Linking main ...
$

您可以看到可执行文件有一个名为的入口点_start:

$ nm main | egrep 'T _start'
0000000000406560 T _start

这实际上与之前调用 C 入口点的 C 运行时存根相同:

$ nm main | egrep 'T main'
0000000000406dc4 T main
$ 

but this main不是你的 Haskellmain. This main is a C mainGHC 在链接时动态创建的程序中的函数。您可以通过运行以下命令来查看这样的程序:

$ ghc -v -keep-tmp-files -fforce-recomp main.hs

并翻查名为的文件ghc_4.c某个地方在一个/tmp子目录:

$ cat /tmp/ghc10915_0/ghc_4.c
#include "Rts.h"
extern StgClosure ZCMain_main_closure;
int main(int argc, char *argv[])
{
 RtsConfig __conf = defaultRtsConfig;
 __conf.rts_opts_enabled = RtsOptsSafeOnly;
 __conf.rts_opts_suggestions = true;
 __conf.rts_hs_main = true;
 return hs_main(argc,argv,&ZCMain_main_closure,__conf);
}

现在,您看到外部引用了吗?ZCMain_main_closure?不管你信不信,这就是你的程序的 Haskell 入口点,你应该在main.o,无论您是使用普通 GHC 管道还是通过 LLVM 后端进行编译:

$ egrep ZCMain_main_closure main.ll
%ZCMain_main_closure_struct = type <{i64, i64, i64, i64}>
...

现在,它不是一个“函数”。它是 Haskell 运行时系统可以理解的特殊格式的数据结构(闭包)。这hs_main()上面的函数(又一个入口点!)是 Haskell 运行时的主要入口点:

$ nm ~/.stack/programs/x86_64-linux/ghc-8.4.3/lib/ghc-8.4.3/rts/libHSrts.a | egrep hs_main
0000000000000000 T hs_main
$

它接受 Haskell 主函数的闭包作为 Haskell 入口点来开始执行程序。

所以,如果你经历了所有这些麻烦,希望能在一个程序中隔离一个 Haskell 程序*.ll您可以通过跳转到其入口点来直接运行的文件,那么我有一些坏消息要告诉您...;)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

编译到 LLVM IR 的 Haskell 程序缺少 main 的相关文章

随机推荐

  • 将 ID 设置为 google 地图 API v2 标记以返回相机意图拍摄的图像

    好吧 这个问题我已经面临了几天 但似乎无法解决 这就是我的地图的工作原理 点击地图上的任意点 根据相机意图拍照 将照片作为标记 缩略图 返回到点击的特定点 以上所有工作正常 这就是我被困的地方 点击标记 图像 它应该显示更大的标记图像 因为
  • python 西里尔字母解码

    我正在尝试打印从 mysql 选择的西里尔字符 这是我的代码 内容 ID DB 是 cp1251 gt gt gt db MySQLdb connect host localhost user XXX passwd XXXX gt gt g
  • Passport-jwt 未经授权

    我是节点js新手 我正在使用护照 jwt 进行身份验证 当我尝试进行身份验证时 它总是显示 未经授权 my 护照 js file const JwtStrategy require passport jwt Strategy const E
  • 在 Firebase 中按孩子的孩子查询

    我在 Firebase 中的对象结构如下所示 KBP27k4iOTT2m873xSE categories Geography true Oceania true correctanswer Yaren de facto languages
  • Python lxml.html XPath“属性不等于”运算符未按预期工作

    我正在尝试运行以下脚本 python from urllib import urlopen urllib request for python3 from lxml import html url http mpk lodz pl rozk
  • c# - 易失性关键字使用与锁定

    我在不确定是否有必要的地方使用了 volatile 我很确定在我的情况下锁是多余的 阅读这篇文章 Eric Lippert 评论 让我对 volatile 的使用感到焦虑 C 中什么时候应该使用 volatile 关键字 我使用 volat
  • 如何控制下拉列表中显示的屏保名称?

    我有一个可用的屏幕保护程序 并且想了解如何控制 Windows 在屏幕保护程序下拉列表中显示的名称 目前 这一切似乎都是基于文件名 但我知道事实并非如此 这是我尝试过的方法和结果 文件名 CC Votd scr屏幕保护程序名称 Cc 文件名
  • Azure 客户端应用程序访问受 AD 保护的 Azure api

    我有一个 Angular 5 应用程序和一个 Web API 应用程序 它们都托管在 Azure 中 它们已在网站级别使用 Azure AD 进行保护 例如不允许匿名访问 浏览 Angular 网站时 它要求我正常登录 然后我就可以访问 a
  • PHP - 从字符串中删除 标签

    嘿 我需要删除字符串中的所有图像 但我找不到正确的方法 这是我尝试过的 但它不起作用 preg replace
  • DateTimeFormatter 月份模式字母“L”失败

    我注意到java time format DateTimeFormatter无法按预期解析 见下文 import java time LocalDate import java time format DateTimeFormatter p
  • 语音识别即服务。(Android)

    我正在开发 Android 应用程序 我的应用程序需要语音识别 但我需要语音识别作为服务 我已经创建了简单的语音识别 当我单击按钮时 它会启动识别器意图 它工作正常并为我提供输出 但是 当用户单击按钮语音识别作为服务启动 在后台 时 我需要
  • Mathematica 中的非交换展开除以加法

    我需要编写一个函数来完全扩展非交换乘法而不是加法 例如 a b c 2 将扩展到a b a c 2从右边开始也是如此 我正在使用 ReplaceRepeated 由于我使用的是 NonCommutativeMultiply 而不是 Time
  • Wpf 按标签和类型查找所有控件

    我正在尝试按类型和标签名称检索所有元素 我已经找到了一些例子 如何按名称或类型查找 WPF 控件 https stackoverflow com a 978352 7444801 我尝试修改其中一些示例 但是它们从未给我想要的结果 所需方法
  • 如何使用 dplyr 在行组之间进行划分

    我有类似的数据 我想要与此链接所述的确切结果 如何使用 dplyr 在行组之间进行划分 然而 与我的数据的唯一区别是 有时 条件 列并不总是有 A 或 B 因此有时没有分母或分子 x lt data frame name rep lette
  • Azure Bing Web 搜索因查询搜索而失败

    我正在使用 Odata4j 当我尝试使用查询执行简单的网络搜索时 在代码的最后一行出现以下异常 PS 如果您觉得有帮助 请投票 谢谢 java lang RuntimeException Expected status OK found B
  • NSRunningApplication,返回最近使用的应用程序列表?

    我正在尝试获取最近使用的应用程序的列表 NSWorkspace返回活动应用程序的列表 我可以使用以下几个选项对它们进行排序NSRunningApplication 参见下面的列表 launchDate finishedLaunching p
  • 如何从日期时间获取 AM/PM 值?

    有问题的代码如下 public static string ChangePersianDate DateTime dateTime System Globalization GregorianCalendar PC new System G
  • Android:使用 ContentObserver 捕获传出短信或接收器不起作用

    我试图使用内容观察器捕获传出的短信事件 TEST OBSERVER ContentObserver co new SMSoutObserver new Handler getApplicationContext ContentResolve
  • 创建一个在 React 中工作的加载动画

    Fiddle var Hello React createClass getInitialState function return gridIsLoaded true animate self console log animating
  • 编译到 LLVM IR 的 Haskell 程序缺少 main

    下列的这个帖子关于Haskell程序的编译 对于 LLVM IR 我采用了相同的 Haskell 程序并尝试运行其生成的 LLVM IR 代码 quicksort quicksort p xs quicksort lesser p quic