strings print interpolated es6 ecmascript ecma6 concatenate concatenar c memory string-literals

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

  1. [...] Un literal de cadena ordinaria tiene el tipo "array of n const char " y duración de almacenamiento estático (3.7)

  2. 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 y t 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 utilizar p 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

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.