2012-05-03 11 views
6

Solo para darle un poco de contexto, esto es lo que estoy tratando de lograr: Estoy incrustando un const char * en un archivo de objeto compartido para tener una cadena de versión en el archivo .so. Estoy haciendo análisis de datos y esta cadena me permite dejar que los datos sepan qué versión del software lo produjo. Todo esto funciona bienDesplazamiento en el valor del símbolo nm?

El problema que estoy teniendo es cuando trato de leer la cadena de la biblioteca .so directamente. He intentado utilizar

nm libSMPselection.so | grep _version_info 

y obtener

000000000003d968 D __SMPselection_version_info 

todo esto está bien y como se esperaba (char * se llama _SMPselection_version_info). Sin embargo, ahora esperaba poder abrir el archivo, buscar 0x3d968 y comenzar a leer mi cadena, pero todo lo que obtengo es basura.

Cuando abro el archivo .so y simplemente busco el contenido de la cadena (sé cómo comienza), puedo encontrarlo en la dirección 0x2e0b4. En esta dirección está allí, terminada en cero y como se esperaba. (Estoy usando este método por ahora.)

No soy científico informático. ¿Podría alguien explicarme por qué el valor del símbolo mostrado por nm no es correcto, o de manera diferente, cuál es el valor del símbolo si no es la dirección del símbolo?

(Por cierto que estoy trabajando en un Mac con OS X 10.7)

Respuesta

2

Nadie sugirió la manera más simple: haga un binario que cargue dinámicamente su lib (asígnele el nombre en la línea de comando) y dlsym() para su símbolo (o puede obtenerlo también en la línea de comando). al puntero de cadena e imprime en stdout.

+1

Esta es una gran idea. Lo estoy intentando en este momento. Solo hay un problema: las bibliotecas que estoy probando tienen una larga cadena de dependencias en otras bibliotecas. Si trato de cargar con dlopen, obtengo errores Symbol-not-found. La cadena de versión que me interesa, por supuesto, no tiene dependencias. ¿Cómo hago que dl ignore las dependencias? – Simon

+0

Lo he comprobado. Esto funciona muy bien si tengo todas las dependencias cargadas, que es uno de mis dos casos de uso. Gracias por la idea – Simon

1

En Linux existen el comando 'cadenas' que le ayudan a extraer cadenas de los binarios.

http://linux.about.com/library/cmd/blcmdl1_strings.htm

En HP-UX (y creo que en otras versiones de Unix también) hay un comando similar llamado 'qué'. Extrae solo cadenas que comienzan con "@ (#)", pero si controla el contenido de la cadena, esto no es un problema.

+1

¿Cómo le ayudará eso a obtener el contenido de un símbolo específico? – PlasmaHH

+0

"qué" es bueno, pero realmente quiero que mi cadena tenga varias líneas y lo que se detiene en las líneas nuevas. El comando de cadenas imprime todas las cadenas sin decirme dónde termina mi propia cadena. También parece que acaba de leer el archivo completo, que es exactamente lo que hago. Parece más elegante si pudiera leer la entrada del símbolo y saltar a la cadena directamente. – Simon

5

Suponiendo que es un ELF o un binario estructurado de forma similar, debe tener en cuenta la dirección en la que se cargan los elementos, que está influenciada por elementos en el encabezado ELF.

Utilizando objdump -Fd en su binario, puede hacer que el desensamblador también muestre el desplazamiento de archivo exacto de un símbolo.

Usando objdump -x puede encontrar esta dirección de cargador, generalmente 0x400000 para ejecutables estándar de Linux.

Lo siguiente que hay que tener cuidado con es ver si es una cadena indirecta, esto se puede hacer más fácilmente usando objdump -g. Cuando la cadena se encuentra como una cadena indirecta, en la posición de salida objdump -Fd no encontrará la cadena, sino la dirección. A partir de esto, debe restar la dirección del cargador nuevamente.Permítanme mostrarles un ejemplo de uno de mis binarios:

