Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Objet appelant d'une fonction

12 réponses
Avatar
Rémi Moyen
Bonjour,

Je me pose la question de savoir si il est possible, depuis une
fonction quelconque, de retrouver l'adresse de l'objet qui a appel=E9
cette fonction. Par exemple, dans le code suivant :

void bar() {
// ... some magic code ...
}

class A {
public:
void foo() {bar();}
};

int main(int argc, char** argv) {
A a;
a.foo();
return 0;
}

je voudrais que bar() soit capable de m'=E9crire un truc du genre
"caller address: 0x1234abcd", o=F9 0x1234abcd est l'adresse de 'a' (i.e.
ce que me sortirait un "cout << this << endl;" depuis n'importe quelle
fonction de A).

Une contrainte forte (sinon la solution est triviale !), c'est que je
ne veux surtout pas modifier la signature de bar(), ni imposer
d'autres contraintes (ou alors le moins possible ?) sur A.

Bon, avant d'en dire plus, quelques pr=E9cisions :
- d'abord, c'est pas un vrai cas d'usage, c'est principalement pour
satisfaire ma curiosit=E9 (pas la peine de me dire que c'est du code pas
propre et tout, quoi :-) ) ;
- ensuite, j'utilise g++/Linux et une solution sp=E9cifique =E0 g++ (ce
qui me semble in=E9vitable, au moins sur la piste o=F9 je suis parti de
manipulation de la pile d'appel...) m'ira tr=E8s bien ;
- finalement, je suis bien conscient que rien ne me dit que bar() est
appel=E9e depuis un objet et qu'il y a sans doute plein d'autres cas
plus compliqu=E9s, mais pour la discussion on dira que =E7a n'est pas pire
que mon exemple...

J'ai trouv=E9 les fonctions backtrace(), backtrace_symbols() de g++ qui
me permettent d'inspecter la pile d'appels, ce qui est d=E9j=E0 =E7a. En
m'inspirant, par exemple, du code ici :
http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-=
my-gcc-c-app-crashes
j'arrive facilement =E0 faire en sorte que bar() =E9crive "caller:
A::foo()".

Je me dis que je devrais pouvoir aller plus loin et exploiter
l'adresse de retour qui est fournie par backtrace() (ce qui doit =EAtre
la m=EAme que __builtin_return_address, je suppose ?) pour essayer de
retrouver le pointeur "this", vu qu'il doit =EAtre quelque part dans les
arguments de A::foo() (en dernier avec g++, je crois - mais vu que
A::foo() n'a pas d'arguments, =E7a doit aussi =EAtre le premier ici...).

Je n'y connais pas grand chose en compilation, assembleur... et j'ai
beaucoup de mal =E0 suivre ce que j'arrive =E0 trouver sur internet =E0
propos de esp, esb et autres registres, mais il me semble bien
qu'autour de la __builtin_return_address ou de la
__builtin_frame_address, il doit y avoir l'adresse des param=E8tres de
la fonction, non ?

Malheureusement, j'ai essay=E9 d'afficher la valeur de 'this', ainsi que
les valeurs du pointeur fourni par backtrace() plus ou moins de 0 =E0
100, pour la frame courante, la frame parente, en d=E9r=E9f=E9ren=E7ant deu=
x
fois (si le pointeur indique l'adresse d'un registre qui lui-m=EAme
contient l'adresse de 'this' ?)... Mais toutes les tortures auxquelles
j'ai pu penser n'ont jamais r=E9ussies =E0 faire avouer =E0 ce pointeur la
valeur de 'this'. Zut, peste et crotte, me voila bien emb=EAt=E9 !

Donc, est-ce que quelqu'un =E0 une id=E9e de si, et comment, je peux
exploiter cette adresse pour retrouver les arguments d'une fonction
quelconque dans la pile, et en particulier 'this' ? Ou est-ce que
quelqu'un =E0 une id=E9e de comment faire ce que je demande (si c'est
possible...) ?

Merci d'avance !
--
R=E9mi Moyen

2 réponses

1 2
Avatar
Alain Ketterlin
Rémi Moyen writes:

Pour les arguments, oublie. Selon le compilo, l'ABI, etc. ils seront
placés soit dans des registres, soit dans la pile (soit dans les de ux).



Bah c'est sûr que je n'aurais jamais un truc générique ni même
robuste. Mais pour un compilateur, un jeu d'options, est-ce qu'il n'y
a pas une certaine logique ?



Si, bien sûr. Si tu contrôles tu peux espérer retrouver des choses. Par
exemple, gcc en -O0 (ou avec -fno-omit-frame-pointer) va conserver un
frame pointer, c'est-à-dire que rbp va toujours pointer sur le dé but de
la pile (et c'est juste en dessous que se trouve l'adresse de retour).
L'une des premières réponses à ton message sa basait aussi d essus. Et
backtrace() compte là-dessus aussi.

Je parlais aussi de 'this' parce que d'une part c'est celui qui m'a
gratouillé le cerveau en premier, mais aussi d'autre part parce que
j'imagine (peut-être à tort ?) qu'il doit être traité de la même
manière dans toutes les fonctions (pour un compilateur, options, etc.
spécifique, toujours). Ça me semble un peu plus constant que des
arguments qui effectivement ne sont pas forcément toujours stockà ©s de
la même manière.



Oui tu as raison. Par exemple, l'ABI x86-64 (disponible à
http://www.x86-64.org/, dans la rubrique "Documentation") précise que le
premier paramètre de type INTEGER (qui couvre aussi les pointeurs) est
passé dans le registre %rdi. Etc. Mais...

Ah, donc si je suis dans g() je dois pouvoir retrouver où est le
'this' courant (n), mais même en jouant avec la backtrace, il n'est
pas évident que le 'this' dans f() soit à un endroit prédi ctible...



Voilà c'est exactement ça (c'est même sûr qu'il aura été "caché" quelque
part pour faire de la place pour l'appel à g()). La seule solution c'e st
effectivement les informations de debug, pour retrouver où il est
stocké. Et là on met le doigt dans l'usine à gaz (qui s'appe lle dwarf).
Il y a bien une libdwarf pour aider, à
http://reality.sgiweb.org/davea/dwarf.html, mais manifestement c'est pas
fait pour les premiers communiants... Il faut vraiment en avoir besoin
pour s'y plonger (ça n'a jamais été mon cas, donc je ne peux pas t'en
dire plus).

-- Alain.
Avatar
Rémi Moyen
On Nov 9, 4:23 pm, Alain Ketterlin
wrote:

[snip]

Et là on met le doigt dans l'usine à gaz (qui s'appelle dwarf).
Il y a bien une libdwarf pour aider, àhttp://reality.sgiweb.org/davea/d warf.html, mais manifestement c'est pas
fait pour les premiers communiants... Il faut vraiment en avoir besoin
pour s'y plonger (ça n'a jamais été mon cas, donc je ne peux pas t' en
dire plus).



D'accord, je vois. Et c'est ce que je ne voulais absolument pas faire
(au sens où pour satisfaire ma curiosité, je n'ai pas envie d'aller
jusque là !).

Merci pour tes explications, je crois que je comprends mieux.
--
Rémi
1 2