assembly - write - nasm tutorial



Como funciona o $ no NASM, exatamente? (1)

message db "Enter a digit ", 0xA,0xD
Length equ $- message

É usado para obter o tamanho de uma string?
Como funciona internamente?


$ é o endereço da posição atual antes de emitir os bytes (se houver) para a linha em que aparece. A seção 3.5 do manual não entra em muitos detalhes.

$ - msg é como fazer here - msg , isto é, a distância em bytes entre a posição atual (no final da string) e o início da string. ( Veja também este tutorial sobre rótulos e diretivas da NASM, como resb )

(Relacionado: A maioria dos outros assemblies x86 também usa $ da mesma maneira, exceto para o GAS que usa . ( Period ). O montador MMIX usa @ , que tem o significado semântico correto).

Para entender melhor, pode ajudar a ver o que acontece quando você erra: .com/questions/26897633/… . Essa pessoa usou

HELLO_MSG db 'Hello, World!',0
GOODBYE_MSG db 'Goodbye!',0

hlen equ $ - HELLO_MSG
glen equ $ - GOODBYE_MSG

resultando em hlen incluindo o comprimento de ambas as strings.

EQU avalia o lado direito imediatamente, para um valor constante. (Em alguns assemblers como o FASM, equ é uma substituição de texto e você tem que usar glen = $ - GOODBYE_MSG para avaliar com $ nessa posição, em vez de avaliar $ em uma instrução posterior mov ecx, glen ou algo assim. o local; use %define para substituições de texto)

Usar $ é exatamente o equivalente a colocar um rótulo no início da linha e usá-lo em vez de $ .

O exemplo do tamanho do objeto também pode ser feito usando rótulos regulares:

msg:   db "Enter a digit "
msgend: 
Length equ msgend - msg
Length2 equ $ - msg     ; Length2 = Length

newline: db 0xA,0xD
Length3 equ $ - msg     ; Length3 includes the \n\r LF CR sequence as well.
                        ; sometimes that *is* what you want

Você pode colocar Length equ msgend - msg qualquer lugar, ou mov ecx, msgend - msg diretamente. (Às vezes é útil ter um rótulo no final de algo, por exemplo, cmp rsi, msgend / jb .loop na parte inferior de um loop.

BTW, geralmente é CR LF, não LF CR.

Exemplos menos óbvios:

times 4  dd $

monta o mesmo que isso (mas sem criar uma entrada de tabela de símbolos ou entrar em conflito com um nome existente):

here:    times 4 dd here

Em times 4 dd $ , $ não atualiza para seu próprio endereço para cada dword, ainda é o endereço do início da linha. (Experimente em um arquivo por si só e faça um hexdump no binário plano: são todos zeros.)

Mas um bloco %rep é expandido antes de $ , então

%rep 4
    dd $
%endrep

produz 0, 4, 8, 12 (a partir de uma posição de saída de 0 em um binário simples para este exemplo).

$ nasm -o foo  rep.asm  && hd foo
00000000  00 00 00 00 04 00 00 00  08 00 00 00 0c 00 00 00  

Codificando manualmente os deslocamentos de salto:

Uma call direta normal é E8 rel32 , com o deslocamento calculado em relação ao final da instrução. (ie em relação ao EIP / RIP enquanto a instrução está em execução, porque o RIP mantém o endereço da próxima instrução. Os modos de endereçamento relativos ao RIP funcionam dessa maneira também.) Um dword é de 4 bytes, portanto em uma pseudo-instrução dd com um operando, o endereço do final é $+4 . Você poderia, é claro, colocar um rótulo na próxima linha e usá-lo.

earlyfunc:           ; before the call
    call func        ; let NASM calculate the offset
    db  0xE8
    dd  func - ($ + 4)       ; or do it ourselves
    db  0xE8
    dd  earlyfunc - ($ + 4)  ; and it still works for negative offsets

    ...

func:                ; after the call

saída de desmontagem (de objdump -drwC -Mintel ):

0000000000400080 <earlyfunc>:
  400080:       e8 34 00 00 00          call   4000b9 <func>    # encoded by NASM
  400085:       e8 2f 00 00 00          call   4000b9 <func>    # encoded manually
  40008a:       e8 f1 ff ff ff          call   400080 <earlyfunc>  # and backwards works too.

Se você errar o offset, o objdump irá colocar a parte simbólica como func+8 , por exemplo. O deslocamento relativo nas primeiras 2 instruções de chamada difere em 5 porque a call rel32 tem 5 bytes de comprimento e eles têm o mesmo destino real, não o mesmo deslocamento relativo. Observe que o desmontador se encarrega de adicionar o rel32 ao endereço das instruções de chamada para mostrar endereços de destino absolutos.

Você pode usar o db target - ($+1) para codificar o deslocamento para um jmp curto ou jcc . (Mas cuidado: db 0xEB, target - ($+1) não está certo, porque o final da instrução é $+2 quando você coloca o opcode e o deslocamento como múltiplos argumentos para a mesma pseudo-instrução db .)

Relacionado: $$ é o começo da seção atual , então $ - $$ é o quão longe você está na seção atual. Mas isso é somente dentro do arquivo atual, então ligar dois arquivos que colocam coisas em .rodata é diferente de ter dois blocos section .rodata no mesmo arquivo de origem. Veja qual é o significado real de $$ em nasm .

De longe, o uso mais comum é o times 510-($-$$) db 0 / dw 0xAA55 para preencher (com db 0 ) um setor de inicialização até 510 bytes e, em seguida, adicionar a assinatura do setor de inicialização para criar 512 bytes. ( O manual do NASM explica como isso funciona )





nasm