IDE integrado (2): explicación detallada y análisis de ejemplo de archivos de enlace de carga dispersa SCF en KEIL

En el artículo anterior , explicación detallada y análisis de ejemplo de archivos de enlace ICF en IAR , analicé la sintaxis de los archivos de enlace ICF en IAR a través de la relación de mapeo de memoria en el SDK de I.MX RT1170. Para el IDE utilizado en la programación de MCU, IAR y Keil se utilizan más, por lo que este artículo analizará los archivos dispersos de Keil .scf( scatter file).

1 mapa de memoria

Al igual que en el artículo anterior, también utilizo el archivo de enlace en el SDK de I.MX RT1170 para el análisis y aprendo la gramática interna a través del archivo disperso real. Es la misma rutina que en el apartado anterior, además de la RAM que viene con el chip, están NOR Flash y SDRAM. Primero mire la tabla de mapeo de memoria de todo el proyecto:

tipo nombre dirección inicial tamaño
Destello Flash ni 0x30000000 0x1000000
RAM SDRAM 0x80000000 0x3000000
RAM NCACHE_REGION 0x83000000 0x1000000
RAM SRAM_DTC_cm7 0x20000000 0x40000
RAM SRAM_ITC_cm7 0x0 0x40000
RAM SRAM_OC1 0x20240000 0x80000
RAM SRAM_OC2 0x202c0000 0x80000
RAM SRAM_OC_ECC1 0x20340000 0x10000
RAM SRAM_OC_ECC2 0x20350000 0x10000

Para nuestro proyecto, existen las siguientes memorias:

  1. Dos memorias de 256 KB estrechamente acopladas DTCMyITCM
  2. Dos RAM en chip con ECC: OC1y OC2.
  3. 0x30000000Se conecta un Flash NOR de 16 MB a la interfaz FlexSPI1 cuya dirección inicial es
  4. 0x80000000Se conecta una SDRAM de 64 MB a la interfaz FlexSPI2 con la dirección inicial del mapeo . Entre ellos, los primeros 48 MB se usan para el área almacenable en caché y los últimos 16 MB ( NCACHE_REGION) se usan para el área no almacenable en caché. Por lo general, el búfer que interactúa directamente con el hardware debe configurarse como no almacenable en caché.

2 análisis de sintaxis SCF

2.1 Archivo SCF del proyecto

Para el mapeo de memoria anterior, los archivos proporcionados en el SDK oficial SCFson los siguientes:

#if (defined(__ram_vector_table__))
  #define __ram_vector_table_size__    0x00000400
#else
  #define __ram_vector_table_size__    0x00000000
#endif

#define m_flash_config_start           0x30000400
#define m_flash_config_size            0x00000C00

#define m_ivt_start                    0x30001000
#define m_ivt_size                     0x00000020

#define m_boot_data_start              0x30001020
#define m_boot_data_size               0x00000010

#define m_dcd_data_start               0x30001030
#define m_dcd_data_size                0x000006E8

#define m_xmcd_data_start              0x30001040
#define m_xmcd_data_size               0x00000204

#define m_interrupts_start             0x30002000
#define m_interrupts_size              0x00000400

#define m_text_start                   0x30002400
#if (defined(__use_flash64MB__))
#define m_text_size                    0x03FFDC00
#else
#define m_text_size                    0x00FFDC00
#endif

#define m_qacode_start                 0x00000000
#define m_qacode_size                  0x00040000

#define m_interrupts_ram_start         0x80000000
#define m_interrupts_ram_size          __ram_vector_table_size__

#define  m_data_start                  (m_interrupts_ram_start + m_interrupts_ram_size)
#define  m_data_size                   (0x03000000 - m_interrupts_ram_size)

#define m_data2_start                  0x20000000
#define m_data2_size                   0x00040000

#define m_data3_start                  0x202C0000
#define m_data3_size                   0x00080000

#define m_ncache_start                 0x83000000
#define m_ncache_size                  0x01000000

/* Sizes */
#if (defined(__stack_size__))
  #define Stack_Size                   __stack_size__
#else
  #define Stack_Size                   0x0400
#endif

#if (defined(__heap_size__))
  #define Heap_Size                    __heap_size__
#else
  #define Heap_Size                    0x0400
#endif

