Boa noite pessoal!!!
Gostaria de saber se existe alguma forma de ver como um código fonte JAVA ficaria em binário?
Boa noite pessoal!!!
Gostaria de saber se existe alguma forma de ver como um código fonte JAVA ficaria em binário?
Tem que converter cada letra em binario.
public String AsciiToBinary(String asciiString){
byte[] bytes = asciiString.getBytes();
StringBuilder binary = new StringBuilder();
for (byte b : bytes)
{
int val = b;
for (int i = 0; i < 8; i++)
{
binary.append((val & 128) == 0 ? 0 : 1);
val <<= 1;
}
// binary.append(' ');
}
return binary.toString();
}
Primeiro você precisa entender o que exatamente é o “binário” gerado a partir do código fonte.
Inicialmente temos o código fonte (o arquivo .java
). Ao compilá-lo, é gerado um arquivo .class
, contendo o bytecode, que não deixa de ser “binário”. Lembrando que aqui o termo “binário” geralmente quer dizer que “não é texto” - porque se formos levar ao pé da letra, tudo no computador vira binário no fim das contas (até texto), mas para o caso em questão, vamos considerar que binário é “um monte de bytes que não correspondem a um texto”.
Ou seja, o .class
já é um tipo de “binário”. Ele contém o bytecode, que são as instruções que serão interpretadas pela máquina virtual (a JVM - Java Virtual Machine). Só que a JVM possui outro compilador (mais precisamente, um JIT - Just In Time compiler), que pode pegar trechos do bytecode e compilar novamente, gerando código de máquina específico da plataforma/sistema operacional no qual ela está rodando. Na verdade é mais complexo que isso, veja mais detalhes aqui.
Ou seja, a partir do código fonte, é gerado o bytecode (um tipo de arquivo “binário”), que por sua vez pode ser transformado em código de máquina nativo (dependente da plataforma/arquitetura/processador/sistema operacional/etc), e portanto é “outro binário”.
Se você quer ver os bytecodes (ou seja, as instruções da JVM que estão no .class
), basta usar o javap
. Por exemplo, se eu tiver um arquivo chamado Teste.java
:
// Teste.java
public class Teste {
public static void main(String args[]) {
int x = 0;
for (int i = 0; i < 1000000; i++) {
x += 1;
}
System.out.println("fim");
}
}
Primeiro eu compilo com javac Teste.java
, gerando o arquivo Teste.class
(que contém o bytecode). Basta rodar:
javap -c Teste.class
Que eu terei uma saída assim:
Compiled from "Teste.java"
public class Teste {
public Teste();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iconst_0
3: istore_2
4: iload_2
5: ldc #2 // int 1000000
7: if_icmpge 19
10: iinc 1, 1
13: iinc 2, 1
16: goto 4
19: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
22: ldc #4 // String fim
24: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
27: return
}
Este é o bytecode. Se você quer saber o que cada instrução significa, é “só” ler a especificação da JVM
Lembrando que a saída do javap
é formatada de um jeito mais “legível”, pois no .class
não tem em nenhum lugar o texto “invokevirtual” ou “istore”. O que ele tem são bytes cujos valores correspondem a estas operações, e o javap
só pega esses valores e mostra de um jeito mais “amigável” para entendermos.
Agora se você quer ver o binário gerado pela JVM (ou seja, pelo JIT), aí você pode rodar o programa com as opções -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly
:
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly Teste
Obs: tem que instalar a libhsdis0-fcml
, existem instruções de instalação aqui e aqui (ou então procure no Google como instalar no seu sistema específico).
Neste caso, a saída será gigante e irá variar conforme o ambiente/sistema operacional, etc. Eu testei no Ubuntu 20.04, com OpenJDK 1.8.0_292 e a saída teve mais de 3000 linhas. Seguem as primeiras, para você ter uma ideia:
Loaded disassembler from /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/hsdis-amd64.so
Decoding compiled method 0x00007f6911103390:
Code:
Argument 0 is unknown.RIP: 0x7f6911103500 Code size: 0x00000290
[Disassembling for mach='amd64']
[Entry Point]
[Constants]
# {method} {0x00007f68f3805000} 'hashCode' '()I' in 'java/lang/String'
# [sp+0x40] (sp of caller)
0x00007f6911103500: mov 0x8(%rsi),%r10d
0x00007f6911103504: shl $0x3,%r10
0x00007f6911103508: cmp %rax,%r10
0x00007f691110350b: jne 0x7f6911045ba0 ; {runtime_call}
0x00007f6911103511: nopw 0x0(%rax,%rax)
0x00007f691110351c: nop
[Verified Entry Point]
0x00007f6911103520: mov %eax,0xfffffffffffec000(%rsp)
0x00007f6911103527: push %rbp
0x00007f6911103528: sub $0x30,%rsp
0x00007f691110352c: movabs $0x7f68f39db0f0,%rax ; {metadata(method data for {method} {0x00007f68f3805000} 'hashCode' '()I' in 'java/lang/String')}
0x00007f6911103536: mov 0xdc(%rax),%edi
0x00007f691110353c: add $0x8,%edi
0x00007f691110353f: mov %edi,0xdc(%rax)
0x00007f6911103545: movabs $0x7f68f3805000,%rax ; {metadata({method} {0x00007f68f3805000} 'hashCode' '()I' in 'java/lang/String')}
0x00007f691110354f: and $0x1ff8,%edi
0x00007f6911103555: cmp $0x0,%edi
0x00007f6911103558: je 0x7f6911103691 ;*aload_0
; - java.lang.String::hashCode@0 (line 1466)
0x00007f691110355e: mov 0x10(%rsi),%eax ;*getfield hash
; - java.lang.String::hashCode@1 (line 1466)
... etc (mais um monte de linhas)
hugokotsubo Vc sabe como fazer para obter o binário do código fonte de outras linguagens como C/C++, Python,Javascript?
C e C++ compilam direto pro código de máquina, então o executável gerado já é o binário. É só abrir o arquivo em um editor hexadecimal se quiser ver os “bytes brutos”. Se quer ver o código de máquina correspondente, pode usar alguma ferramenta disassembler como o objdump
, disponível no Linux:
objdump -d arquivo_executavel
A saída é algo do tipo:
Disassembly of section .init:
0000000000001000 <_init>:
1000: f3 0f 1e fa endbr64
1004: 48 83 ec 08 sub $0x8,%rsp
1008: 48 8b 05 d9 2f 00 00 mov 0x2fd9(%rip),%rax # 3fe8 <__gmon_start__>
100f: 48 85 c0 test %rax,%rax
1012: 74 02 je 1016 <_init+0x16>
1014: ff d0 callq *%rax
1016: 48 83 c4 08 add $0x8,%rsp
101a: c3 retq
No Windows tem o dumpbin
, mas eu nunca usei (talvez tenha outras ferramentas, é só procurar).
Em Python você pode fazer algo como:
import py_compile
py_compile.compile('arquivo.py')
Isso irá criar um arquivo .pyc
na pasta __pycache__/
, que é um binário contendo o byte code correspondente ao código que está no arquivo.py
.
Mas você também pode fazer na linha de comando:
python -m dis arquivo.py
Isso irá mostrar o byte code em um formato mais “amigável”, exemplo:
3 0 LOAD_CONST 0 (0)
2 LOAD_CONST 1 (None)
4 IMPORT_NAME 0 (io)
6 STORE_NAME 0 (io)
8 LOAD_CONST 0
etc...
Em JavaScript eu não faço ideia.
Se ele roda no browser, então isso deve ficar meio “escondido”. Por exemplo, no Chrome, a engine de JavaScript é o V8, que gera o byte code e ele também tem um JIT, que gera código de máquina sob demanda. Mas eu não sei onde esse binário fica (se é só em memória, ou se o Chrome guarda isso em algum lugar).
Se o JavaScript está rodando fora do browser (por exemplo, no Node), talvez ele guarde em algum lugar também (o Node também usa o V8, mas nada impede que ele faça alguma coisa diferente do browser com relação a isso). No fim, acho que depende da implementação (cada browser deve fazer de um jeito, e runtimes como o Node e o Deno também devem fazer cada um da sua maneira).