tipos sintaxis rangos programas ejemplos declaracion datos comandos c++ windows code-size

sintaxis - tipos de datos en c++



Obtener el tamaño de una función de C++ (15)

Estaba leyendo this pregunta porque estoy tratando de encontrar el tamaño de una función en un programa C ++. Se sugiere que puede haber una manera que sea específica de la plataforma. Mi plataforma objetivo es windows

El método que tengo actualmente en mi cabeza es el siguiente:
1. Obtener un puntero a la función.
2. Incremente el puntero (y contador) hasta que alcance el valor del código de máquina para ret
3. ¿El contador será el tamaño de la función?

Edit1: para aclarar lo que quiero decir con ''tamaño'' me refiero al número de bytes (código de máquina) que conforman la función.
Edit2: ha habido algunos comentarios que preguntan por qué o qué planeo hacer con esto. La respuesta honesta es que no tengo ninguna intención, y realmente no puedo ver los beneficios de conocer el tiempo de precompilación de las funciones. (aunque estoy seguro de que hay algunos)

Esto me parece un método válido, ¿funcionará?


¿Qué quiere decir "tamaño de una función"?

Si te refieres a un puntero de función, entonces siempre es solo 4 bytes para sistemas de 32 bits.

Si se refiere al tamaño del código, debe desensamblar el código generado y encontrar el punto de entrada y la llamada de ret más cercana. Una forma de hacerlo es leer el registro de puntero de instrucciones al principio y al final de su función.

Si desea calcular la cantidad de instrucciones solicitadas en el caso promedio de su función, puede usar perfiladores y dividir la cantidad de instrucciones retiradas en función de la cantidad de llamadas.


Creo que funcionará en los programas de Windows creados con msvc, ya que para las ramas, la ''ret'' parece llegar siempre al final (incluso si hay ramas que regresan antes, hace un jne para ir al final). Sin embargo, necesitará algún tipo de biblioteca de desensamblador para calcular la longitud del código de operación actual, ya que son de longitud variable para x86. Si no haces esto, te encontrarás con falsos positivos.

No me sorprendería si hay casos en los que no se detecte.


El enfoque no portátil, pero basado en API y que funciona correctamente es usar lectores de bases de datos de programas, como dbghelp.dll en Windows o en Linux. El uso de estos solo es posible si la información de depuración está habilitada / presente junto con el programa. Aquí hay un ejemplo de cómo funciona en Windows:

SYMBOL_INFO symbol = { }; symbol.SizeOfStruct = sizeof(SYMBOL_INFO); // Implies, that the module is loaded into _dbg_session_handle, see ::SymInitialize & ::SymLoadModule64 ::SymFromAddr(_dbg_session_handle, address, 0, &symbol);

Obtendrá el tamaño de la función en symbol.Size , pero también puede necesitar lógica adicional para identificar si la dirección dada es en realidad una función, un shim colocado allí por un enlazador incremental o un thunk call thunk (lo mismo).

Supongo que se puede hacer algo similar a través de readelf en Linux, pero quizás tengas que crear la biblioteca encima de su código fuente ...

