Se você deseja limitar-se à detecção de ELF, pode ler o cabeçalho ELF de /proc/$PID/exe
si mesmo. É bastante trivial: se o quinto byte do arquivo for 1, é um binário de 32 bits. Se for 2, é de 64 bits. Para verificação de sanidade adicional:
- 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 a sua libmagic
dependência e a substitui por uma libelf
.
Outra maneira : você também pode analisar o /proc/$PID/auxv
arquivo. De acordo com proc(5)
:
Ele contém o conteúdo das informações do interpretador ELF passadas para o processo no momento da execução. O formato é um ID longo não assinado mais um valor longo não assinado para cada entrada. A última entrada contém dois zeros.
Os significados das unsigned long
teclas estão em /usr/include/linux/auxvec.h
. Você quer AT_PLATFORM
, o que é 0x00000f
. Não me cite, mas parece que o valor deve ser interpretado como um char *
para obter a descrição da plataforma da string.
Você pode achar útil essa pergunta sobre StackOverflow .
Outra maneira : 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 consegui 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ê deseja evitar executar o referido executável. Uma maneira de carregá-lo e vinculá-lo dinamicamente sem realmente chamar sua main()
função é executá ldd(1)
-lo. A desvantagem: LD_SHOW_AUXV
é ativada pelo shell, para que você obtenha despejos das estruturas AUXV para: o subshell ldd
, e o seu binário de destino. Então, grep
para AT_PLATFORM, mantemos apenas a última linha.
Analisando auxv : se você analisar a auxv
estrutura você mesmo (sem depender do carregador dinâmico), haverá um pouco de um dilema: a auxv
estrutura segue a regra do processo descrito, sizeof(unsigned long)
sendo 4 para processos de 32 bits e 8 para 64 processos de bits. 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 Intel são pouco conhecidas, portanto esses 32 bits seguem os menos significativos na memória.
Como tal, tudo o 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 completamente. Aqui está uma versão modificada que faz. Depende da leitura do /proc/$PID/maps
arquivo. Se o arquivo listar endereços de 64 bits, o processo será de 64 bits. Caso contrário, são 32 bits. O problema reside no fato de o kernel simplificar a saída eliminando os zeros à esquerda dos endereços hexadecimais nos grupos de 4, para que o tamanho não funcione. 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 de 64 bits, o endereço mais alto será mais longo que isso. O nono caractere será um dígito hexadecimal.
Esteja ciente: todos, exceto o primeiro e o último método, precisam do kernel Linux 2.6.0 ou mais recente, pois o auxv
arquivo não estava lá antes.
/proc/[pid]/auxv
: "as informações do interpretador ELF passadas para o processo no momento da execução. O formato é um ID longo não assinado mais um valor longo não assinado para cada entrada" (man proc
).hd
editar um e faltava o número mágico. Pode haver alguma informação relevante lá, mas acho que estaria sujeita a alterações mais frequentes do que o próprio cabeçalho da ELF. Também foi introduzido na 2.6.0, por isso não é tão onipresente quanto/proc/PID/exe
. Mas não têm a arquitetura de informação. Vou atualizar minha resposta.sizeof(unsigned long)
é de 8 em 64 bits ou 4 em 32 bits, o que significa que, para interpretá-lo corretamente, você precisa saber se o processo é de 64 ou 32 bits!auxv
códigos de chave se encaixam em 32 bitsunsigned long
, então os 32 bits mais significativos em uma caixa de 64 bits seriam zero.Olhe dentro
/proc/$pid/maps
. Os intervalos de endereços têm mais de 32 bits (8 dígitos hexadecimais) ou 64 bits (16 dígitos hexadecimais). Isso funciona para qualquer tipo de executável, independentemente do formato. Você só pode obter informações sobre processos em execução como o mesmo usuário (a menos que você seja root).Se você não tem permissão para acessar esse arquivo, acho que a única maneira é tentar analisar o executável. (Enquanto você sempre pode ler
/proc/$pid/stat
, nenhum dos campos mostrados para processos em execução como usuários diferentes revela o tamanho do bit do processo.) Você pode adivinhar o executável do processops -o comm=
e pesquisá-lo noPATH
- mas cuidado com o processo pode ter sido lançado com um diferentePATH
ou pode ter reescrito seuargv[0]
. Você pode analisar o executável - se estiver disposto a assumir o ELF, observe o quinto byte .fonte
vsyscall
mapa é sempre o mais alto, você pode simplesmente mudarhead
paratail
- o que, infelizmente, não funciona porque o proc não é implementadoseek(2)
, por isso terá que ser algo mais feio, comoawk /proc/self/maps -- 'END { print substr($1, 0, 9); }'