Ir al contenido



Foto

Callbacks en Linux: método a procedure


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

#1 Gronfi

Gronfi

    Newbie

  • Miembros
  • Pip
  • 8 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

    Newbie

  • Miembros
  • Pip
  • 8 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
  • 540 mensajes
  • Location127.0.0.1

Escrito 15 noviembre 2017 - 02:43

PhytonForDelphi = Opensource Project ?

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

 

Saludos!


  • 0