Se você quiser se limitar à detecção ELF, leia o cabeçalho ELF de /proc/$PID/exe
você mesmo . É bastante trivial: se o quinto byte no arquivo for 1, é um binário de 32 bits. Se é 2, é de 64 bits. Para verificação adicional de sanidade:
- Se os primeiros 5 bytes forem
0x7f, "ELF", 1
: é um binário ELF de 32 bits. - Se os primeiros 5 bytes forem
0x7f, "ELF", 2
: é um binário ELF de 64 bits. - Caso contrário: é inconclusivo.
Você também pode usar objdump
, mas isso tira sua dependência de libmagic
e a substitui por libelf
.
Outra maneira : você também pode analisar o arquivo /proc/$PID/auxv
. De acordo com proc(5)
:
This contains the contents of the ELF interpreter information passed to the process at exec time. The format is one unsigned long ID plus one unsigned long value for each entry. The last entry contains two zeros.
Os significados das chaves unsigned long
estão em /usr/include/linux/auxvec.h
. Você quer AT_PLATFORM
, que é 0x00000f
. Não me cite sobre isso, mas parece que o valor deve ser interpretado como char *
para obter a descrição da string da plataforma.
Você pode encontrar esta pergunta StackOverflow útil.
Ainda de outra forma : você pode instruir o vinculador dinâmico ( man ld
) a despejar informações sobre o executável. Ele imprime na saída padrão a estrutura AUXV decodificada. Aviso: isso é um hack, mas funciona.
LD_SHOW_AUXV=1 ldd /proc/$SOME_PID/exe | grep AT_PLATFORM | tail -1
Isso mostrará algo como:
AT_PLATFORM: x86_64
Eu tentei em um binário de 32 bits e recebi i686
.
Como isso funciona: LD_SHOW_AUXV=1
instrui o Dynamic Linker a despejar a estrutura AUXV decodificada antes de executar o executável. A menos que você realmente goste de tornar sua vida interessante, você quer evitar realmente executar o executável. Uma maneira de carregá-lo e vinculá-lo dinamicamente sem realmente chamar sua função main()
é executar ldd(1)
nele. A desvantagem: LD_SHOW_AUXV
é ativado pelo shell, portanto, você obterá dumps das estruturas do AUXV para: o subshell, ldd
e seu binário de destino. Então, nós grep
para AT_PLATFORM, mas mantemos apenas a última linha.
Parsing auxv : se você mesmo analisar a estrutura auxv
(não contando com o carregador dinâmico), então há um pouco de enigma: a estrutura auxv
segue a regra do processo ele descreve, portanto sizeof(unsigned long)
será 4 para processos de 32 bits e 8 para processos de 64 bits. Nós podemos fazer isso funcionar para nós. Para que isso funcione em sistemas de 32 bits, todos os códigos de chave devem ser 0xffffffff
ou menos. Em um sistema de 64 bits, os 32 bits mais significativos serão zero. As máquinas da Intel são little endians, então esses 32 bits seguem os menos significativos da memória.
Como tal, tudo que você precisa fazer é:
1. Read 16 bytes from the 'auxv' file.
2. Is this the end of the file?
3. Then it's a 64-bit process.
4. Done.
5. Is buf[4], buf[5], buf[6] or buf[7] non-zero?
6. Then it's a 32-bit process.
7. Done.
8. Go to 1.
Analisando o arquivo de mapas : isso foi sugerido por Gilles, mas não funcionou direito. Aqui está uma versão modificada que faz. Ele depende da leitura do arquivo /proc/$PID/maps
. Se o arquivo listar endereços de 64 bits, o processo será de 64 bits. Caso contrário, são 32 bits. O problema é que o kernel irá simplificar a saída, retirando os zeros à esquerda dos endereços hexadecimais em grupos de 4, então o comprimento não pode funcionar. awk
para o resgate:
if ! [ -e /proc/$pid/maps ]; then
echo "No such process"
else
case $(awk </proc/$pid/maps -- 'END { print substr($1, 0, 9); }') in
*-) echo "32 bit process";;
*[0-9A-Fa-f]) echo "64 bit process";;
*) echo "Insufficient permissions.";;
esac
fi
Isso funciona verificando o endereço inicial do último mapa de memória do processo. Eles estão listados como 12345678-deadbeef
. Portanto, se o processo for de 32 bits, esse endereço terá oito dígitos hexadecimais e o nono será um hífen. Se for um de 64 bits, o endereço mais alto será maior do que isso. O nono caractere será um dígito hexadecimal.
Lembre-se: todos os métodos, exceto o primeiro e o último, precisam do kernel Linux 2.6.0 ou mais recente, pois o arquivo auxv
não estava lá antes.