Ir al contenido



Foto

Callbacks en Linux: método a procedure


  • Por favor identifícate para responder
5 respuestas en este tema

#1 Gronfi

Gronfi

    Member

  • Miembros
  • PipPip
  • 22 mensajes

Escrito 13 noviembre 2017 - 04:25

Hola,
estoy probando a pasar un proyecto de Firemonkey a Linux. Evidentemente en Linux funcionaría en modo consola, con lo que he llenado de ifdefs los packages gráficos.
Ya en windows me costó encontrar solución a un problema (solución un poco elegante quiero decir), y es que el proyecto tira de muchas dll's generadas en C, con las que se intercambian mensajes y por tanto hay callbacks (debido a que esas dll's se comunican con un bus de comunicaciones). Bueno hasta ahí no parece que hayan problemas, salvo que los callbacks requieren ser llamados por procedimientos regulares, es decir no se puede pasar por parámetro un método de una clase (si un método estático de una clase, pero estaríamos más o menos en las mismas). El asunto viene que por cada dll puede haber varias instancias de clases, así pues debido a esa restricción el callback a un método regular sería para todas las clases el mismo procedure, lo cual es un problema, enmaraña la solución. Lo ideal, que cada clase tenga su callback particular claro.
Tras buscar encontré que en las librerías de la utilidad madexception había una función que le pasas el método de una instancia de una clase y te devuelve la dirección de la misma, es decir la convierte en un procedimiento regular (pensaba ingenuo de mi que sería algo tan simple como algo parecido a lo siguiente: @InstanciaDeClase.MetodoX). Funciona bien.
Pero claro, al pasar a Linux, llego a ese punto y ooohh no compila, pues accede a la función VirtualAlloc que es del winapi (aparte de los ansistrings), y ya ahí atasco, y eso sin mirar al resto de la función en si, pues es bastante compleja (para mi al menos). La pongo a continuación:
 
 


delphi
  1. function MethodToProcedure(Self: TObject; methodAddr: Pointer; maxParamCount: Integer): Pointer;
  2. var
  3.   stackSpace: Integer;
  4.   s1, s2    : AnsiString;
  5.   pos       : Integer;
  6.   i1        : Integer;
  7. begin
  8.   if maxParamCount < 4 then
  9.     maxParamCount := 4;
  10.   if odd(maxParamCount) then
  11.     stackSpace := (maxParamCount + 2) * 8 // parameters + self + localVar
  12.   else
  13.     stackSpace := (maxParamCount + 3) * 8;        // parameters + self + localVar + alignment
  14.   s1           := #$48 + #$81 + #$ec + #0#0#0#0 + // sub     rsp, $118
  15.     #$48 + #$89 + #$84 + #$24 + #0#0#0#0 +        // mov     [rsp+$110], rax
  16.     #$48 + #$8b + #$84 + #$24 + #0#0#0#0 +        // mov     rax, [rsp+$120]    // read 1st original stack parameter
  17.     #$48 + #$89 + #$44 + #$24 + #$08 +            // mov     [rsp+8], rax       // store as 2nd new stack parameter
  18.     #$48 + #$8b + #$84 + #$24 + #0#0#0#0 +        // mov     rax, [rsp+$128]    // read 2nd original stack parameter
  19.     #$48 + #$89 + #$44 + #$24 + #$10 +            // mov     [rsp+$10], rax     // store as 3rd new stack parameter
  20.     #$48 + #$8b + #$84 + #$24 + #0#0#0#0 +        // mov     rax, [rsp+$130]    // read 3rd original stack parameter
  21.     #$48 + #$89 + #$44 + #$24 + #$18 +            // mov     [rsp+$18], rax     // store as 4th new stack parameter
  22.     #$4c + #$89 + #$4c + #$24 + #$20 +            // mov     [rsp+$20], r9      // store 4th original register parameter as 5th new stack parameter
  23.     #$4d + #$89 + #$c1 +                          // mov     r9, r8             // cycle the register parameters (rcx -> rdx -> r8 -> r9)
  24.     #$49 + #$89 + #$d0 +                          // mov     r8, rdx
  25.     #$48 + #$89 + #$ca +                          // mov     rdx, rcx
  26.     #$66 + #$0f + #$6f + #$da +                   // movdqa  xmm3, xmm2         // cycle the register parameters (xmm0 -> xmm1 -> xmm2 -> xmm3)
  27.     #$66 + #$0f + #$6f + #$d1 +                   // movdqa  xmm2, xmm1
  28.     #$66 + #$0f + #$6f + #$c8;                    // movdqa  xmm1, xmm0
  29.   Integer(Pointer(@s1[4])^)  := stackSpace;
  30.   Integer(Pointer(@s1[12])^) := stackSpace - $8;
  31.   Integer(Pointer(@s1[20])^) := stackSpace + $8;
  32.   Integer(Pointer(@s1[33])^) := stackSpace + $10;
  33.   Integer(Pointer(@s1[46])^) := stackSpace + $18;
  34.   pos                        := Length(s1) + 1;
  35.   SetLength(s1, Length(s1) + (maxParamCount - 4) * 16);
  36.   s2 := #$48 + #$8b + #$84 + #$24 + #0#0#0#0 + // mov     rax, [rsp+$140]
  37.     #$48 + #$89 + #$84 + #$24 + #0#0#0#0;      // mov     [rsp+$28], rax
  38.   for i1 := 1 to maxParamCount - 4 do
  39.   begin
  40.     Integer(Pointer(@s2[5])^)  := $20 + i1 * 8 + stackSpace;
  41.     Integer(Pointer(@s2[13])^) := $20 + i1 * 8;
  42.     Move(s2[1], s1[pos], Length(s2));
  43.     inc(pos, Length(s2));
  44.   end;
  45.   s2 := #$48 + #$8b + #$84 + #$24 + #0#0#0#0 + // mov     rax, [rsp+$110]
  46.     #$48 + #$b9 + #0#0#0#0#0#0#0#0 +           // mov     rcx, methodAddr
  47.     #$48 + #$89 + #$8c + #$24 + #0#0#0#0 +     // mov     [rsp+$110], rcx
  48.     #$48 + #$b9 + #0#0#0#0#0#0#0#0 +           // mov     rcx, self
  49.     #$48 + #$89 + #$0c + #$24 +                // mov     [rsp], rcx         // store "self" as 1st new stack parameter
  50.     #$ff + #$94 + #$24 + #0#0#0#0 +            // call    [rsp+$110]
  51.     #$48 + #$81 + #$c4 + #0#0#0#0 +            // add     rsp, $118
  52.     #$c3;                                      // ret
  53.   Integer(Pointer(@s2[5])^)  := stackSpace - $8;
  54.   Pointer(Pointer(@s2[11])^) := methodAddr;
  55.   Integer(Pointer(@s2[23])^) := stackSpace - $8;
  56.   Pointer(Pointer(@s2[29])^) := Self;
  57.   Integer(Pointer(@s2[44])^) := stackSpace - $8;
  58.   Integer(Pointer(@s2[51])^) := stackSpace;
  59.   s1                         := s1 + s2;
  60.   Result                     := VirtualAlloc(nil, Length(s1), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  61.   Move(s1[1], Result^, Length(s1));
  62. end;

 
Por finalizar, las preguntas:
1-La primera es si alguien tiene quizás algo parecido a esta función ya implementado para Linux    :)
2-La segunda, si alguien tiene idea de como atacar a esa función, vamos de entender qué hace esta función, y si alguien tiene idea de como se puede tocar para que vaya para Linux. Pudiera ser que sea todo cuasi igual, pero la función VirtualAlloc no sabría por qué reemplazarla.
 
