Подтвердить что ты не робот

Что такое 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
4b9b3361

Ответ 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.

Надеюсь, что это поможет.