#if defined(XIP_BOOT_HEADER_ENABLE) && (XIP_BOOT_HEADER_ENABLE == 1)
LR_m_text m_flash_config_start m_text_start+m_text_size-m_flash_config_start {   ; load region size_region
  RW_m_config_text m_flash_config_start FIXED m_flash_config_size { ; load address = execution address
    * (.boot_hdr.conf, +FIRST)
  }

  RW_m_ivt_text m_ivt_start FIXED m_ivt_size { ; load address = execution address
    * (.boot_hdr.ivt, +FIRST)
  }

  RW_m_boot_data_text m_boot_data_start FIXED m_boot_data_size { ; load address = execution address
    * (.boot_hdr.boot_data, +FIRST)
  }

#if defined(XIP_BOOT_HEADER_DCD_ENABLE) && (XIP_BOOT_HEADER_DCD_ENABLE == 1)
  RW_m_dcd_data_text m_dcd_data_start FIXED m_dcd_data_size { ; load address = execution address
    * (.boot_hdr.dcd_data, +FIRST)
  }
#elif defined(XIP_BOOT_HEADER_XMCD_ENABLE) && (XIP_BOOT_HEADER_XMCD_ENABLE == 1)
  RW_m_xmcd_data_text m_xmcd_data_start FIXED m_xmcd_data_size { ; load address = execution address
    * (.boot_hdr.xmcd_data, +FIRST)
  }
#endif
#else
LR_m_text m_interrupts_start m_text_start+m_text_size-m_interrupts_start {   ; load region size_region
#endif
  VECTOR_ROM m_interrupts_start FIXED m_interrupts_size { ; load address = execution address
    * (.isr_vector,+FIRST)
  }
  ER_m_text m_text_start FIXED m_text_size { ; load address = execution address
    * (InRoot$$Sections)
    .ANY (+RO)
  }
#if (defined(__ram_vector_table__))
  VECTOR_RAM m_interrupts_ram_start EMPTY m_interrupts_ram_size {
  }
#else
  VECTOR_RAM m_interrupts_start EMPTY 0 {
  }
#endif
  RW_m_data2 m_data2_start m_data2_size {
    * (RamFunction)
    * (DataQuickAccess)
  }
#if (defined(__heap_noncacheable__))
  RW_m_data m_data_start m_data_size-Stack_Size { ; RW data
#else
  RW_m_data m_data_start m_data_size-Stack_Size-Heap_Size { ; RW data
#endif
    .ANY (+RW +ZI)
    *(*m_usb_dma_init_data)
    *(*m_usb_dma_noninit_data)
  }
#if (!defined(__heap_noncacheable__))
  ARM_LIB_HEAP +0 EMPTY Heap_Size {    ; Heap region growing up
  }
#endif
  ARM_LIB_STACK m_data_start+m_data_size EMPTY -Stack_Size { ; Stack region growing down
  }
  RW_m_ram_text m_qacode_start m_qacode_size { ;
    * (CodeQuickAccess)
  }
#if (defined(__heap_noncacheable__))
  RW_m_ncache m_ncache_start m_ncache_size - Heap_Size { ; ncache data
#else
  RW_m_ncache m_ncache_start m_ncache_size { ; ncache data
#endif
    * (NonCacheable.init)
    * (*NonCacheable)
  }
#if (defined(__heap_noncacheable__))
  ARM_LIB_HEAP +0 EMPTY Heap_Size {    ; Heap region growing up
  }
  RW_m_ncache_unused +0 EMPTY m_ncache_size-ImageLength(RW_m_ncache)-Heap_Size { ; Empty region added for MPU configuration
#else
  RW_m_ncache_unused +0 EMPTY m_ncache_size-ImageLength(RW_m_ncache) { ; Empty region added for MPU configuration
#endif
  }
}

2.2 definir

Primero analicemos el primer párrafo de archivos dispersos. La sintaxis definey #if definedlas declaraciones de los archivos dispersos de KEIL son consistentes con el lenguaje C, por lo que el siguiente párrafo es fácil de entender:

#if (defined(__ram_vector_table__))
  #define __ram_vector_table_size__    0x00000400
#else
  #define __ram_vector_table_size__    0x00000000
#endif

#define m_flash_config_start           0x30000400
#define m_flash_config_size            0x00000C00

#define m_ivt_start                    0x30001000
#define m_ivt_size                     0x00000020

#define m_boot_data_start              0x30001020
#define m_boot_data_size               0x00000010

#define m_dcd_data_start               0x30001030
#define m_dcd_data_size                0x000006E8

#define m_xmcd_data_start              0x30001040
#define m_xmcd_data_size               0x00000204

#define m_interrupts_start             0x30002000
#define m_interrupts_size              0x00000400

#define m_text_start                   0x30002400
#if (defined(__use_flash64MB__))
#define m_text_size                    0x03FFDC00
#else
#define m_text_size                    0x00FFDC00
#endif

#define m_qacode_start                 0x00000000
#define m_qacode_size                  0x00040000

#define m_interrupts_ram_start         0x80000000
#define m_interrupts_ram_size          __ram_vector_table_size__

#define  m_data_start                  (m_interrupts_ram_start + m_interrupts_ram_size)
#define  m_data_size                   (0x03000000 - m_interrupts_ram_size)

#define m_data2_start                  0x20000000
#define m_data2_size                   0x00040000

#define m_data3_start                  0x202C0000
#define m_data3_size                   0x00080000

#define m_ncache_start                 0x83000000
#define m_ncache_size                  0x01000000

/* Sizes */
#if (defined(__stack_size__))
  #define Stack_Size                   __stack_size__
#else
  #define Stack_Size                   0x0400
#endif

#if (defined(__heap_size__))
  #define Heap_Size                    __heap_size__
#else
  #define Heap_Size                    0x0400
#endif

Permítanme explicarles primero, cuando el microcontrolador de la serie I.MX esté encendido, ingresará al cargador de arranque L1, que se usa para iniciar cómo iniciar el programa, por ejemplo, si está cifrado, la clave de cifrado, si es XIP o no XIP (debe copiarse en la RAM) y si es necesario inicializarlo. En el caso del inicio de NOR Flash, los primeros 0x2000 bytes de la imagen del programa se utilizan para proporcionar información de inicio al cargador de arranque L1. No es necesario prestar demasiada atención al significado de estos campos. Si desea comprender en Para obtener más detalles, puede consultar mi artículo I.MX Explicación detallada del inicio de RT1170: Configuración de inicio y composición del encabezado de imagen de inicio .
(1) __ram_vector_table__no está definido en ningún otro lugar, por lo que __ram_vector_table_size__es 0. Esto también es fácil de entender, porque aquí hay NOR Flash, la tabla de vectores no se coloca en la RAM, sino que se coloca al frente de NOR Flash.
(2) m_flash_config_starty m_flash_config_size: se utiliza para proporcionar información de configuración de NOR Flash al L1 BootLoader, porque después del encendido, L1 BootLoader utiliza la configuración más lenta y segura para inicializar NOR Flash. Si el usuario desea configurar algunos parámetros por sí mismo, como hacer el reloj más rápido. Puede completar la información de configuración en este campo, la dirección inicial es 0x30000400(la dirección base de NOR Flash es 0x30000000), la longitud es 0xC00
(3) m_ivt_starty m_ivt_size: Image Vector Tablecampo IVT (), utilizado para guardar parámetros como la entrada del programa. dirección
(4) m_boot_data_starty m_boot_data_size: usado Guarde la dirección inicial absoluta y el tamaño de la imagen
(5) m_dcd_data_starty m_dcd_data_size: DCDcampos, que generalmente se usan para inicializar SDRAM. Especialmente si desea que el programa se ejecute en SDRAM, debe configurar este campo
( 6) m_xmcd_data_startym_xmcd_data_size: Puede ver que la dirección inicial y el tamaño aquí DCDse superponen con los campos anteriores. De hecho, las funciones de los dos son similares, excepto que DCDla configuración es una instrucción de configuración de registro, que es más complicada y XMCD simplifica estas operaciones de configuración. Estos dos campos son opcionales.
(7) m_interrupts_starty m_interrupts_size: Como se mencionó anteriormente, el tamaño de la información del encabezado del BootLoader L1 es 0x2000, por lo que 0x2000es el comienzo del programa desde el principio, y la tabla de vectores se coloca al frente y la longitud es 0x400.
(8) m_text_starty m_text_size: El segmento de código está inmediatamente después de la tabla de vectores, la dirección inicial es 0x30002400, aquí __use_flash64MB__es falso, asumimos que se usan 16 MB () NOR Flash 0x1000000y el tamaño restante es 0x1000000-0x2400=0x00FFDC00.
(9) m_qacode_startSuma : es decir , (10) m_qacode_sizesuma dentro del chip en el mapa de memoria anterior : si la tabla de vectores no se coloca en NOR Flash, se coloca al comienzo de SDRAM. Dado que se coloca en NOR Flash, estos No se utilizan dos campos (11) y : segmento de datos de datos, donde el segmento de datos se coloca en SDRAM. Aquí el tamaño de SDRAM es 64M y este campo ocupa los primeros 48M. (12) , y : el mismo segmento de datos, respectivamente y , es decir, la RAM en el chip se puede utilizar como segmento de datos para colocar variablesSRAM_ITC_cm7
m_interrupts_ram_startm_interrupts_ram_size
m_data_startm_data_size
m_data2_startm_data2_sizem_data3_startm_data3_sizeSRAM_ITC_cm7SRAM_OC2

  • SRAM_OC1Si no se utiliza, podemos declararlo nosotros mismos. Debido a que L1 BootLoader usa esta SRAM cuando se ejecuta, debe considerar el tiempo al usar esta SRAM.

(13) m_ncache_startSuma m_ncache_size: Los últimos 16 M de SDRAM se utilizan para áreas que no se pueden almacenar en caché, como el búfer de dibujo de la GUI, el búfer de la cámara y los datos DMA. Este tipo de memoria que interactúa directamente con el hardware debe definirse en un área que no se puede almacenar en caché. Esto está relacionado con la configuración de MPU. Puede consultar los artículos de mi serie MPU, explicación detallada y ejemplos de la unidad de protección de memoria MPU y explicación detallada de L1 Cache I-Cache y D-cache .
(14) Stack_Sizey Heap_Size: son los tamaños de pila y montón respectivamente. Dado que FreeRTOS se utiliza en el programa, solo asegúrese de que los tamaños de pila y montón aquí puedan inicializar correctamente FreeRTOS. No debe haber ninguna asignación de memoria durante el proceso de inicialización de FreeRTOS, por lo que se puede establecer en 0 Heap Size. .

2.3 Zona de carga y zona de ejecución

A continuación, comenzaremos a hablar sobre la sintaxis de algunos archivos dispersos, consulte la documentación: <DUI0377G_02_mdk_armlink_user_guide.pdf>(se puede encontrar en el directorio de instalación de KEIL).
En comparación con IAR, la sintaxis de los archivos dispersos de KEIL es mucho más simple, similar a la del archivo ld de Linux. Los archivos dispersos se componen de una o más áreas de carga (), como se muestra en la siguiente figura: La sintaxis Load Regiondel
Insertar descripción de la imagen aquí
área de carga es como sigue:

load_region_name (base_address | ("+" offset)) [attribute_list] [max_size]
"{"
execution_region_description+
"}"
  • load_region_name(nombre): una etiqueta única utilizada para identificar diferentes áreas de carga por parte del vinculador, cada área de carga debe tener un nombre único
  • base_address(Dirección base): la dirección de memoria inicial donde se colocan en la memoria el código y los datos en el área de carga.
  • attribute_list(propiedad): define las características y el comportamiento del área de carga, incluidos los atributos de solo lectura, lectura-escritura, solo ejecución u otros atributos de protección de memoria.
  • max_size(tamaño máximo): opcional, se utiliza para limitar el tamaño del área de carga para evitar el desbordamiento de la memoria
  • execution_region_description(Región de ejecución): la región de carga puede contener una o más regiones de ejecución. La región de ejecución representa un bloque contiguo de código y datos cargados en la memoria como una sola unidad.

Sería demasiado tiempo resumir toda la gramática en el artículo, por lo que debemos continuar analizando los documentos dispersos, si aparece alguna gramática o palabra clave, encontraremos su significado. Dado que hay demasiadas definiciones de macros en los siguientes archivos dispersos, lo que afecta la lectura, aquí se supone que , XIP_BOOT_HEADER_ENABLE=1y ( significan que el montón se coloca en el área que no se puede almacenar en caché para garantizar que la XIP_BOOT_HEADER_DCD_ENABLE=1memoria del montón no se vea afectada por el cache).XIP_BOOT_HEADER_XMCD_ENABLE=0__heap_noncacheable__

Los archivos dispersos restantes en realidad definen un área de carga LR_m_text. Su dirección inicial es m_flash_config_start( 0x30000400) y su tamaño máximo es m_text_start+m_text_size-m_flash_config_start( 16M-0x400=0xFFFC00), es decir, 0x30000400el enlace comienza desde allí y el tamaño es 0xFFFC00. Este tamaño solo limita el tamaño del área de carga (el siguiente atributos para FIXEDel área de ejecución). 0~0x400Estos campos, relevantes para el inicio cifrado de los microcontroladores de la serie NXP RT, no pueden ser completados por el compilador, por lo que no se consideran aquí.

LR_m_text m_flash_config_start m_text_start+m_text_size-m_flash_config_start {   ; load region size_region
	......
}
  • En archivos dispersos, ;seguidos de comentarios.

LR_m_textHay muchas áreas de ejecución debajo del área de carga , analicémoslas una por una:

1.RW_m_config_text : dirección inicial 0x30000400, tamaño0xC00

RW_m_config_text m_flash_config_start FIXED m_flash_config_size { ; load address = execution address
	* (.boot_hdr.conf, +FIRST)
}
  • FIXED: Atributos del área de ejecución, que indican que la dirección de ejecución y la dirección de carga del área de ejecución deben mantenerse iguales tanto como sea posible. Esto significa que el código y los datos asignados a esta región de ejecución intentarán colocarse en la dirección de ejecución especificada cuando se carguen en la memoria. Si no se puede satisfacer debido a conflictos de memoria o espacio insuficiente, el vinculador informará un error.
  • +FIRST: Indica sectioncolocar la función al inicio del área de ejecución

Entonces aquí colocamos boot_hdr.confel segmento desde el comienzo de 0x30000400. Debido a que la posición de ubicación debe ser fija para que L1 BootLoader la reconozca correctamente, el área de ejecución debe usar FIXEDatributos.

2.RW_m_ivt_text : dirección inicial 0x30001000, tamaño0x00000020

RW_m_ivt_text m_ivt_start FIXED m_ivt_size { ; load address = execution address
	* (.boot_hdr.ivt, +FIRST)
}

Igual que arriba, coloque el encabezado de arranque de L1 BootLoader.

3.RW_m_boot_data_text : dirección inicial 0x30001020, tamaño0x00000010

RW_m_boot_data_text m_boot_data_start FIXED m_boot_data_size { ; load address = execution address
	* (.boot_hdr.boot_data, +FIRST)
}

Igual que arriba, coloque el encabezado de arranque de L1 BootLoader.

4.RW_m_dcd_data_text : dirección inicial 0x30001030, tamaño0x000006E8

RW_m_dcd_data_text m_dcd_data_start FIXED m_dcd_data_size { ; load address = execution address
	* (.boot_hdr.dcd_data, +FIRST)
}

Igual que arriba, coloque el encabezado de arranque de L1 BootLoader.

5.VECTOR_ROM : dirección inicial 0x30002000, tamaño0x00000400

VECTOR_ROM m_interrupts_start FIXED m_interrupts_size { ; load address = execution address
	* (.isr_vector,+FIRST)
}

Coloque la tabla de vectores de interrupción. startup_MIMXRT1176_cm7.SEsta sección está definida en el archivo de inicio :, .section .isr_vector, "a"lo que asignifica que la sección está marcada como asignable ( allocatable), lo que significa que se puede asignar a una determinada ubicación en la memoria en el momento del enlace.

6.ER_m_text : dirección inicial 0x30002400, tamaño0x00FFDC00

ER_m_text m_text_start FIXED m_text_size { ; load address = execution address
	* (InRoot$$Sections)
	.ANY (+RO)
}
  • InRoot$$SectionsEs una marca especial que se utiliza en archivos dispersos para colocar segmentos de datos comprimidos en el área de ejecución para garantizar que estos segmentos de datos puedan descomprimirse automáticamente y proporcionarse al programa en tiempo de ejecución. Esta característica está diseñada por ARM para reducir el uso del espacio de almacenamiento. Después del encendido, la biblioteca ARM se descomprimirá de acuerdo con esta sección.
  • .ANY: puede entenderse como que *indica todos los segmentos, pero .ANYse puede usar en múltiples áreas de ejecución, pero *generalmente solo se usa en un área de ejecución, por lo .ANYque es más flexible. Consulte el Capítulo 7.4 del manual para obtener más detalles.<Placement of unassigned sections with the .ANY module selector>
  • (+RO): segmento de datos de solo lectura

Esto significa colocar todos los segmentos de datos de solo lectura en esta área de ejecución.

7VECTOR_RAM .: La tabla de vectores de interrupción se coloca aquí en NOR Flash, esta área de ejecución no se utiliza.

VECTOR_RAM m_interrupts_start EMPTY 0 {
}
  • EMPTYIndica que se reserva un área vacía, pero el tamaño de esta área se establece en 0, por lo que esta área de ejecución no tiene ningún efecto.

8.RW_m_data2 : dirección inicial 0x20000000, tamaño0x00040000

RW_m_data2 m_data2_start m_data2_size {
	* (RamFunction)
	* (DataQuickAccess)
}

Aquí se definen dos secciones: RamFunctiony DataQuickAccess, que se pueden __attribute__((section("")))utilizar para SRAM_DTCM, lo que puede acelerar la ejecución de funciones y el acceso a datos.

9.RW_m_data : dirección inicial 0x80000000, tamaño0x03000000-0x400

RW_m_data m_data_start m_data_size-Stack_Size { ; RW data
	.ANY (+RW +ZI)
	*(*m_usb_dma_init_data)
	*(*m_usb_dma_noninit_data)
}

Esta área de ejecución son los primeros 48 M de SDRAM. Aquí se colocan todos los segmentos de datos de lectura y escritura y los segmentos bss, y se declaran dos segmentos USB al mismo tiempo para la implementación de funciones relacionadas con USB en el SDK. De hecho, el segmento USB non-cacheabledefinitivamente puede ejecutarse si se coloca en la zona, pero también significa que el caché no se usa y la velocidad se reducirá mucho. Por lo tanto, puede declarar variables relacionadas con USB en cacheableel área y luego almacenar en caché y actualizar manualmente las funciones relacionadas en los lugares necesarios del código, como SCB_CleanInvalidateDCachey SCB_CleanDCache.

10.ARM_LIB_STACK : dirección inicial 0x83000000, tamaño0x400

ARM_LIB_STACK m_data_start+m_data_size EMPTY -Stack_Size { ; Stack region growing down
}
  • ARM_LIB_STACK: Nombre fijo del área de ejecución de la pila.

Stack_SizeHay uno delante de aquí -, lo que indica que la pila crece hacia abajo.

11.RW_m_ram_text : dirección inicial 0x00000000, tamaño0x00040000

RW_m_ram_text m_qacode_start m_qacode_size { ;
	* (CodeQuickAccess)
}

Similar al punto 7 anterior, declare un CodeQuickAccesssegmento para vincular funciones a la SRAM_ITCMSRAM interna porque la velocidad de acceso de la SRAM interna es mucho más rápida que la de NOR Flash o SDRAM.

12.RW_m_ncache : dirección inicial 0x83000000, tamaño0x01000000-0x400

RW_m_ncache m_ncache_start m_ncache_size - Heap_Size { ; ncache data
	* (NonCacheable.init)
	* (*NonCacheable)
}

Defina non-cacheablelos dos segmentos del área NonCacheable.inity NonCacheablereserve el espacio del montón al mismo tiempo, porque aquí asumimos que el espacio del montón también lo es non-cacheable.

13.ARM_LIB_HEAP : Tamaño0x400

ARM_LIB_HEAP +0 EMPTY Heap_Size {    ; Heap region growing up
}
  • +0: Representa la dirección final del área de ejecución anterior, RW_m_ncacheesta dirección se determina en función del número y tamaño de las variables colocadas por el usuario en el área de ejecución.
  • ARM_LIB_HEAP: Nombre fijo del área de ejecución del montón.

Define la memoria del espacio del montón.

14 、RW_m_ncache_unused

RW_m_ncache_unused +0 EMPTY m_ncache_size-ImageLength(RW_m_ncache)-Heap_Size { ; Empty region added for MPU configuration
}

También se coloca en la dirección final del área de ejecución anterior para configurar la MPU. El tamaño es el área m_ncache_size-ImageLength(RW_m_ncache)-Heap_Sizerestante non-cacheablefuera del montón excepto las variables.

  • ImageLengthPuedes tomar el tamaño de un área de ejecución.

Supongo que te gusta

Origin blog.csdn.net/tilblackout/article/details/132715348
Recomendado
Clasificación