print - Literales de cadenas: ¿a dónde van?
js concat string es6 (8)
Como esto puede diferir del compilador al compilador, la mejor manera es filtrar un volcado de objetos para el literal de cadena buscado:
objdump -s main.o | grep -B 1 str
donde -s
fuerzas objdump
para mostrar el contenido completo de todas las secciones, main.o
es el archivo objeto, -B 1
fuerza a grep
a imprimir una línea antes de la coincidencia (para que pueda ver el nombre de la sección) y str
es la cadena literalmente estás buscando.
Con gcc en una máquina con Windows, y una variable declarada en main
como
char *c = "whatever";
corriendo
objdump -s main.o | grep -B 1 whatever
devoluciones
Contents of section .rdata:
0000 77686174 65766572 00000000 whatever....
Me interesa saber dónde se asignan / almacenan los literales de cadena.
Encontré una respuesta intrigante here , diciendo:
Definir una cadena en línea realmente integra los datos en el programa mismo y no se puede cambiar (algunos compiladores lo permiten mediante un truco inteligente, no se moleste).
Pero, tenía que ver con C ++, sin mencionar que dice no molestarse.
Me estoy molestando = D
¿Entonces mi pregunta es dónde y cómo se conserva mi cadena literal? ¿Por qué no debería tratar de alterarlo? ¿La implementación varía según la plataforma? ¿Alguien quiere elaborar sobre el "truco inteligente"?
Depende del format de tu executable . Una forma de pensarlo es que, si fuera la programación de ensamblaje, podría poner literales de cadena en el segmento de datos de su programa de ensamblaje. Su compilador de C hace algo así, pero todo depende de para qué sistema se está compilando el binario.
FYI, simplemente respaldando las otras respuestas:
El estándar: ISO / IEC 14882: 2003 dice:
2.13. Literales de cadenas
[...] Un literal de cadena ordinaria tiene el tipo "array of
n const char
" y duración de almacenamiento estático (3.7)Si todos los literales de cadena son distintos (es decir, se almacenan en objetos no superpuestos) está definido por la implementación. El efecto de intentar modificar un literal de cadena no está definido.
Los literales de cadena se asignan con frecuencia a la memoria de solo lectura, lo que los hace inmutables. Sin embargo, en algunos compiladores, la modificación es posible mediante un "truco inteligente". Y el truco más inteligente es "usar el puntero del carácter apuntando a la memoria" ... recuerden algunos compiladores, pueden no permitir esto ... Aquí está la demostración
char *tabHeader = "Sound";
*tabHeader = ''L'';
printf("%s/n",tabHeader); // Displays "Lound"
No hay una respuesta para esto. Los estándares C y C ++ solo dicen que los literales de cadena tienen una duración de almacenamiento estática, cualquier intento de modificarlos proporciona un comportamiento indefinido, y múltiples literales de cadena con los mismos contenidos pueden o no compartir el mismo almacenamiento.
Según el sistema para el que esté escribiendo y las capacidades del formato de archivo ejecutable que utiliza, pueden almacenarse junto con el código de programa en el segmento de texto, o pueden tener un segmento separado para los datos inicializados.
La determinación de los detalles también variará según la plataforma; lo más probable es que incluya herramientas que puedan indicarle dónde lo está colocando. Algunos incluso le darán control sobre detalles como esos, si lo desea (p. Ej., Gnu ld le permite suministrar un script para contar todo sobre cómo agrupar datos, códigos, etc.)
Una técnica común es que los literales de cadena se coloquen en la sección "datos de solo lectura", que se asigna al espacio de proceso como de solo lectura (por lo que no se puede modificar).
Sí varía según la plataforma. Por ejemplo, las arquitecturas de chips más simples pueden no admitir segmentos de memoria de solo lectura, por lo que el segmento de datos será de escritura.
En lugar de eso, trate de descubrir un truco para hacer que los literales de cadena cambien (dependerá en gran medida de su plataforma y podría cambiar con el tiempo), solo use matrices:
char foo[] = "...";
El compilador organizará que la matriz se inicie desde el literal y puede modificar la matriz.
gcc hace una sección .rodata
que se asigna "en algún lugar" en el espacio de direcciones y está marcada como de solo lectura,
Visual C ++ ( cl.exe
) cl.exe
una sección .rdata
para el mismo propósito.
Puede ver el resultado de dumpbin
u objdump
(en Linux) para ver las secciones de su ejecutable.
P.ej
>dumpbin vec1.exe
Microsoft (R) COFF/PE Dumper Version 8.00.50727.762
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file vec1.exe
File Type: EXECUTABLE IMAGE
Summary
4000 .data
5000 .rdata <-- here are strings and other read-only stuff.
14000 .text
¿Por qué no debería tratar de alterarlo?
Porque es un comportamiento indefinido. Cita de C99 N1256 borrador 6.7.8 / 32 "Inicialización" :
EJEMPLO 8: La declaración
char s[] = "abc", t[3] = "abc";
define objetos de matriz char "simples"
s
yt
cuyos elementos se inicializan con literales de cadena de caracteres.Esta declaración es idéntica a
char s[] = { ''a'', ''b'', ''c'', ''/0'' }, t[] = { ''a'', ''b'', ''c'' };
El contenido de las matrices es modificable. Por otro lado, la declaración
char *p = "abc";
define
p
con el tipo "puntero a char" y lo inicializa para apuntar a un objeto con el tipo "array of char" con longitud 4 cuyos elementos se inicializan con un literal de cadena de caracteres. Si se intenta utilizarp
para modificar el contenido de la matriz, el comportamiento no está definido.
¿A dónde van?
GCC 4.8 x86-64 ELF Ubuntu 14.04:
-
char s[]
: pila -
char *s
:- Sección de
.rodata
del archivo de objeto - el mismo segmento donde se
.text
sección.text
del archivo objeto, que tiene permisos de lectura y ejecución, pero no escritura
- Sección de
Programa:
#include <stdio.h>
int main() {
char *s = "abc";
printf("%s/n", s);
return 0;
}
Compilar y descompilar:
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
La salida contiene:
char *s = "abc";
8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
f: 00
c: R_X86_64_32S .rodata
Entonces, la cadena se almacena en la sección .rodata
.
Entonces:
readelf -l a.out
Contiene (simplificado):
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000000704 0x0000000000000704 R E 200000
Section to Segment mapping:
Segment Sections...
02 .text .rodata
Esto significa que el script del enlazador predeterminado .text
ambos .text
y .rodata
en un segmento que puede ser ejecutado pero no modificado ( Flags = RE
). Intentar modificar dicho segmento conduce a una segfault en Linux.
Si hacemos lo mismo para char[]
:
char s[] = "abc";
obtenemos:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
por lo que se almacena en la pila (en relación con %rbp
), y podemos, por supuesto, modificarlo.