Как я могу запустить этот код сборки на OS X?
Начав изучать ассемблер, мне дали код ассемблера Hello World, созданный во время урока в Linux. Я хотел бы заставить его работать на 64-битной Mac OS X.
code.asm:
SECTION .data
hola: db "Hola!",10
tam: equ $-hola
SECTION .text
global main
main:
mov edx,tam
mov ecx,hola
mov ebx,1
mov eax,4
int 0x80
mov ebx,0
mov eax,1
int 0x80
Вот что я делаю:
nasm -f macho32 -o object.o code.asm
gcc -m32 -o program object.o
Что говорит мне:
Неопределенные символы для архитектуры i386: "_main", на которую ссылаются из: start в crt1.10.6.o ld: символы не найдены для архитектуры i386
В поисках этой ошибки я нашел этот вопрос: nasm и gcc: 32-битное соединение не удалось (64-битная Mac OS X)
Один ответ говорит
Проблема в том, что вы создаете 32-битный объектный файл Linux(ELF), который не совместим с форматом объектов Mac OS X. Попробуйте переключить '-f elf' на '-f macho32'.
Но я определенно использую -f macho32
, Так в чем же тогда проблема?
3 ответа
Я тоже пытался научиться программированию на ассемблере начального уровня и столкнулся с похожими проблемами. Я изначально скомпилировал используя nasm
с elf
, но это не сработало, когда я пытался использовать ld
связать объектный файл и создать исполняемый файл.
Я думаю, что ответ на главный вопрос "what would the problem be then?" [to get this to run on 64bit MacOSX]
это: вы используете -f macho32
но ожидая, что он будет работать на 64-битной машине, вам нужно изменить параметр команды, чтобы -f macho64
, Конечно, это не решит тот факт, что ваш ассемблерный код написан для другой архитектуры (подробнее об этом чуть ниже).
Я нашел этот удобный ответ для правильной команды, которая будет использоваться в этом случае для компиляции и компоновки вашего кода (после того, как вы измените код ассемблера, чтобы использовать правильный синтаксис вместо * nix, как указано в duskwuff): nasm -f macho64 main.asm -o main.o && ld -e _main -macosx_version_min 10.8 -arch x86_64 main.o -lSystem
После некоторых поисков вот что я узнал...
- На Mac 64bit лучше использовать
as
ассемблер вместоnasm
(если вы хотите что-то более нативное), но если вы хотите более переносимый код (изучите различия). nasm
не поставляется с установленным по умолчанию типом вывода macho64- Сборка - это боль в кейстере (это в стороне)
Теперь, когда моя учебная напыщенная речь ушла с пути...
Вот код, который должен работать на MacOSX 64, используя nasm
(если вы обновили nasm
с macho64
, кредит Дастину Шульцу):
section .data
hello_world db "Hello World!", 0x0a
section .text
global start
start:
mov rax, 0x2000004 ; System call write = 4
mov rdi, 1 ; Write to standard out = 1
mov rsi, hello_world ; The address of hello_world string
mov rdx, 14 ; The size to write
syscall ; Invoke the kernel
mov rax, 0x2000001 ; System call number for exit = 1
mov rdi, 0 ; Exit success = 0
syscall ; Invoke the kernel
Рабочий код, который я использовал с as
родной ассемблер для MacOSX64:
.section __TEXT,__text
.global start
start:
movl $0x2000004, %eax # Preparing syscall 4
movl $1, %edi # stdout file descriptor = 1
movq str@GOTPCREL(%rip), %rsi # The string to print
movq $100, %rdx # The size of the value to print
syscall
movl $0, %ebx
movl $0x2000001, %eax # exit 0
syscall
.section __DATA,__data
str:
.asciz "Hello World!\n"
Команда компиляции: as -arch x86_64 -o hello_as_64.o hello_as_64.asm
Команда Link: ld -o hello_as_64 hello_as_64.o
Выполнить команду: ./hello_as_64
Некоторые полезные ресурсы, которые я нашел в своем путешествии:
AS
Справочник по сборке OSX: https://developer.apple.com/library/mac/documentation/DeveloperTools/Reference/Assembler/Assembler.pdf
Написание 64-битной сборки на Mac OSX: http://www.idryman.org/blog/2014/12/02/writing-64-bit-assembly-on-mac-os-x/
Не удалось связать объектный файл с помощью ld
: Невозможно связать объектный файл с помощью ld - Mac OS X
OSX i386 SysCalls: http://www.opensource.apple.com/source/xnu/xnu-1699.26.8/osfmk/mach/i386/syscall_sw.h
Основные определения системных вызовов OSX: http://www.opensource.apple.com/source/xnu/xnu-1504.3.12/bsd/kern/syscalls.master
Системный вызов OSX: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/syscall.2.html
Вам необходимо:
Измените название метки с
main
в_main
(в обоих местах). Именование символов работает немного по-другому в Mac OS X.Измените способ передачи аргументов системному вызову. Mac OS X использует другое соглашение о вызовах для ядра из Linux; этот код не является переносимым! Я не знаю, так как есть какая-либо официальная документация о том, как это работает, но, глядя на разборку в GDB для стандартной функции библиотеки, такой как
_exit()
может быть поучительным
Вот _exit
в моей системе, например:
<_exit+0>: mov $0x40001,%eax
<_exit+5>: call 0x96f124c2 <_sysenter_trap>
<_exit+10>: jae 0x96f10086 <_exit+26>
<_exit+12>: call 0x96f1007d <_exit+17>
<_exit+17>: pop %edx
<_exit+18>: mov 0x15a3bf9f(%edx),%edx
<_exit+24>: jmp *%edx
<_exit+26>: ret
<_exit+27>: nop
Дополнительный бит установлен в 0x40001
это... странно, но здесь можно смело игнорировать.
Материал после звонка _sysenter_trap
для обработки ошибок.
_sysenter_trap
является:
<_sysenter_trap+0>: pop %edx
<_sysenter_trap+1>: mov %esp,%ecx
<_sysenter_trap+3>: sysenter
<_sysenter_trap+5>: nop
Учитывая все вышесказанное, вам, вероятно, лучше связываться с libSystem (эквивалентом libc для OS X), а не пытаться напрямую вызывать ядро.
Я написал сообщение в блоге на эту тему: https://cs-flex.hashnode.dev/linux-assembly-on-macos .
У вас есть 3 основных варианта:
- ВМ -- не рекомендую
- Аренда сервера, неплохой вариант, если не жалко платить ~20-30$ в месяц
- (мой личный лучший вариант), используя для создания
Linux
контейнер, который разделяет папку (том) и запускает там ассемблер. Если вы раньше не использовали Docker — я все равно считаю этот вариант лучшим.
Вы можете прочитать подробности в моем блоге (особенно если вы не использовалиDocker
до). Но вкратце, все, что вам нужно, это два файла:
# Dockerfile
FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -y gcc
RUN apt-get install -y make
# docker-compose.yml
version: "3"
services:
linux:
image: linux-image
container_name: linux-container
build:
context: .
command: sleep 1000
volumes:
- .:/code
Вы сможете запустить контейнер и подключиться к нему через
docker-compose up # build and run docker container
docker exec -it linux-container bash # "ssh" into container
после этого весь ваш код в папке с файлами докеров будет "привязан" к папке/code/
внутри Докера. Поэтому вы можете выполнить его внутри контейнера докеров, как если бы вы работали под управлением Linux.