TL: DR: используйте gcc -m32
.
.code32
делает not изменение формата выходного файла, и именно это определяет режим, в котором будет работать ваша программа. вам не нужно запускать 32-битный код в режиме 64 бит. .code32
предназначен для сборки «чужого» машинного кода, который может потребоваться в качестве данных, или для экспорта в сегменте общей памяти. Если это не то, что вы делаете, избегайте его, чтобы получить ошибки времени сборки, когда вы создаете .S
в неправильном режиме, если у него есть, например, команды push
или pop
.
Предложение: используйте расширение .S
для рукописного ассемблера. (gcc foo.S
будет запускать его через препроцессор C до as
, поэтому вы можете #include
заголовок с номерами системного вызова, например). Кроме того, он отличает его от выхода компилятора .s
(от gcc foo.c -O3 -S
).
gcc -g foo.S -o foo -m32 -nostdlib -static # static binary with absolutely no libraries or startup code
# -nostdlib by itself makes static executables on Linux, but not OS X.
gcc -g foo.S -o foo -m32 # dynamic binary including the startup boilerplate code. Use with code that defines a main() but not a _start
Документация для nostdlib
, -nostartfiles
и -static
.
_start
(см. конец этого ответа для примера) Некоторые функции, такие как функции malloc(3)
или stdio, включая printf(3)
, зависят от инициализации некоторых глобальных данных (например, FILE *stdout
и объекта, на который он фактически указывает).
gcc -nostartfiles
оставляет из кода шаблона CRT _start
, но все же ссылки libc
(динамически, по умолчанию). В Linux общие библиотеки могут иметь разделы инициализатора, которые запускаются динамическим компоновщиком, когда он загружает их, прежде чем переходить к вашей точке входа _start
. Таким образом, gcc -nostartfiles hello.S
все еще позволяет вам называть printf
. Для динамического исполняемого файла ядро запускает на нем /lib/ld-linux.so.2
вместо его непосредственного использования (используйте readelf -a
, чтобы увидеть строку «ELF интерпретатор» в вашем двоичном файле). Когда ваш _start
в конечном итоге будет запущен, не все регистры будут обнулены, потому что динамический компоновщик выполнил код в вашем процессе.
Однако gcc -nostartfiles -static hello.S
свяжется, но сбой во время выполнения, если вы вызываете printf
или что-то, не вызывая внутренние функции init glibc. (см. комментарий Майкла Пётча).
Конечно, вы можете поместить любую комбинацию файлов .c
, .S
и .o
в одну и ту же командную строку, чтобы связать их все в один исполняемый файл , Если у вас есть C, не забудьте -Og -Wall -Wextra
: вы не хотите отлаживать ваш asm, когда проблема была чем-то простым в C, который вызывает это, о котором мог бы предупредить компилятор.
Используйте -v
, чтобы gcc показывал вам команды, которые он запускает для сборки и соединения. Для этого «вручную»:
as foo.S -o foo.o -g --32 && # skips the preprocessor
ld -o foo foo.o -m elf_i386
file foo
foo: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped
gcc -nostdlib -m32
легче запомнить и ввести, чем два разных варианта для as и ld (--32
и -m elf_i386
). Кроме того, он работает на всех платформах, включая те, где исполняемый формат не является ELF. (Но примеры Linux не будут работать в OS X, потому что номера системных вызовов различны или в Windows, потому что в нем даже не используется int 0x80
ABI.)
gcc не может обрабатывать синтаксис NASM. (-masm=intel
больше похож на MASM, чем на синтаксис NASM, где вам нужно offset symbol
, чтобы получить адрес как немедленный). И, конечно, директивы различны (например, .globl
vs global
).
Вы можете построить с помощью nasm
или yasm
, затем свяжите .o
с gcc
, как указано выше, или ld
напрямую.
Я использую сценарий оболочки, чтобы избежать повторного ввода одного и того же имени файла с тремя разными расширениями. (nasm и yasm по умолчанию file.asm
-> file.o
, в отличие от вывода GNU as по умолчанию a.out
). Используйте это с помощью -m32
, чтобы собрать и связать 32-разрядные исполняемые файлы ELF. Не все ОС используют ELF, поэтому этот скрипт менее переносим, чем использование gcc -nostdlib -m32
для ссылки будет ..
#!/bin/sh
# usage: asm-link [-q] [-m32] foo.asm [assembler options ...]
# Just use a Makefile for anything non-trivial. This script is intentionally minimal and doesn't handle multiple source files
verbose=1 # defaults
fmt=-felf64
#ldopt=-melf_i386
while getopts 'm:vq' opt; do
case "$opt" in
m) if [ "m$OPTARG" = "m32" ]; then
fmt=-felf32
ldopt=-melf_i386
fi
if [ "m$OPTARG" = "mx32" ]; then
fmt=-felfx32
ldopt=-melf32_x86_64
fi
# default is -m64
;;
q) verbose=0 ;;
v) verbose=1 ;;
esac
done
shift "$((OPTIND-1))" # Shift off the options and optional --
src=$1
base=${src%.*}
shift
[ "$verbose" = 1 ] && set -x # print commands as they're run, like make
#yasm "$fmt" -Worphan-labels -gdwarf2 "$src" "$@" &&
nasm "$fmt" -Worphan-labels -g -Fdwarf "$src" "$@" &&
ld $ldopt -o "$base" "$base.o"
# yasm -gdwarf2 includes even .local labels so they show up in objdump output
# nasm defaults to that behaviour of including even .local labels
# nasm defaults to STABS debugging format, but -g is not the default
Я предпочитаю yasm по нескольким причинам, в том числе по умолчанию для создания long- nop
s вместо заполнения со многими однобайтными nop
s. Это приводит к беспорядочному выводу разборки, а также к замедлению, если nops когда-либо выполняются. (В NASM вам необходимо использовать макрос smartalign
.)
# hello32.S
#include // syscall numbers. only #defines, no C declarations left after CPP to cause asm syntax errors
.text
#.global main # uncomment these to let this code work as _start, or as main called by glibc _start
#main:
#.weak _start
.global _start
_start:
mov $__NR_gettimeofday, %eax # make a syscall that we can see in strace output so we know when we get here
int $0x80
push %esp
push $print_fmt
call printf
#xor %ebx,%ebx # _exit(0)
#mov $__NR_exit_group, %eax # same as glibc's _exit(2) wrapper
#int $0x80 # won't flush the stdio buffer
movl $0, (%esp) # reuse the stack slots we set up for printf, instead of popping
call exit # exit(3) does an fflush and other cleanup
#add $8, %esp # pop the space reserved by the two pushes
#ret # only works in main, not _start
.section .rodata
print_fmt: .asciz "Hello, World!\n%%esp at startup = %#lx\n"
$ gcc -m32 -nostdlib hello32.S
/tmp/ccHNGx24.o: In function `_start':
(.text+0x7): undefined reference to `printf'
...
$ gcc -m32 hello32.S
/tmp/ccQ4SOR8.o: In function `_start':
(.text+0x0): multiple definition of `_start'
...
Не работает во время выполнения, потому что ничего не вызывает функции glibc init. (__libc_init_first
, __dl_tls_setup
и __libc_csu_init
в этом порядке, в соответствии с комментарием Майкла Пётча. Существуют и другие реализации libc
, в том числе MUSL , который предназначен для статической компоновки и работает без вызовов инициализации .)
$ gcc -m32 -nostartfiles -static hello32.S # fails at run-time
$ file a.out
a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, BuildID[sha1]=ef4b74b1c29618d89ad60dbc6f9517d7cdec3236, not stripped
$ strace -s128 ./a.out
execve("./a.out", ["./a.out"], [/* 70 vars */]) = 0
[ Process PID=29681 runs in 32 bit mode. ]
gettimeofday(NULL, NULL) = 0
--- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)
Вы также можете gdb ./a.out
и запустить b _start
, layout reg
, run
и посмотреть, что произойдет.
$ gcc -m32 -nostartfiles hello32.S # Correct command line
$ file a.out
a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=7b0a731f9b24a77bee41c13ec562ba2a459d91c7, not stripped
$ ./a.out
Hello, World!
%esp at startup = 0xffdf7460
$ ltrace -s128 ./a.out > /dev/null
printf("Hello, World!\n%%esp at startup = %#lx\n", 0xff937510) = 43 # note the different address: Address-space layout randomization at work
exit(0
+++ exited (status 0) +++
$ strace -s128 ./a.out > /dev/null # redirect stdout so we don't see a mix of normal output and trace output
execve("./a.out", ["./a.out"], [/* 70 vars */]) = 0
[ Process PID=29729 runs in 32 bit mode. ]
brk(0) = 0x834e000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
.... more syscalls from dynamic linker code
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
mmap2(NULL, 1814236, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xfffffffff7556000 # map the executable text section of the library
... more stuff
# end of dynamic linker's code, finally jumps to our _start
gettimeofday({1461874556, 431117}, NULL) = 0
fstat64(1, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0 # stdio is figuring out whether stdout is a terminal or not
ioctl(1, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0xff938870) = -1 ENOTTY (Inappropriate ioctl for device)
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xfffffffff7743000 # 4k buffer for stdout
write(1, "Hello, World!\n%esp at startup = 0xff938fb0\n", 43) = 43
exit_group(0) = ?
+++ exited with 0 +++
Если бы мы использовали _exit(0)
или сделали систему sys_exit
называть себя int 0x80
, , то write(2)
не было бы . Если stdout перенаправлен на не-tty, по умолчанию он заполняется с полным буфером (не с буферизацией строки), поэтому write(2)
запускается только fflush(3)
как часть exit(3)
. Без перенаправления вызов printf(3)
со строкой, содержащей символы новой строки, будет немедленно скрыт.
Поведение в зависимости от того, является ли stdout терминалом, может быть желательно, но только если вы делаете это специально, а не по ошибке.
Если это, 'Какой язык работает лучше на CLR', затем прямо сейчас, IronPython без труда побеждает.
В течение длительного срока, хотя, 'какой язык будет учить меня больше и служить мне лучше в моей карьере как программист', определенно сказал бы я IronRuby (это будет верно для CPython по сравнению с CRuby также),
Ruby подвергнет Вас большему количеству 'понятий', чем Python делает, из-за него являющийся более либеральным в том, как он обрабатывает вещи как функции лямбды, блоки кода, оценка, и так далее.
Так или иначе это, вероятно, собирается убывать в войну пламени.Прошу прощения
Не входя в относительные достоинства языков (который был бы всем конкурсом pissing сам по себе), IronPython (стабильные 1.1.1, бета 2.0) далее приезжает в разработке, чем IronRuby (альфа)
Какой бы ни Вы выбираете, смотрите на IronEditor - это является большим для проигрывания вокруг. Я в настоящее время использую его, чтобы попытаться взять те языки сам...
Как другой сказанный, IronPython является более стабильным и сформировавшимся, и можно найти больше образцов и много обычного кода Python, который Вы сможете испытать.
IronRuby является большим, но остерегайтесь: для попытки его, необходимо разобраться в коде от SVN и скомпилировать его собой. Или можно загрузить Динамическую Silverlight SDK и получить его тот путь, но необходимо будет потратить когда-то, изучая эту среду также.
Я просто хочу упомянуть, что существует также версия ДОЛЛАРА JavaScript (JScript), который является моим персональным fav. Если бы Вы ищете новый язык для изучения для использования доллара, я предложил бы ironpython, как упомянуто, это дальше приезжает в долларе. Python также довольно популярен вне доллара для программирования gui и предлагает Django для mvc веб-приложений.
Это чисто субъективно, но я думаю, что рубиновая популярность уменьшается немного. В конечном счете я чувствую, что это будет похоже на жемчуг, который используемый и уважают, но капля в море по сравнению с другими опциями. Мне, оказывается, действительно нравится рубин (и жемчуг), но не предложил бы это в качестве нового пути для Вашей намеченной цели.
Я пошел бы для IronPython в данный момент, так как существует много книг/информации вокруг для него (IronPython В Действии просто становится законченным).
Я лично изучил бы IronRuby, потому что я предпочитаю способ, которым Ruby работает по Python и так как это - одно из того же, затем выбирают, какой язык Вы предпочитаете.