Как изменить сборку Erlang? Доступны ли какие-либо ресурсы?

#assembly #erlang

#сборка #erlang

Вопрос:

Я сомневаюсь, что кто-нибудь может помочь с этим вопросом из-за следующего в документации по компиляции Erlang:

Обратите внимание, что формат файлов ассемблера не документирован и может меняться между выпусками — этот параметр в основном предназначен для внутренней отладки.

… но на всякий случай, здесь идет трассировка стека истории:

  • скомпилируйте:file/2 с помощью [‘S’] для генерации кода сборки
  • Прочитайте файл .S и создайте структуру данных ключ-значение, в которой ключ — это кортежи «функции» в файле .S, а значение — тело функции, то есть инструкции по сборке, которые реализуют функцию.
  • Измените структуру данных, добавив сборку для выполнения вызова внешней функции в определенных функциях.
  • сбой…

К сожалению, я просто быстро просмотрел файлы .S, созданные при компиляции модуля, содержащего следующую функцию, с первым выражением в функции и без него, закомментировано:

 spawn_worker(Which) ->
    %syner:sync_pt(),
    case Which of
            ?NAIVE -> spawn(err1, naive_worker_loop, [])
    end.
 

Когда я это сделал, я подумал, что единственное, что изменилось, это кортеж:

{call_ext,0,{extfunc,syner,sync_pt,0}}.

… поэтому я предположил, что единственное, что необходимо для внедрения вызова функции в сборку, — это добавить этот кортеж… но теперь, когда я фактически ввел кортеж… Я вижу, что сгенерированная сборка содержит несколько дополнительных инструкций:

Без syner:sync_pt():

 {function, spawn_worker, 1, 4}.
{label,3}.
    {func_info,{atom,err1},{atom,spawn_worker},1}.
{label,4}.
    {test,is_eq_exact,{f,5},[{x,0},{atom,naive}]}.
    {move,{atom,naive_worker_loop},{x,1}}.
    {move,nil,{x,2}}.
    {move,{atom,err1},{x,0}}.
    {call_ext_only,3,{extfunc,erlang,spawn,3}}.
{label,5}.
    {case_end,{x,0}}.
 

С помощью syner:sync_pt():

 {function, spawn_worker, 1, 4}.
{label,3}.
    {func_info,{atom,err1},{atom,spawn_worker},1}.
{label,4}.
    {allocate,1,1}.
    {move,{x,0},{y,0}}.
    {call_ext,0,{extfunc,syner,sync_pt,0}}.
    {test,is_eq_exact,{f,5},[{y,0},{atom,naive}]}.
    {move,{atom,naive_worker_loop},{x,1}}.
    {move,nil,{x,2}}.
    {move,{atom,err1},{x,0}}.
    {call_ext_last,3,{extfunc,erlang,spawn,3},1}.
{label,5}.
    {case_end,{y,0}}.
 

Я не могу просто сделать вывод, что добавление чего-то вроде:

    {allocate,1,1}.
   {move,{x,0},{y,0}}.
   {call_ext,0,{extfunc,syner,sync_pt,0}}.
 

для каждой функции, в которую я хочу ввести вызов внешней функции, это поможет.

  1. потому что я не уверен, применим ли этот код сборки ко всем функциям, в которые я хочу внедрить (например, всегда ли {allocate,1,1} в порядке)
  2. потому что, если вы внимательно посмотрите на остальную часть сборки, она немного изменится (например, {call_ext_only,3,{extfunc,erlang,spawn,3}}. изменения в {call_ext_last,3,{extfunc,erlang,spawn,3},1} .).

Итак, теперь вопрос в том, есть ли какой-либо ресурс, который я могу использовать для понимания и управления сборкой, созданной компиляцией Erlang:file/ 2?

Я задаю этот вопрос на всякий случай. Я сомневаюсь, что для этого есть ресурс, поскольку в документации четко указано, что его нет, но, думаю, мне нечего терять. Даже если бы это было так, похоже, что манипулирование ассемблерным кодом будет сложнее, чем хотелось бы. Использование parse_transform/2 определенно проще, и мне удалось получить что-то похожее для работы с ним … просто пробуя разные альтернативы.

Спасибо за ваше время.

Комментарии:

1. (Информация из тестирования, а не из источников чтения.) {allocate,1,1} выделяет одно место в стеке y. Стек x — это «обычный» стек, регистры. Стек y является вспомогательным стеком. {move,{x,0},{y,0}} перемещает самый верхний x в самый нижний y (по крайней мере, думаю, что они растут в обратном направлении). Мой тестовый файл: pastebin.com/R21ZJ29Q . Его результат: pastebin.com/jULjMCV0 .

2. Спасибо за помощь, Кей. Похоже, что выяснение сборки займет у меня слишком много времени, поэтому я не планирую ее расшифровывать. Приветствия.

Ответ №1:

Единственный инструмент, который я знаю, который использует beam asm, — это HiPE, есть много примеров кода и тому подобное в https://github.com/erlang/otp/tree/master/lib/hipe/icode , хотя я бы не рекомендовал много работать с этим форматом, поскольку он постоянно меняется.

Комментарии:

1. Точка взята. Работа с представлением сборки вызывает проблемы. Приветствия.

Ответ №2:

Я не уверен, чего вы пытаетесь достичь с помощью этого, но core erlang может быть лучшим уровнем для выполнения манипуляций с кодом. Это задокументировано (во всяком случае, одна его версия, но это все же лучше, чем ничего) здесь (и есть еще только Google для core erlang):

Для компиляции в ядро erlang и из него используйте опции ‘to_core’ и ‘from_core’ (к сожалению, недокументированные):

 c(my_module, to_core). %%this generates my_module.core
c(my_module, from_core). %%this loads my_module.core
 

Комментарии:

1. Я думаю, что core erlang — это то, что используется lfe ( github.com/rvirding/lfe ), вы могли бы найти там какую-нибудь полезную информацию

2. хм, я быстро прочитал эту статью, но еще не пробовал использовать core Erlang. Я думаю, я рассмотрю это немного подробнее. Спасибо за указатель TamasNagy.

3. lfe использует ядро erlang да. Круто, тем больше причин проверить это. Спасибо Лукасу.

Ответ №3:

Мне показалось, что то, что вы пытаетесь сделать, можно легко решить, манипулируя абстрактным кодом. Просто напишите модуль parse_transform, в который вы могли бы вставить вызов функции в рассматриваемые функции.

Я написал на ?funclog основе erlang decorators . см https://github.com/nicoster/erl-decorator-pt#funclog