Скомпилированная программа на Haskell для LLVM IR отсутствует main - PullRequest
0 голосов
/ 29 августа 2018

следующий этот пост SO относительно компиляции программ на 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.

Я что-то не так делаю? спасибо.

РЕДАКТИРОВАТЬ: (из ответа К. А. Бура)

$ 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]

1 Ответ

0 голосов
/ 30 августа 2018

tl; др. Точка входа (вероятно) называется 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 runtime" , маленьком файле, который связывается с 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. Именно эта маленькая заглушка определяет настоящую точку входа, а именно _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

Все это означает, что «точка входа» программы - это довольно сложная концепция.

Когда вы компилируете и запускаете программу на C с помощью цепочки инструментов LLVM вместо GCC, ситуация довольно похожа. Это сделано для того, чтобы все было совместимо с GCC. Так называемая точка входа в вашем файле hello.ll - это просто функция C main, а не точка входа real вашей программы. Это все еще обеспечивается заглушкой crt1.o.

Теперь, если мы (наконец) перейдем от разговора о 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
$ 

но это main не ваш Haskell main. Эта main является функцией C main в программе, динамически создаваемой GHC во время соединения. Вы можете посмотреть на такую ​​программу, запустив:

$ 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. Вышеуказанная функция * 1082 (еще одна точка входа!) Является основной точкой входа в среду выполнения 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, которую вы могли бы каким-то образом запустить непосредственно, перейдя к ее точке входа, то у меня для вас есть плохие новости. ..;)

...