¿Dónde se almacenan las constantes de cadena por GCC y desde donde se asignan estos punteros?

Cuando compilo y ejecuto el siguiente programa C en mi máquina con Linux x86_64, compilado por GCC:

#include <stdio.h>

int main(void)
{
    char *p1 = "hello";               // Pointers to strings
    char *p2 = "hello";               // Pointers to strings
    if (p1 == p2) {                   // They are equal
    printf("equal %p %p\n", p1, p2);  // equal 0x40064c 0x40064c
                                      // This is always the output on my machine
    }
    else {
    printf("NotEqual %p %p\n", p1, p2);
    }
}

Siempre obtengo la salida como:

igual 0x40064c 0x40064c

Entiendo que las cadenas se almacenan en una tabla constante pero la dirección es demasiado baja en comparación con la memoria asignada dinámicamente.

Compara con el siguiente programa:

#include <stdio.h>

int main(void)
{
    char p1[] = "hello";                // char arrar
    char p2[] = "hello";                // char array
    if (p1 == p2) {
    printf("equal %p %p\n", p1, p2);
    }
    else {                              // Never equal
    printf("NotEqual %p %p\n", p1, p2); // NotEqual 0x7fff4b25f720 0x7fff4b25f710
                                        // Different pointers every time
                                        // Pointer values too large
    }
}

Los dos punteros no son iguales, porque se trata de dos matrices que pueden manipularse independientemente.

Quiero saber cómo GCC genera el código para estos dos programas y cómo se asignan a la memoria durante la ejecución. Ya que esto ya estaría documentado, muchas veces cualquier enlace a la documentación también es bienvenido.

Respuestas a la pregunta(1)

Su respuesta a la pregunta