Ошибка недопустимой инструкции в AArch64 Linux с использованием AsmJit для довольно простого кода на C++

Я пытаюсь динамически генерировать и выполнять ассемблерный код ARM64, используя GCC и библиотеку AsmJit в системе Linux aarch64. Я постоянно сталкиваюсь с ошибкой «недопустимой инструкции» даже в самых простых программах. Вот пример кода, вызывающего проблему:

#include <stdio.h>
#include "asmjit.h"
#include "a64.h"
using namespace asmjit;

typedef int (*SumFunc)(int, int);

int main() {
  // Signature of the generated function.

  for (int nop_num = 0; nop_num < 10; nop_num += 1) {
    JitRuntime rt;    // Create a runtime specialized for JIT.
    CodeHolder code;  // Create a CodeHolder.

    code.init(
        rt.environment());    // Initialize code to match the JIT environment.
    a64::Assembler a(&code);  // Create and attach a64::Assembler to code.

    // a64::Gp s1 = a64::w0;
    // a64::Gp s2 = a64::w1;
    // a64::Gp result = a64::w11;
    a.add(a64::w0, a64::w0, a64::w1);
    a.ret(a64::w0);

    SumFunc fn;
    Error err = rt.add(&fn, &code);  // Add the generated code to the runtime.
    if (err) return 1;  // Handle a possible error returned by AsmJit.
    int final_result = fn(10, 20);  // Execute our constructed function fn
    printf("%d", final_result);
    rt.release(fn);  // Explicitly remove the function from the runtime
    code.reset();
  }
  return 0;
}

Почему даже этот простой ассемблерный код ARM64 вызывает ошибку «недопустимой инструкции» в моей системе Linux aarch64? Нужно ли мне настраивать или изменять какие-либо настройки в AsmJit, чтобы адаптировать его к архитектуре aarch64?

Шаги, которые я пробовал Я подтвердил, что моя система имеет архитектуру aarch64 и что настройки компилятора GCC верны. Я попробовал другие фрагменты кода из примеров AsmJit и столкнулся с той же проблемой. Я просмотрел документацию AsmJit на предмет соответствующих параметров конфигурации или известных проблем совместимости, но не нашел никаких явных указаний. Я надеюсь найти способ правильно использовать AsmJit в Linux aarch64 или понять потенциальные причины этой проблемы. Спасибо за любую помощь, которую вы можете оказать!

Инструкция ret выглядит неправильно. Его операндом должен быть регистр, содержащий обратный адрес (обычно это x30, регистр связи), но вы передаете ему w0. Вы уверены, что это правильно? Также проверьте, нужен ли какой-то барьер между генерацией кода и его выполнением.

fuz 01.05.2024 19:27
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
136
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема в вашей ret инструкции:

a.ret(a64::w0);

Это недействительно. Во-первых, потому что вы используете 32-битный регистр для указания адреса возврата (он должен быть 64-битным), а во-вторых, инструкция ret ожидает регистр, содержащий адрес, куда нужно вернуться, а не возвращаемое значение.

Вам необходимо сначала изучить соглашения о вызовах, если вы хотите сгенерировать код для AArch64. И одно замечание из моего опыта: Apple внесла некоторые изменения в соглашение о вызовах AArch64 по умолчанию, так что вам тоже придется это учитывать.

Таким образом, правильная инструкция возврата будет такой:

a.ret(a64::x30)

Возможно, вас смутил инструмент компилятора AsmJit, который обычно использует ret() в качестве встроенной функции, но здесь вы используете напрямую Ассемблер, который не предоставляет никаких встроенных функций (что вы генерируете, то и получаете).

Кстати: в документации AsmJit есть раздел рекомендаций (https://asmjit.com/doc/index.html), в котором предлагается использовать Logger и ErrorHandler. Когда вы выдаете недопустимую инструкцию, обработчик ошибок будет ее правильно отформатировать, так что вы точно будете знать, что это такое, а если вы поставите точку останова в свой обработчик ошибок, вы будете точно знать, какая часть вашего кода вызвала ошибку. В AsmJit имеется множество инструментов, которые могут значительно облегчить вашу жизнь при их использовании.

ret w0 даже не кодируется (ret может принимать только 64-битный регистровый операнд), поэтому мне интересно, что же было сгенерировано. Если бы это было ret x0, это не должно было привести к сбою при использовании SIGILL, поскольку это действительная инструкция; он просто разветвился бы на абсолютный адрес 30, который, как я предполагаю, был бы просто SIGSEGV, поскольку это должна быть несопоставленная страница.
Nate Eldredge 01.05.2024 20:57

Ох, испустило udf; или же он ничего не излучал и выполнял нули, оставшиеся в неинициализированном пространстве. Было бы лучше, если бы ассемблер сообщал об ошибке при указании недопустимого операнда вместо того, чтобы собирать мусор или ничего.

Nate Eldredge 01.05.2024 21:16

Ассемблер выдает ошибку kErrorInvalidInstruction и отказывается кодировать инструкцию. Вот почему я упомянул об использовании регистратора и обработчика ошибок — я не думаю, что люди захотят проверять возвращаемое значение каждой «испускающей» функции, поэтому существует обработчик ошибок, который может быть запущен, когда происходит что-то подобное.

Petr 02.05.2024 22:59

После использования оператора a.ret(a64::x30); я получил правильный результат. Следует отметить, что возвращаемое значение должно быть заранее записано в регистр x0. Спасибо за ответ на этот вопрос для начинающих.

Chales Guo 04.05.2024 08:46

@ChalesGuo Я не пытаюсь документировать соглашения о вызове функций в AsmJit, поскольку это указано в другом месте. AsmJit также предоставляет API-интерфейс функций, который используется его компилятором и может также использоваться пользователями Assembler/Builder - он понимает соглашения о вызовах и может генерировать для вас пролог/эпилог - однако возвращаемое значение всегда должно обрабатываться пользователем. в любом случае.

Petr 04.05.2024 11:03

Другие вопросы по теме