← На главную

Код на ассемблере Apple Silicon не работает на других ARM — макросы решают

31.05.2026 17:59 · hackernews

Рост популярности Apple Silicon привёл к неожиданной проблеме: разработчики пишут ассемблерный код под 64-битную ARM, который отлично работает на Mac, но ломается на всех остальных ARM-устройствах — одноплатниках, серверах под Linux или BSD. Всё из-за того, что они затачивают код под Darwin и Mach-O, забывая про существование ELF.

На самом деле написать переносимый код несложно. Нужно лишь помнить о нескольких различиях между Mach-O и ELF ABI и обходить Apple-специфичные расширения синтаксиса.

Первое и главное: в Mach-O все символы получают префикс с подчёркиванием. Функция unmask в коде на C в ассемблере под Darwin превращается в _unmask. Второе — ELF различает типы данных (STT_FUNC, STT_OBJECT), а в Mach-O такого нет. Директива .type для ELF-целей там не поддерживается.

Есть и мелкие отличия в Platform ABI. Например, регистр x18 зарезервирован в Darwin (и обнуляется при переключении контекста), зарезервирован на Android, но свободно используется в GNU/Linux и Alpine. Если ваш код трогает x18 — он упадёт на Mac.

Вторая крупная ловушка — Apple-специфичные мнемоники для NEON. Они упрощают запись инструкций, но несовместимы с официальным синтаксисом ARM. Вместо короткой записи нужно указывать layout памяти для каждого регистра: eor v2.16b, v2.16b, v0.16b.

Рецепт переносимости прост. Для ABI-деталей — два макроса. Первый прячет подчёркивание для Darwin:

#ifdef __APPLE__ # define PROC_NAME(__proc) _ ## __proc #else # define PROC_NAME(__proc) __proc #endif

Второй макрос опционален — он добавляет .type для ELF, а под clang (где его нет) превращается в пустышку:

#ifdef __clang__ # define TYPE(__proc, __typ) #else # define TYPE(__proc, __typ) .type __proc, __typ #endif

Дальше код пишется как обычно, но с этими макросами:

.global PROC_NAME(unmask) .align 2 TYPE(unmask, @function) PROC_NAME(unmask):

Для NEON — просто используйте синтаксис из официального ARM-мануала, а не Apple-сокращения. Следуя этим правилам, вы получите ассемблер, который собирается под любой UNIX на 64-битной ARM.

Читать оригинал →