Debe tener en cuenta que aunque es posible un enfoque basado en desmontaje, básicamente tendrá que analizar un gráfico dirigido con puntos finales en ret, halt, jmp (PROPORCIONADO, tiene habilitado el enlace incremental y puede leer la tabla jmp para identifique si el jmp que está enfrentando en la función es interno a esa función (falta en la tabla jmp de la imagen) o externo (presente en esa tabla; tales jmps ocurren con frecuencia como parte de la optimización de la llamada de cola en x64, como sé) , todas las llamadas que están destinadas a ser nonret (como un ayudante que genera excepciones), etc.


En C ++, no hay noción de tamaño de la función. Además de todo lo mencionado, las macros del preprocesador también tienen un tamaño indeterminado. Si desea contar el número de palabras de instrucción, no puede hacer eso en C ++, porque no existe hasta que se compila.


Es posible obtener todos los bloques de una función, pero no es una pregunta natural preguntarse cuál es el "tamaño" de una función. El código optimizado reorganizará los bloques de códigos en el orden de ejecución y moverá los bloques utilizados raramente (rutas de excepción) a las partes externas del módulo. Para obtener más detalles, consulte Optimizaciones guiadas por perfil, por ejemplo, cómo Visual C ++ logra esto en la generación de código de tiempo de enlace. Por lo tanto, una función puede comenzar en la dirección 0x00001000, bifurcarse en 0x00001100 en un salto en 0x20001000 y en un ret, y tener un código de manejo de excepciones 0x20001000. En 0x00001110 se inicia otra función. ¿Cuál es el ''tamaño'' de tu función? Se extiende desde 0x00001000 hasta + 0x20001000, pero ''posee'' solo unos pocos bloques en ese lapso. Así que tu pregunta no debe ser contestada.

Hay otras preguntas válidas en este contexto, como el número total de instrucciones que tiene una función (se puede determinar a partir de la base de datos de símbolos del programa y de la imagen), y lo que es más importante, ¿cuál es el número de instrucciones en la ruta del código ejecutado con frecuencia? la función. Todas estas son preguntas que normalmente se hacen en el contexto de la medición del rendimiento y hay herramientas que instrumentan el código y pueden dar respuestas muy detalladas.

Perseguir punteros en la memoria y buscar ret no te llevará a ningún lado, me temo. El código moderno es mucho más complejo que eso.


Esto puede funcionar en escenarios muy limitados. Lo uso en parte de una utilidad de inyección de código que escribí. No recuerdo dónde encontré la información, pero tengo lo siguiente (C ++ en VS2005):

#pragma runtime_checks("", off) static DWORD WINAPI InjectionProc(LPVOID lpvParameter) { // do something return 0; } static DWORD WINAPI InjectionProcEnd() { return 0; } #pragma runtime_checks("", on)

Y luego en alguna otra función tengo:

size_t cbInjectionProc = (size_t)InjectionProcEnd - (size_t)InjectionProc;

Debe desactivar algunas optimizaciones y declarar las funciones como estáticas para que esto funcione; No recuerdo los detalles. No sé si este es un recuento de bytes exacto, pero está lo suficientemente cerca. El tamaño es solo el de la función inmediata; no incluye ninguna otra función que pueda ser llamada por esa función. Aparte de casos extremos como este, "el tamaño de una función" no tiene sentido y es inútil.


Esto no funcionará ... ¿qué pasa si hay un salto, un dummy ret , y luego el objetivo del salto? Su código será engañado.

En general, es imposible hacerlo con el 100% de precisión, ya que tiene que predecir todas las rutas de código, lo cual es como resolver el problema de la detención . Puede obtener una precisión "bastante buena" si implementa su propio desensamblador, pero ninguna solución será tan fácil como lo imagina.

Un "truco" sería averiguar qué código de función está después de la función que está buscando, lo que daría resultados bastante buenos si se asumen ciertas suposiciones (peligrosas). Pero luego tendría que saber qué función viene después de su función, que, después de las optimizaciones, es bastante difícil de entender.

Edición 1:

¿Qué pasa si la función ni siquiera termina con una instrucción ret ? Podría muy bien simplemente regresar a su interlocutor (aunque es poco probable).

Edición 2:

No olvides que x86, al menos, tiene instrucciones de longitud variable ...

Actualizar:

Para aquellos que dicen que el análisis de flujo no es lo mismo que resolver el problema de detención:

Considera lo que pasa cuando tienes un código como:

foo: .... jmp foo

Tendrá que seguir el salto cada vez para averiguar el final de la función, y no puede ignorarlo después de la primera vez porque no sabe si está tratando con un código de auto-modificación. (Podría tener un ensamblaje en línea en su código C ++ que se modifique a sí mismo, por ejemplo). Podría extenderse a algún otro lugar de la memoria, por lo que su analizador terminará (o debería) terminar en un bucle infinito, a menos que tolere falsos negativos.

¿No es eso como el problema de la detención?


Estoy publicando esto para decir dos cosas:

1) La mayoría de las respuestas que se dan aquí son realmente malas y se romperán fácilmente . Si usa el puntero de la función C (usando el nombre de la función), en una compilación de debug de su ejecutable, y posiblemente en otras circunstancias, puede apuntar a una corrección JMP que no tendrá el cuerpo de la función en sí. Aquí hay un ejemplo. Si hago lo siguiente para la función que definí a continuación:

