Что такое global_start на ассемблере?
Это мой код уровня сборки...
section .text
global _start
_start: mov eax, 4
mov ebx, 1
mov ecx, mesg
mov edx, size
int 0x80
exit: mov eax, 1
int 0x80
section .data
mesg db 'KingKong',0xa
size equ $-mesg
Вывод:
[email protected]:~/Arena# nasm -f elf a.asm -o a.o
[email protected]:~/Arena# ld -o out a.o
[email protected]:~/Arena# ./out
KingKong
Мой вопрос Что такое глобальный _start, используемый для? Я попробовал удачу с Mr.Google, и я обнаружил, что он используется, чтобы сообщить начальную точку моей программы. Почему мы не можем иметь _start
, чтобы указать, где начинается программа, как приведенная ниже, которая порождает
предупреждение на экране
section .text
_start: mov eax, 4
mov ebx, 1
mov ecx, mesg
mov edx, size
int 0x80
exit: mov eax, 1
int 0x80
section .data
mesg db 'KingKong',0xa
size equ $-mesg
[email protected]:~/Arena# nasm -f elf a.asm
[email protected]:~/Arena# ld -e _start -o out a.o
ld: warning: cannot find entry symbol _start; defaulting to 0000000008048080
[email protected]:~/Arena# ld -o out a.o
ld: warning: cannot find entry symbol _start; defaulting to 0000000008048080
Ответы
Ответ 1
Директива global
- это NASM. Он предназначен для экспорта символов в код, где он указывает в генерируемый объектный код. Здесь вы помечаете символ _start
global, поэтому его имя добавляется в код объекта (a.o
). Линкер (ld
) может читать этот символ в объектном коде и его значение, чтобы он знал, где отмечать как точку входа в исполняемом файле вывода. Когда вы запускаете исполняемый файл, он начинается с символа _start
в коде.
Если директива global
отсутствует для символа, этот символ не будет помещен в таблицу экспорта кода объекта, чтобы линкер не знал о символе.
Если вы хотите использовать другое имя точки входа, чем _start
(по умолчанию), вы можете указать параметр -e
для ld, например:
ld -e my_entry_point -o out a.o
Ответ 2
Ярлык не является явно глобальным, пока вы не объявите его глобальным, поэтому вам нужно использовать глобальную директиву.
Глобальный ярлык "_start" необходим компоновщику, если нет глобального адреса _start, тогда компоновщик будет жаловаться, потому что он не может найти его. Вы не объявляли _start как глобальный, поэтому он не виден вне этого модуля/объекта кода, поэтому он не отображается в компоновщике.
Это противоположность C, где все подразумевается как глобальное, если вы не объявляете их локальными
unsigned int hello;
int fun ( int a )
{
return(a+1);
}
hello и fun являются глобальными, видимыми вне объекта, но это
static unsigned int hello;
static int fun ( int a )
{
return(a+1);
}
делает их локальными не видимыми.
все локальные:
_start:
hello:
fun:
more_fun:
теперь они доступны глобально для компоновщика и других объектов
global _start
_start:
global hello
hello:
...
Ответ 3
_start
используется сценарием компоновщика по умолчанию ld
в качестве точки входа
Мы можем видеть соответствующую часть этого скрипта компоновщика с помощью:
ld -verbose a.o | grep ENTRY
который выводит:
ENTRY(_start)
Формат файла ELF (и другой объектный формат, я полагаю) явно указывает, с какого адреса будет запускаться программа, через поле заголовка e_entry
.
ENTRY(_start)
сообщает компоновщику, чтобы эта запись задала адрес символа _start
при создании файла ELF из объектных файлов.
Затем, когда ОС запускает программу (системный вызовexec
в Linux), она анализирует файл ELF, загружает исполняемый код в память и устанавливает указатель инструкции на указанный адрес.
Флаг -e
, упомянутый в Sedat, переопределяет символ _start
по умолчанию.
Вы также можете заменить весь скрипт компоновщика по умолчанию опцией -T <script>
, здесь приведен конкретный пример, который настраивает некоторые элементы сборки.
global - это директива ассемблера, которая помечает символ как глобальный в файле ELF
Файл ELF содержит некоторые метаданные для каждого символа, указывающие его видимость.
Самый простой способ убедиться в этом - инструмент nm
.
Например, в автономном мире Linux x86_64 GAS:
main.S
.text
.global _start
_start:
asm_main_after_prologue:
/* write */
mov $1, %rax /* syscall number */
mov $1, %rdi /* stdout */
lea msg(%rip), %rsi /* buffer */
mov $len, %rdx /* len */
syscall
/* exit */
mov $60, %rax /* syscall number */
mov $0, %rdi /* exit status */
syscall
msg:
.ascii "hello\n"
len = . - msg
GitHub upstream
скомпилируйте и запустите:
gcc -ffreestanding -static -nostdlib -o main.out main.S
./main.out
nm
дает:
00000000006000ac T __bss_start
00000000006000ac T _edata
00000000006000b0 T _end
0000000000400078 T _start
0000000000400078 t asm_main_after_prologue
0000000000000006 a len
00000000004000a6 t msg
и man nm
говорят нам, что:
Если строчные буквы, символ обычно является локальным; в верхнем регистре символ является глобальным (внешним).
Итак, мы видим, что _global
виден снаружи (верхний регистр T
), но msg
, который мы не пометили как .global
, не является (нижний регистр t
).
Затем компоновщик знает, как взорвать, если несколько глобальных символов с одним и тем же именем видны, или сделать более умные вещи, то есть более экзотические типы символов, видимые.
Если мы не помечаем _start
как глобальный, ld
становится грустным и говорит:
невозможно найти символ ввода _start
Ответ 4
global _start
- это просто метка, указывающая на адрес памяти. В случае _start, когда дело доходит до двоичных файлов ELF, это стандартная метка, которая действует как адрес, где запускается программа.
Существует также main
или _main
или main_
, который известен на языке C, и вызывается "кодом запуска", который обычно связан с - если вы используете C.
Надеюсь, что это поможет.