objdump -Fd BIN | grep VersionString 
    45152f:  48 8b 1d 9a df 87 00 mov 0x87df9a(%rip),%rbx  # ccf4d0 <acVersionString> (File Offset: 0x8cf4d0) 

objdump -x BIN 
... 
LOAD off 0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**12 
... 

Así nos fijamos en 0x8cf4d0 en el archivo y encontramos en el editor hexadecimal:

008C:F4D0 D8 C1 89 00 00 00 00 00 01 00 00 00 FF FF FF FF 

Así que tomamos la 0x89C1D8 allí, restar 0x400000 y tienen 0x49c1d8 y cuando miramos allí en el editor hexadecimal encontramos:

0049:C1D0 FF FF 7F 7F FF FF 7F FF 74 72 75 6E 6B 5F 38 30 
0049:C1E0 34 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

que significa "trunk_8043".

YMMV, especialmente cuando es algún otro formato de archivo, pero esa es la manera general de cómo estas cosas están estructuradas, con muchas verrugas y detalles que se desvían para casos especiales.

+0

Ok, gracias, siento que lo has respondido. Lo que esperaba era poder obtener la cadena sin escanear todo el archivo (o diagramarlo). Por cierto, mi versión de objdump no tiene la opción -F (estoy usando GNU objdump 2.17.50.0.6-20.el5 20061020). – Simon

+0

@Simon: Esa es una versión bastante antigua de objdump (ni siquiera recuerdo cómo fue 2006). Puedes obtener este fileoffset por tu cuenta restando el mismo desplazamiento '0x400000' del' 0xccf4d0'. Tal vez también hay una herramienta que hace todas esas cosas para ti, o podrías escribir un pequeño guión. – PlasmaHH

1

¿Por qué esperar que el desplazamiento mostrado por nm sea el desplazamiento en el archivo .so? .so archivos no son simplemente imágenes de memoria; contienen mucha otra información también, y tienen un formato más o menos complicado. En Unix (al menos en la mayoría de Unices), los objetos compartidos usan el formato elf . Para buscar la información, deberá interpretar los diversos campos en el archivo, para encontrar dónde se encuentra el símbolo que desea , en qué segmento y dónde comienza ese segmento en el archivo. (probablemente usted puede encontrar una biblioteca que va a simplificar su lectura.)

Además, si estás en lo correcto al decir que se haya incorporado un char const*, es decir que su código contenía algo como:

char const* version = "..."; 

, entonces la dirección o desplazamiento de version es la dirección o desplazamiento del puntero , no los datos de cadena a los que apunta. Definirlo como:

char const version[] = "..."; 

resolverá esto.

Por último, la solución más simple podría ser simplemente asegurarse de que la cadena tenga un patrón altamente identificable, y escanear el archivo completo buscando linealmente este patrón.

+0

Escanear todo el archivo es exactamente lo que hago. Simplemente parece menos elegante y quiero aprender algo, así que hice esta pregunta. Declarar la matriz en lugar del puntero hace que desaparezca de la lista de símbolos que muestra nm. – Simon

+1

@Simon Bueno, es más elegante analizar el archivo correctamente, pero también es mucho más trabajo. En cuanto a declarar el conjunto en lugar de un puntero, la razón por la que desaparece es debido a la sutileza de C++: un objeto const tiene un enlace interno por defecto. Si declara 'extern char const version [] =" ... "', esto no sucederá; el 'extern' fuerza el enlace externo y la inicialización lo convierte en una definición, y no en una declaración. –

+0

¡Gracias, por supuesto, me olvidé de la vinculación! Usando la palabra clave 'extern', la cadena ahora aparece en la tabla de símbolos y la dirección que obtengo de' nm' en realidad coincide con la ubicación del aguijón. Funciona ahora. ¡Puedo obtener la cadena buscando la dirección que obtengo de 'nm'! – Simon

Cuestiones relacionadas