FARPROC pfn = (FARPROC)some_function_with_possibility_to_get_its_size_at_runtime;

el pfn que recibo (por ejemplo: 0x7FF724241893 ) apuntará a esto, que es solo una instrucción JMP :

Además, un compilador puede anidar varias de esas cuñas o ramificar su código de función para que tenga varios epílogos o instrucciones de recuperación . Heck, ni siquiera puede utilizar una instrucción ret . Entonces, no hay garantía de que las propias funciones se compilen y vinculen en el orden en que las define en el código fuente.

Puedes hacer todas esas cosas en lenguaje ensamblador , pero no en C o C ++.

2) Así que lo de arriba fue la mala noticia. La buena noticia es que la respuesta a la pregunta original es, sí, hay una forma (o un hack ) de obtener el tamaño exacto de la función, pero viene con las siguientes limitaciones:

  • Funciona en ejecutables de 64 bits solo en Windows.

  • Obviamente, es específico de Microsoft y no es portátil.

  • Tienes que hacer esto en tiempo de ejecución.

El concepto es simple: utilice la forma en que SEH se implementa en los binarios de Windows x64. El compilador agrega detalles de cada función en el encabezado PE32 + (en el directorio IMAGE_DIRECTORY_ENTRY_EXCEPTION del encabezado opcional) que puede usar para obtener el tamaño exacto de la función. (En caso de que se lo pregunte, esta información se utiliza para detectar, manejar y desenrollar excepciones en los bloques __try/__except/__finally ).

Aquí hay un ejemplo rápido:

//You will have to call this when your app initializes and then //cache the size somewhere in the global variable because it will not //change after the executable image is built. size_t fn_size; //Will receive function size in bytes, or 0 if error some_function_with_possibility_to_get_its_size_at_runtime(&fn_size);

y entonces:

#include <Windows.h> //The function itself has to be defined for two types of a call: // 1) when you call it just to get its size, and // 2) for its normal operation bool some_function_with_possibility_to_get_its_size_at_runtime(size_t* p_getSizeOnly = NULL) { //This input parameter will define what we want to do: if(!p_getSizeOnly) { //Do this function''s normal work //... return true; } else { //Get this function size //INFO: Works only in 64-bit builds on Windows! size_t nFnSz = 0; //One of the reasons why we have to do this at run-time is //so that we can get the address of a byte inside //the function body... we''ll get it as this thread context: CONTEXT context = {0}; RtlCaptureContext(&context); DWORD64 ImgBase = 0; RUNTIME_FUNCTION* pRTFn = RtlLookupFunctionEntry(context.Rip, &ImgBase, NULL); if(pRTFn) { nFnSz = pRTFn->EndAddress - pRTFn->BeginAddress; } *p_getSizeOnly = nFnSz; return false; } }


La solución real para esto es profundizar en la documentación de su compilador. El compilador ARM que usamos se puede hacer para producir un volcado de ensamblaje (code.dis), del cual es bastante trivial restar las compensaciones entre una etiqueta de función mutilada dada y la siguiente etiqueta de función mutilada.

Sin embargo, no estoy seguro de qué herramientas necesitará para esto con un objetivo de Windows. Parece que las herramientas enumeradas en la respuesta a esta pregunta podrían ser lo que está buscando.

También tenga en cuenta que yo (trabajando en el espacio incrustado) asumí que estaba hablando acerca del análisis posterior a la compilación. Todavía podría ser posible examinar estos archivos intermedios mediante programación como parte de una compilación siempre que:

  • La función objetivo está en un objeto diferente.
  • El sistema de compilación ha sido enseñado las dependencias.
  • Usted sabe con seguridad que el compilador construirá estos archivos de objetos

