Jump to content

fredericopissarra

Membros
  • Content Count

    301
  • Joined

  • Last visited

  • Country

    Brazil

Community Reputation

247 Excellent

Personal Information

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. Você obtem o erro: Nao é? É porque 'dados' é um array. []s Fred
  2. PS: O código acima usa SysV x86-64 ABI (só funciona em 'unixes'). O do Windows é diferente (vide Addendum).
  3. Sempre topo com essa questão em foruns e mídias como o Discord: Como usar funções da biblioteca padrão de C em códigos puramente escritos em assembly? É “fácil”, mas eu não recomendo. Primeiro mostrarei como, usando o NASM e o GCC e depois digo o por quê da não recomendação. Eis um “hello, world” simples: ; test.asm ; Compile com: ; nasm -felf64 -o test.o test.asm bits 64 ; A glibc exige "RIP relative addressing". ; De qualquer maneira, isso é sempre uma boa ; prática no modo x86-64. default rel section .rodata msg: db `hello, world`,0 section .text ; Este símbolo está na glibc! extern puts global main main: ; Chama puts(), passando o ponteiro da string. lea rdi,[msg] call puts wrt ..plt ; return 0; xor eax,eax ret O código acima é diretamente equivalente a: #include <stdio.h> int main( void ) { puts( "hello, world" ); return 0; } Para compilar, linkar e testar: $ nasm -felf64 -o test.o test.asm $ gcc -o test test.o $ ./test hello, world Simples assim. O gcc deve ser usado como linker porque ele sabe onde a libc está. O wrt ..plt é um operador do NASM chamado With Reference To. Ele diz que o símbolo está localizado em uma section específica. No caso a section ..plt, que contém os saltos para as funções da libc (late binding). Isso é necessário, senão obterá um erro de linkagem porque o linker não encontrará o símbolo… PLT é a sigla de “Procedure Linkage Table“. É uma tabela com ponteiros para as funções carregadas dinamicamente da libc. Eis o porque de minha não recomendação: O compilador nem sempre usa o nome “oficial” da função, que pode ser um #define num header e, ás vezes, existem argumentos adicionais “invisíveis” para certas funções “intrínsecas”, mas o principal argumento contra essa prática é que seu código não ficará menor ou mais rápido só porque você está usando assembly. Por exemplo, ambos os códigos em C e Assembly, acima, fazem exatamente a mesma coisa e têm o mesmíssimo tamanho final (6 KiB, extirpando os códigos de debbugging e stack protection, no caso de C). De fato, é provável que você crie um código em assembly pior do que o compilador C criaria… Addedum: Não recomendo, particularmente, que se tente escrever códigos em assembly que usem a libc no ambiente Windows. O problema é que Windows coloca algum código adicional nas funções. Por exemplo, A pilha deve ser alterada para acomodar SEH (Structured Exception Handling), no caso de main, é necessário chamar uma função chamada _main… Coisas assim são problemáticas para o novato. Fonte: Lá no #BitIsMyth: https://wp.me/pudbF-1RF
  4. PS: Usei um buffer de 24 bytes para mantê-lo alinhado na pilha, um buffer de 20 bytes seria suficiente, já que ceil(log10(2⁶³-1)) = 19.
  5. Recomendo algo assim: #include <unistd.h> void print_decimal( long n ) { char buffer[24]; // buffer local que conterá a string. char *p; // ponteiro para o buffer. unsigned long m; // O valor "sem sinal" de n. unsigned long r; // resto. _Bool signaled; size_t size; p = buffer + sizeof buffer - 1; // aponta para o fim do buffer. // Verifica sinal. signaled = 0; m = n; // O caso de n==0 falha, então precisamos disso. if ( ! n ) { size = 1; *p-- = '0'; goto print; } if ( n < 0 ) { signaled = !signaled; m = -n; } size = 0; while ( m ) { r = m % 10; m /= 10; *p-- = '0' + r; size++; } if ( signaled ) { *p-- = '-'; size++; } print: write( STDOUT_FILENO, p+1, size ); } int main( void ) { print_decimal(-12); putchar('\n'); } Convertido para asm: ; NASM code. bits 64 default rel section .text global print_decimal print_decimal: sub rsp, 40 ; Aloca espaço na pilha para o buffer. test rdi, rdi ; testa se n é 0 ou negativo. je .zero_case js .negative xor r9d, r9d ; Usa R9 como booleano (signaled). .prepare: xor ecx, ecx ; RCX é size lea rsi, [rsp+23] ; Aponta para o final do buffer. mov r8, -3689348814741910323 ; Constante usada no "macete" da multiplicação por 1/10, abaixo. jmp .divide .loop: mov rcx, rdx .divide: mov rax, rdi sub rsi, 1 mul r8 shr rdx, 3 ; Obtém o quociente da divisão por 10 em RAX mov rax, rdx lea rdx, [rdx+rdx*4] ; Calcula o resto em RDX. add rdx, rdx sub rdi, rdx mov rdx, rdi mov rdi, rax ; O quociente é o novo dividendo. add edx, '0' mov [rsi+1], dl lea rdx, [rcx+1] test rax, rax ; Dividendo é 0? jne .loop ; Não? permanece no loop. test r9d, r9d ; Tem sinal? je .print ; Não? salta para impressão. mov rax, rsi lea rdx, [rcx+2] sub rsi, 1 mov BYTE [rax], '-' .print: add rsi, 1 ; de volta para o primeiro caracter do buffer. mov edi, 1 ; STDOUT_FILENO. mov eax, edi ; SysV ABI write syscall syscall add rsp, 40 ; Dealloca espaço para o buffer. ret .zero_case: mov BYTE [rsp+23], '0' mov edx, 1 lea rsi, [rsp+22] jmp .print .negative: neg rdi mov r9d, 1 jmp .prepare Dá pra melhorar? Yep... meu ponto é que criar uma rotina em ASM não necessariamente a faz mais rápida (essa ai é mais rápida que a sua - medida!), nem menor.
  6. A ideia de empilhar os restos foi interessante, mas tem um problema: Vocẽ está empilhando QWORDs, não BYTES. Pra quê alocar o "tamanho" na pilha... Existem 16 registradores de uso geral disponíveis. Existem alguns "acertos" a serem feitos no código... Por exemplo: XOR EDX,EDX (sim EDX, não RDX) é mais performático e menor que MOV RDX,0, mas, como o @Felipe.Silva disse, estender o sinal é melhor. E você acabará com um problema com valores negativos (acho que não testou esses, né?)... -12, por exemplo, resultará num resto de -2 na primeira divisão, o que empilhará '0'-2 ou o caracter '.'. Ainda... multiplicações são sempre mais rápidas que divisões. Veja isso: https://is.gd/rB9Vlb Como calcular o resto... Ora... se tem o quociente Q, o resto de A/10 é A - (Q*10) "Q*10" pode ser fácilmente calculado com 'LEA RDX,[RAX+RAX*4] / ADD EDX,EDX' (unsigned).
  7. You should not USE you system as root, period.
  8. Experimente rodar ./configure antes do make. O script configure é criado pelo autoconf e CONFIGURA seu código, criando o arquivo config.h, por exemplo, antes da compilação.
  9. Como ele monta todo o header "na mão", o formato para compilação com o NASM (ele não usa MASM) deve ser bin: nasm -fbin mini.asm -o mini.exe
  10. tsk, tsk, tsk... pedindo pros outros fazerem seu trabalho escolar... que vergonha!
  11. Estranho... o código está certo e o checksum PRECISA estar em BIG ENDIAN... vou investigar... Thanks.
  12. @mandelacruz, lembro a você que "herrar é umano" e, assim como Fernando disse, a iniciativa de aprender é sempre louvável! Meu comentário deve-se apenas ao fato de que algumas informações são facilmente encontradas... [[]]ão Fred
  13. Wikipedia é seu amigo: https://en.wikipedia.org/wiki/Insertion_sort Em pt-br não é tão bom: https://pt.wikipedia.org/wiki/Insertion_sort
  14. Era o que eu tinha em mente mesmo! 🙂
  15. PS: "CAST" é uma daquelas palavras em inglês difíceis de fixar... "CAST" significa elenco (teatro), mas também "jogar" (como em "cast a spell" ou "jogar um feitiço"). Mas, também é o nome que se dá ao suporte usado quando quebramos um braço ou uma perna (um "molde")... Esse último parece ser o neologismo para "casting", no contexto de C...
×
×
  • Create New...