Программа может определять кучу меток. Некоторые из этих меток представляют функции, некоторые используются внутри функций. Тем не менее нужно учитывать, что в программе может быть только одна метка с определенным именем. Например, пусть у нас есть следующая программа на Linux:
global _start section .data nums dq 5, 6, 7, 8, 1, 2, 3, 4 numSize equ 8 ; размер одного числа count equ ($-nums)/numSize ; количество чисел section .text _start: mov rdi, nums ; в RDI - адрес массива mov rsi, count ; в RSI - количество элементов call max mov rdi, rax ; в RDI из RAX количество выведенных на консоль байтов mov rax, 60 syscall ; Функция min возвращает минимальное значение в массиве ; RDI - ссылка на массив ; RSI - количество элементов в массиве ; RAX - результат функции - наименьшее значение. При 0 элементов результат неопределен ; выполняем (RSI-1) сравнений RAX с каждым элементом кроме первого min: mov rax, [rdi] ; RAX по умолчанию указывает на первый элемент jmp condition ; переходим к проверке условия while: dec rsi ; уменьшаем счетчик количества элементов массива cmp rax, [rdi + rsi* numSize] ; сравниваем rax с элементами, начиная с последних jb condition ; если RAX меньше, переходим к проверке условия mov rax, [rdi + rsi*numSize] ; если RAX больше, то помещаем меньшее число в RAX condition: cmp rsi, 1 ; если элементов больше 1 ja while ; то выполняем цикл ret ; Функция max возвращает максимальное значение в массиве ; RDI - ссылка на массив ; RSI - количество элементов в массиве ; RAX - результат функции - наибольшее значение. При 0 элементов результат неопределен ; выполняем (RSI-1) сравнений RAX с каждым элементом кроме первого max: mov rax, [rdi] ; RAX по умолчанию указывает на первый элемент jmp condition ; переходим к проверке условия while: dec rsi ; уменьшаем счетчик количества элементов массива cmp rax, [rdi + rsi* numSize] ; сравниваем rax с элементами, начиная с последних ja condition ; если RAX больше, переходим к проверке условия mov rax, [rdi + rsi*numSize] ; если RAX меньше, то помещаем большее число в RAX condition: cmp rsi, 1 ; если элементов больше 1 ja while ; то выполняем цикл ret
Здесь определены однотипные функции min и max, которые вычисляют соответственно минимальное и максимальное значенние из некоторого массива. Внутри каждой из функций применяются две метки - while и condition, которые управляют циклом. В цикле мы проходим последовательно по всем элементам массива, начиная с последнего, и сравниваем их со значением в регистре RAX. Однако проблема этой программы в том, что названия меток дублируются. Поэтому при компиляции мы получим ошибку:
root@Eugene:~/asm# nasm -f elf64 hello.asm -o hello.o hello.asm:44: error: label `while' inconsistently redefined hello.asm:26: info: label `while' originally defined here hello.asm:49: error: label `condition' inconsistently redefined hello.asm:31: info: label `condition' originally defined here
Мы можем выйти из ситуации, изменив названия меток, чтобы они отличались. Однако, возможно, мы захотим оставить названия меток, учитывая, что здесь однотипные функции. А если программа большая, управлять метками становится гораздо сложнее. И NASM предоставляет решение - локальные метки. Локальные метки по сути те же метки, только перед их названием ставится точка, например:
.while: .condition:
Локальные метки доступны только в рамках кода глобальной метки, которая определена перед локальными метками. Например:
func1: .label1: func2: .label2:
Здесь метка ".label1" определена в рамках кода метки "func1" (метку "func1" можно назвать "глобальной"), поэтому ".label1" доступна в рамках кода метки "func1", но недоступна в рамках кода метки "func2".
Так, изменим предыдущую программу, применив локальные метки:
global _start section .data nums dq 5, 6, 7, 8, 1, 2, 3, 4 numSize equ 8 ; размер одного числа count equ ($-nums)/numSize ; количество чисел section .text _start: mov rdi, nums ; в RDI - адрес массива mov rsi, count ; в RSI - количество элементов call max mov rdi, rax ; в RDI из RAX количество выведенных на консоль байтов mov rax, 60 syscall ; Функция min возвращает минимальное значение в массиве ; RDI - ссылка на массив ; RSI - количество элементов в массиве ; RAX - результат функции - наименьшее значение. При 0 элементов результат неопределен ; выполняем (RSI-1) сравнений RAX с каждым элементом кроме первого min: mov rax, [rdi] ; RAX по умолчанию указывает на первый элемент jmp .condition ; переходим к проверке условия .while: dec rsi ; уменьшаем счетчик количества элементов массива cmp rax, [rdi + rsi* numSize] ; сравниваем rax с элементами, начиная с последних jb .condition ; если RAX меньше, переходим к проверке условия mov rax, [rdi + rsi*numSize] ; если RAX больше, то помещаем меньшее число в RAX .condition: cmp rsi, 1 ; если элементов больше 1 ja .while ; то выполняем цикл ret ; Функция max возвращает максимальное значение в массиве ; RDI - ссылка на массив ; RSI - количество элементов в массиве ; RAX - результат функции - наибольшее значение. При 0 элементов результат неопределен ; выполняем (RSI-1) сравнений RAX с каждым элементом кроме первого max: mov rax, [rdi] ; RAX по умолчанию указывает на первый элемент jmp .condition ; переходим к проверке условия .while: dec rsi ; уменьшаем счетчик количества элементов массива cmp rax, [rdi + rsi* numSize] ; сравниваем rax с элементами, начиная с последних ja .condition ; если RAX больше, переходим к проверке условия mov rax, [rdi + rsi*numSize] ; если RAX меньше, то помещаем большее число в RAX .condition: cmp rsi, 1 ; если элементов больше 1 ja .while ; то выполняем цикл ret
Теперь метки .while
и .condition
являются локальными по отношению к меткам-функциям, в которых они объявлены. И проблем с компиляцией такой программы не возникнет.
Но стоит сказать, как NASM обарабатывает подобные метки. В реальности для NASM локальная метка идентична:
имя_глобальной_метки.имя_локальной_метки
Например, метка .while
в функции min фактически разворачивается в метку min.while
. За счет этого при компиляции и не происходит никаких ошибок. И мы даже можем
обращаться к таким меткам по их полному имени:
max: mov rax, [rdi] jmp max.condition ; переходим к локальной метке по ее полному имени .while: dec rsi cmp rax, [rdi + rsi* numSize] ja max.condition ; переходим к локальной метке по ее полному имени mov rax, [rdi + rsi*numSize] .condition: cmp rsi, 1 ja max.while ; переходим к локальной метке по ее полному имени ret