Tenga en cuenta que no estoy completamente seguro de POR QUÉ quiere saber esta información. Lo he necesitado en el pasado para estar seguro de que puedo encajar un trozo particular de código en un lugar muy particular en la memoria. Debo admitir que tengo curiosidad por saber qué propósito tendría esto en un objetivo más general del sistema operativo de escritorio.


No hay instalaciones en el Estándar C ++ para obtener el tamaño o la longitud de una función.
Vea mi respuesta aquí: ¿Es posible cargar una función en alguna memoria asignada y ejecutarla desde allí?

En general, saber el tamaño de una función se usa en sistemas integrados al copiar código ejecutable desde una fuente de solo lectura (o un dispositivo de memoria lenta, como un Flash de serie) en la RAM. El escritorio y otros sistemas operativos cargan funciones en la memoria usando otras técnicas, como bibliotecas dinámicas o compartidas.


No, esto no funcionará:

  1. No hay garantía de que su función solo contenga una única instrucción ret .
  2. Incluso si solo contiene una única ret , no puede simplemente mirar los bytes individuales, ya que el valor correspondiente podría aparecer simplemente como un valor, en lugar de una instrucción.

El primer problema posiblemente se puede solucionar si restringe su estilo de codificación a, digamos, solo tiene un único punto de retorno en su función, pero el otro básicamente requiere un desensamblador para que pueda distinguir las instrucciones individuales.


Solo establece PAGE_EXECUTE_READWRITE en la dirección donde obtuviste tu función. Entonces lee cada byte. Cuando tienes el byte "0xCC" significa que el final de la función es actual_reading_address - 1.


Usando GCC, no es tan difícil en absoluto.

void do_something(void) { printf("%s!", "Hello your name is Cemetech"); do_something_END: } ... printf("size of function do_something: %i", (int)(&&do_something_END - (int)do_something));


Wow, uso el tamaño de la función contando todo el tiempo y tiene muchos usos. ¿Es confiable? De ninguna manera. ¿Es estándar c ++? De ninguna manera. Pero es por eso que necesita revisarlo en el desensamblador para asegurarse de que funcione, cada vez que lance una nueva versión. Las banderas del compilador pueden desordenar el orden.

static void funcIwantToCount() { // do stuff } static void funcToDelimitMyOtherFunc() { __asm _emit 0xCC __asm _emit 0xCC __asm _emit 0xCC __asm _emit 0xCC } int getlength( void *funcaddress ) { int length = 0; for(length = 0; *((UINT32 *)(&((unsigned char *)funcaddress)[length])) != 0xCCCCCCCC; ++length); return length; }

Parece funcionar mejor con funciones estáticas. Las optimizaciones globales pueden matarlo.

PD Odio a las personas, preguntar por qué quieres hacer esto y es imposible, etc. Deja de hacer estas preguntas, por favor. Te hace sonar estúpido. A los programadores a menudo se les pide que hagan cosas no estándar, porque los nuevos productos casi siempre superan los límites de lo que está disponible. Si no lo hacen, su producto es probablemente una repetición de lo que ya se ha hecho. ¡¡¡Aburrido!!!


debajo del código para obtener el tamaño exacto del bloque de funciones, funciona bien con mi prueba runtime_checks deshabilita _RTC_CheckEsp en modo de depuración

#pragma runtime_checks("", off) DWORD __stdcall loadDll(char* pDllFullPath) { OutputDebugStringA(pDllFullPath); //OutputDebugStringA("loadDll.................../r/n"); return 0; //return test(pDllFullPath); } #pragma runtime_checks("", restore) DWORD __stdcall getFuncSize_loadDll() { DWORD maxSize=(PBYTE)getFuncSize_loadDll-(PBYTE)loadDll; PBYTE pTail=(PBYTE)getFuncSize_loadDll-1; while(*pTail != 0xC2 && *pTail != 0xC3) --pTail; if (*pTail==0xC2) { //0xC3 : ret //0xC2 04 00 : ret 4 pTail +=3; } return pTail-(PBYTE)loadDll; };