Saludos


Editado por egostar, 13 noviembre 2017 - 05:43 .
Agregar etiqueta de CODIGO para una mejor comprensión del tema.

  • 0

#2 Gronfi

Gronfi

    Member

  • Miembros
  • PipPip
  • 22 mensajes

Escrito 14 noviembre 2017 - 02:58

Hola de nuevo,

para quien siga el tema o le genere interés, en cuanto tenga la solución (y probada) la escribiré aquí.

De momento he hecho algunos hallazgos importantes, haciendo búsquedas por repositorios git de librerías disponibles en la red:

  • La primera búsqueda ha sido del equivalente a la función VirtualAlloc, que es la función mmap. Ese equivalente lo he visto en la unidad FastMM_OSXUtil.pas de la librería FastMM, donde tienen una función creada a la que llaman VirtualAlloc que hace la traducción de la del API de Windows a la requerida. Con unos ifdef y la misma llamada tienen así el mismo código, parece elegante.
  • La segunda búsqueda, ya sabiendo el asunto del mmap, ha sido en la librería PhytonForDelphi, donde hay una unidad específica MethodCallback.pas que está creada solo para esta labor de convertir métodos en callbacks, y parece que puede dar luz al tema.

En cuanto tenga algo más específico lo escribo aquí.

Saludos


  • 1

#3 sir.dev.a.lot

sir.dev.a.lot

    Advanced Member

  • Miembros
  • PipPipPip
  • 545 mensajes
  • Location127.0.0.1

Escrito 15 noviembre 2017 - 02:43

PhytonForDelphi = Opensource Project ?

https://github.com/p...r/python4delphi

 

Saludos!


  • 0

#4 Gronfi

Gronfi

    Member

  • Miembros
  • PipPip
  • 22 mensajes

Escrito 03 diciembre 2017 - 02:16

Hola,

comenté que en cuanto tuviera resultados los comentaría por aquí, por si son de interés para alguien:

  1. Utilizando la unidad methodcallback.pas de la librería Python4delphi (que no compila en linux en su ultima version, echando atrás los últimos cambios relacionados con el código que no compila va bien, ya se lo he comentado a los autores) la conversión de métodos a procedimientos/funciones va bien
  2. He tenido curro con otros temas, como conseguir pasar librerías de manejo de pipes de win32/win64 a linux64
  3. Otro tema que quería verificar son las librerías Spring4Dephi, sobre todo los multicastevents, y parece que va bien
  4. Un último tema era crear shared libraries en C bajo linux y todo el proceso para que la compilación de las mismas sea viable en el ide, y ya el proceso está completado

Así pues, básicamente lo más complejo que tenía que pasar, está resuelto :)

Saludos


  • 1

#5 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 13.812 mensajes
  • LocationMéxico

Escrito 03 diciembre 2017 - 08:34

Vaya que está interesantísimo el asunto, seguiremos viendo tus avances amigo Gronfi

 

Saludos.


  • 0

#6 Gronfi

Gronfi

    Member

  • Miembros
  • PipPip
  • 22 mensajes

Escrito 04 diciembre 2017 - 09:19

Si alguno estáis interesados en que me extienda en alguno de los temas, me lo comentáis. Y si alguien tiene algunas experiencias a comentar en el mundo linux-delphi, que comenten sin miedo también


  • 1