Lister les méthodes documentées ou non, les variables

Philippe49Philippe49 Membre
avril 2007 modifié dans Objective-C, Swift, C, C++ #1
A la fois une réponse et une question

La réponse
Pour lister tous les symboles (y compris les méthodes documentées ou non documentées) présentes dans la librairie compilée et filtrer les lignes  concernant une classe particulière, on peut exécuter sur le terminal :
nm /System/Library/Frameworks/AppKit.framework/AppKit | grep "TreeController"

La question
Ces méthodes non documentées sont utilisables pour débogguer provisoirement. mais est-il possible de les utiliser pour le code définitif d'une application ?
«1

Réponses

  • schlumschlum Membre
    08:11 modifié #2
    Avec ta méthode, tu n'as pas les arguments... Essaie "otool -ov" ou "class-dump" (qui fait un parsing complet...) plutôt  ;) Le binaire Objective-C stocke les arguments (sous une forme un peu complexe il est vrai), c'est bien pratique.

    C'est bien sûr utilisable dans le code définitif d'une application utilisant cette version du Framework.
  • BruBru Membre
    08:11 modifié #3
    class-dump ne sort que les méthodes Obj-C.
    nm ne sort que les fonctions.

    D'autre part, nm peut sortir toutes les fonctions d'un binaire, même celles (dans le cas d'une lib) qui ne sont pas "exportées" (appelables depuis un autre binaire) : elle sont donc inutilisables (c'est le cas des symboles dont le type -lettre dans la seconde colonne- est en minuscule).

    L'utilisation de ce qu'on appelle undocumented (fais une recherche sur ce mot ici) est toujours risquée : dans le cas des méthodes Obj-C, il est aisé de vérifier leur existence (voire leur signature) afin de les appeler ou non.
    Par contre, pour les fonctions, c'est plus complexe.

    Or selon les versions du système, ces méthodes/fonctions peuvent être modifiées (disparaà®tre, ou changer de signature, ou faire autre chose que ce qu'elles faisaient avant).

    .
  • schlumschlum Membre
    08:11 modifié #4
    Oui, mais les méthodes sont des fonctions, donc vues par "nm"... On peut d'ailleurs les caster en tant que tel avec le type IMP (pointeur de fonction) :

    void (*meth)(id,SEL,id,typ1,typ2);<br />meth = (void(*)(id,SEL,id,typ2,typ1))[object methodForSelector:@selector(nomMethode:arg2:arg3:)];
    
  • schlumschlum Membre
    08:11 modifié #5
    dans 1177753992:
    D'autre part, nm peut sortir toutes les fonctions d'un binaire, même celles (dans le cas d'une lib) qui ne sont pas "exportées" (appelables depuis un autre binaire) : elle sont donc inutilisables (c'est le cas des symboles dont le type -lettre dans la seconde colonne- est en minuscule).


    Il y a deux manières d'utiliser une méthode non documentée : la surcharger ou l'appeler (dans ce cas on a un Warning car elle n'est bien sûr pas dans le .h)
    Pour les fonctions, on peut juste les appeler, mais je ne vois pas comment certaines fonctions peuvent être inutilisables  ???


    Or selon les versions du système, ces méthodes/fonctions peuvent être modifiées (disparaà®tre, ou changer de signature, ou faire autre chose que ce qu'elles faisaient avant).


    Pour ça que j'ai dit "utilisant cette version du Framework" !  :)
  • BruBru Membre
    08:11 modifié #6
    dans 1177754786:

    Oui, mais les méthodes sont des fonctions, donc vues par "nm"... On peut d'ailleurs les caster en tant que tel avec le type IMP (pointeur de fonction)


    Exact.


    dans 1177755114:

    Il y a deux manières d'utiliser une méthode non documentée : [...] l'appeler (dans ce cas on a un Warning car elle n'est bien sûr pas dans le .h)


    Sauf si tu créés une décla de catégorie en haut du source utilisant la undocumented...
    <br />@interface UneClasse(MethodesNonDocumentees) <br />- (void)uneMethodeNonDocumentee;<br />@end<br />
    



    dans 1177755114:

    Pour les fonctions, on peut juste les appeler, mais je ne vois pas comment certaines fonctions peuvent être inutilisables  ???


    Inutilisables, car non "exportées".
    Tu peux créer autant de fonctions que tu veux dans une lib, ce n'est pas pour autant que tu peux les appeler depuis une appli qui s'appuie sur cette lib, si ces fonctions ne sont pas exposées.
    Prends l'exemple de la lib CoreGraphics.
    Un nm de montre 2 symboles pouvant te donner l'impression de récupérer la valeur alpha d'une fenêtre :
    [tt]904a3658 T _CGSGetWindowAlpha
    9060b5e8 t _CGXGetWindowAlpha[/tt]

    La 1ère est exportée : en effet tu peux l'utiliser dans tes applis :
    <br />{<br />&nbsp; &nbsp; float f;<br />&nbsp; &nbsp; CGSGetWindowAlpha([NSApp contextID], [uneFenetre windowNumber], &amp;f);<br />&nbsp; &nbsp; NSLog(@&quot;alpha=%f&quot;,&nbsp; f);<br />}<br />
    

    La 2nde te plantera ton appli (surement sur un SIGABORT), puisque le symbole n'aura pas pu être résolu au chargement de la lib par ton appli.

    D'ailleurs, la plupart des symboles non exposés (local en terminologie informatique) peuvent être purement et simplement supprimer de la table des symboles (qui est utilisée par nm) par la simple commande strip : tu ne les verras plus dans nm !


    dans 1177755114:

    Pour ça que j'ai dit "utilisant cette version du Framework" !  :)


    Oui, c'est pas faux...
    Mais, framework est un peu trop restrictif.
    OS X est composé de beaucoup de lib (statiques ou dynamiques), qui ne sont pas des frameworks.
    C'est donc pourquoi, j'ai évoqué "la version du système" plus que "la version du framework".

    .
  • schlumschlum Membre
    08:11 modifié #7
    dans 1177759429:

    Sauf si tu créés une décla de catégorie en haut du source utilisant la undocumented...
    <br />@interface UneClasse(MethodesNonDocumentees) <br />- (void)uneMethodeNonDocumentee;<br />@end<br />
    


    Bonne idée 

    Inutilisables, car non "exportées".
    Tu peux créer autant de fonctions que tu veux dans une lib, ce n'est pas pour autant que tu peux les appeler depuis une appli qui s'appuie sur cette lib, si ces fonctions ne sont pas exposées.


    Qu'est-ce qui définit une fonction non "exportée" ?
    Parce qu'il me semble qu'en faisant un Framework Cocoa, toutes les méthodes définies dans ce Framework sont accessibles, qu'elles-soient dans le .h ou non, je me trompe ?  ???
  • BruBru Membre
    08:11 modifié #8
    dans 1177760958:

    Qu'est-ce qui définit une fonction non "exportée" ?
    Parce qu'il me semble qu'en faisant un Framework Cocoa, toutes les méthodes définies dans ce Framework sont accessibles, qu'elles-soient dans le .h ou non, je me trompe ?  ???


    Non, tu ne te trompes pas.
    Mais tu restes trop focalisé sur "méthode".

    Ce que montre nm (pour recoller au 1er post de Philippe49), c'est la table des symboles d'un binaire.
    Le symbole peut être une implémentation d'une méthode Obj-C (donc, a priori toujours exportée, quoique...), mais aussi toute autre fonction du binaire (et les variables globales, les constantes, etc...).

    Si, ces symboles (fonctions, variables globales, constantes, etc...) ne sont pas exportés, ils ne seront pas "linkable" par un autre binaire.

    .
  • schlumschlum Membre
    08:11 modifié #9
    Okay...
    C'est les définitions "static" ? (ou je suis encore complètement à  côté de la plaque ?  :o)
  • BruBru Membre
    08:11 modifié #10
    dans 1177762163:

    C'est les définitions "static" ?


    Non.
    static est un mot clé uniquement utilisé par les sources pour restreindre le "scope" d'un symbole lors du linkage final des .o .

    Lis ce post pour plus de détail.


    dans 1177762163:

    (ou je suis encore complètement à  côté de la plaque ?  :o)


    Stop !
    Je ne crois pas avoir lu dans tes interventions quelque chose à  côté de la plaque...
    Sinon, je te garantis que certains d'entre nous te seraient "tombés sur le poil" (moi le premier) pour te remettre dans le droit chemin du cocoaà¯sme, religion d'état sur ce site.

    .
  • schlumschlum Membre
    08:11 modifié #11
    Okay...

    Donc j'ai toujours pas compris comment on définit les fonctions "exportées" et les "non exportées" en codant une bibliothèque...

    Pour reprendre ton exemple :
    904a3658 T _CGSGetWindowAlpha<br />9060b5e8 t _CGXGetWindowAlpha
    


    Est-ce dû à  une différence de définition de CGSGetWindowAlpha et CGXGetWindowAlpha ?
    J'imagine que les deux sont définies dans le .cpp puisqu'elles ne sont pas dans le .h
    Mais comment précise-on qu'une est "utilisable" et l'autre non ?
  • Philippe49Philippe49 Membre
    janvier 2008 modifié #12
    dans 1177751372:

    Avec ta méthode, tu n'as pas les arguments... Essaie "otool -ov" ou "class-dump" (qui fait un parsing complet...) plutôt  ;) Le binaire Objective-C stocke les arguments (sous une forme un peu complexe il est vrai), c'est bien pratique.
    C'est bien sûr utilisable dans le code définitif d'une application utilisant cette version du Framework.


    On trouve class-dump ici
    http://www.codethecode.com/Projects/class-dump/
    Belle surprise  :o: il liste également les variables de la classe  :o

    Un exemple d'utilisation ( -C choix de la classe, -r récursif )
    class-dump -C NSNotification /System/Library/Frameworks/Cocoa.framework -r



  • avril 2007 modifié #13
    class-dump est bien mais il y a plus pratique à  mon goût: MagicHat.
  • Philippe49Philippe49 Membre
    08:11 modifié #14
    dans 1177766297:

    class-dump est bien mais il y a plus pratique à  mon goût: MagicHat.


    :adios!: Avec une interface graphique !!  :kicking:

    class-dump permet les regex, la récursivité, ce qui semble plus difficile avec MagicHat .
  • Philippe49Philippe49 Membre
    08:11 modifié #15
    dans 1177759429:

    dans 1177755114:

    Pour ça que j'ai dit "utilisant cette version du Framework" !  :)


    Oui, c'est pas faux...
    Mais, framework est un peu trop restrictif.
    OS X est composé de beaucoup de lib (statiques ou dynamiques), qui ne sont pas des frameworks.
    C'est donc pourquoi, j'ai évoqué "la version du système" plus que "la version du framework".


    Oui, mais tout cela me semble vrai également pour les méthodes documentées.
    Le problème n'est-il pas plutôt la politique suivie par Apple : prévient-il lorsque les méthodes non documentées sont transformées lors de changements majeurs ?
  • BruBru Membre
    08:11 modifié #16
    dans 1177838372:

    Le problème n'est-il pas plutôt la politique suivie par Apple : prévient-il lorsque les méthodes non documentées sont transformées lors de changements majeurs ?


    Dans un autre monde parallèle, peut être.

    Le propre d'une méthode non documentée, c'est qu'elle n'existe pas (du moins officiellement) !
    Si Apple ne les documentent pas, le programmeur ne devrait pas l'utiliser.
    Ce qui laisse tout loisir à  la firme de Cupertino de modifier comme bon lui semble lesdites méthodes.

    .
  • AliGatorAliGator Membre, Modérateur
    08:11 modifié #17
    Ben en même temps s'ils ne les documentent pas c'est qu'il y a une raison  :o
    C'est justement parce que normalement ce sont des méthodes "privées" que tu n'es pas sensé utiliser.
    C'est pour ça qu'elles n'apparaissent nulle part (dans la doc, j'entends), et qu'Apple se réserve le droit de les modifier sans prévenir, puisqu'officiellement tu ne devrais pas les connaà®tre ni les utiliser ;)
  • Philippe49Philippe49 Membre
    janvier 2008 modifié #18
    Je relance ce post parce que MagicHat ne fonctionne plus sous Lepeurd.
    Avez-vous vu un substitut ?

    class-dump non plus d'ailleurs :

    % class-dump -C NSNotification /System/Library/Frameworks/Cocoa.framework -r
    class-dump: Fat archive (/usr/lib/libgcc_s.1.dylib) does not contain required cpu type: ppc7400.
    /*
    *    Generated by class-dump 3.1.2.
    *
    *    class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard.
    */

    This file does not contain any Objective-C runtime information.

  • psychoh13psychoh13 Mothership Developer Membre
    08:11 modifié #19
    Le problème à  mon avis ça vient peut-être du fait qu'il s'agisse d'Objective-C 2.0 et qu'on n'est pas encore capable de le lire... Ou alors ils ont changé l'architecture interne des fichiers et ne les font plus en Mach-O ce qui fait qu'on ne peut plus regarder dedans.
  • 08:11 modifié #20
    Ou autre possibilité. Si le class-dump du framework Cocoa ne renvoie rien, c'est surtout parce que Cocoa ...ne contient rien. Le framework Cocoa est en quelque sorte un framework de "commodité" pour les frameworks Foundation, AppKit et CoreData.
  • psychoh13psychoh13 Mothership Developer Membre
    08:11 modifié #21
    Non, Renaud, ces 3 frameworks sont compilés dans le framework Cocoa, donc si on fouille dans les fichiers Mach-O on doit pouvoir trouver la définition de toutes les classes des 3 frameworks Foundation, AppKit et CoreData.

    En revanche pour économiser les fichiers, le fichier Cocoa.h du framework fait importe les .h principaux des 3 autres frameworks. Mais avec class-dump on aurait du pouvoir voir la définition de toutes les classes de Cocoa...
    Donc il contient bel et bien quelque, je dirais même qu'il contient tout, sauf les fichiers .h définis autre part.
  • 08:11 modifié #22
    Il faudrait que tu m'expliques comment tu fais pour faire tenir les 61,5Mo du binaire de l'AppKit dans un binaire de 68ko dans ce cas.

    Parce que class-dump fonctionne sous Leopard, mais à  condition "d'attaquer" les frameworks individuels.
  • psychoh13psychoh13 Mothership Developer Membre
    08:11 modifié #23
    Je te présente mes plus plates excuses... En effet, Cocoa ne contient rien, il doit just ey avoir des liens vers les frameworks utilisés.
    D'ailleurs, si on fait un class-dump sur le binaire de l'AppKit, on voit bien toutes les classes définies.

    NB : j'ai fait le teste sur l'AppKit contenu dans les frameworks système, et j'ai utilisé directement le binaire et non pas le dossier.
  • schlumschlum Membre
    08:11 modifié #24
    Je ne vois pas pourquoi l'Obj-C 2.0 empêcherait de le lire...  ???
    À ce niveau du runtime, j'ai des doutes sur le fait qu'il y ait eu des changements majeurs !
  • Philippe49Philippe49 Membre
    janvier 2008 modifié #25
    Il y a eu des changements, parce que la ligne de commande que j'indique fonctionnait auparavant (voir plus haut dans le poste).

    D'après votre expérimentation, il s'agirait d'un dysfonctionnement du -r

    [EDIT] je suis retourné sur le site, et il indique (Mac OS X 10.4+) et la mise à  jour n'est sans doute pas faite.
  • AliGatorAliGator Membre, Modérateur
    janvier 2008 modifié #26
    Je confirme, j'ai pas class-dump mais j'ai fait un coup de "otool -ov" (qui est l'équivalent bien plus light pour explorer le contenu des binaires, à  la grande différence qu'il ne formate pas aussi joliment sous forme de headers et qu'il est moins évolué) alors que je suis sous OSX.4 et pas encore passé à  Léopard :
    $ otool -ov /System/Library/Frameworks/Cocoa.framework/Cocoa  
    /System/Library/Frameworks/Cocoa.framework/Cocoa:
    Objective-C segment
    can't print objective-C information no (__OBJC,__module_info) section

    $ ls -lh /System/Library/Frameworks/Cocoa.framework/Versions/Current/Cocoa /System/Library/Frameworks/AppKit.framework/Versions/Current/AppKit
    -rwxr-xr-x   1 root  wheel       21M Jan 15 20:28 /System/Library/Frameworks/AppKit.framework/Versions/Current/AppKit
    -rwxr-xr-x   1 root  wheel       24K Jan 15 20:28 /System/Library/Frameworks/Cocoa.framework/Versions/Current/Cocoa

    $ $ otool -ov /System/Library/Frameworks/AppKit.framework/AppKit | head -n 50 | tail
                         method_types 0x93899ab8 v16@0:4i8i12
                           method_imp 0x9356eebb -[_NSSparseArray removeSpace:atIndex:]
                          method_name 0x93893b78 insertSpace:atIndex:
                         method_types 0x93899ab8 v16@0:4i8i12
                           method_imp 0x9356f15e -[_NSSparseArray insertSpace:atIndex:]
                          method_name 0x93893b9c removeAllIndices
                         method_types 0x93899ab0 v8@0:4
                           method_imp 0x9356ee9b -[_NSSparseArray removeAllIndices]
                          method_name 0x93893bb0 removeIndex:
                         method_types 0x93899aa4 v12@0:4i8
    Donc sur AppKit ça marche, sur Cocoa ça marche pas car il ne fait en effet que 24K et est donc bien vide (contre 21M pour AppKit...) ;D

    Donc après peut-être qu'avant class-dump savait lire dans les "faux frameworks" comme Cocoa qui ne serait qu'un "wrapper", et que maintenant il ne sait plus... Mais déjà  si tu lancais la commande class-dump sur les frameworks AppKit, CoreData et Fundation, tu pourrais voir si ça marche ;)
  • psychoh13psychoh13 Mothership Developer Membre
    08:11 modifié #27
    dans 1201440837:

    Je ne vois pas pourquoi l'Obj-C 2.0 empêcherait de le lire...  ???
    À ce niveau du runtime, j'ai des doutes sur le fait qu'il y ait eu des changements majeurs !


    Au contraire, il y en a eu beaucoup de modifications du runtime avec Leopard, pour le voir il te suffit de regarder dans le document Objective-C 2.0 Runtime Reference, dans la section Appendix A : Mac OS X 10.5 Delta.
  • schlumschlum Membre
    08:11 modifié #28
    dans 1201441262:

    dans 1201440837:

    Je ne vois pas pourquoi l'Obj-C 2.0 empêcherait de le lire...  ???
    À ce niveau du runtime, j'ai des doutes sur le fait qu'il y ait eu des changements majeurs !


    Au contraire, il y en a eu beaucoup de modifications du runtime avec Leopard, pour le voir il te suffit de regarder dans le document Objective-C 2.0 Runtime Reference, dans la section Appendix A : Mac OS X 10.5 Delta.


    Sur le runtime, oui, mais à  ce niveau du runtime  ??? (écriture des signatures dans le binaire)
  • Philippe49Philippe49 Membre
    08:11 modifié #29
    dans 1201441231:

    Mais déjà  si tu lancais la commande class-dump sur les frameworks AppKit, CoreData et Fundation, tu pourrais voir si ça marche ;)


    Oui sur ces frameworks "sans récursion", cela fonctionne.

    % class-dump -C NSNotification /System/Library/Frameworks/Foundation.framework
    /*
    *     Generated by class-dump 3.1.2.
    *
    *     class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard.
    */

    /*
    * File: /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation
    * Arch: PowerPC 7400 (ppc7400)
    *       Current version: 677.1.0, Compatibility version: 300.0.0
    */

    @interface NSNotificationCenter : NSObject
    {
       void *_impl;
       void *_callback_block[4];
       void *_pad[8];
    }

    + (id)defaultCenter;
    + (void)setNotificationCenterSerializeRemoves:(BOOL)fp8;
    - (id)init;
    ...

    %

  • RB jpRB jp Membre
    08:11 modifié #30
    Oui, mais les méthodes sont des fonctions, donc vues par "nm"... On peut d'ailleurs les caster en tant que tel avec le type IMP (pointeur de fonction) :

    Pas du tout ! Une méthode est une méthode.
    Si certaines méthodes Objective-C possèdent une entrée dans la table de symbole de ton fichier Mach-O, c'est parce que gcc à  fait une optimisation en transformant la méthode en fonction. Toutefois, si tu utilises les capacités objets et les capacités avancées de l'Objective-C sur les classes, alors gcc ne pourra plus transformer tes méthodes en "fonctions", et leur nom ne sera plus présents dans la table de symbole, mais uniquement dans la section "__module_info" qui contient, entre autres, la structure de classe, des propriétés, et des héritages.

    Tu peux créer autant de fonctions que tu veux dans une lib, ce n'est pas pour autant que tu peux les appeler depuis une appli qui s'appuie sur cette lib, si ces fonctions ne sont pas exposées.

    Si si, du moment que le symbole est présent dans la table de symbole, ce qui est le cas des fonctions exportées comme non exportés par défaut (tant que tu n'a pas "stripé").
    Seulement les fonctions système (notamment celle du linkeur dynamique dyld) te refuseront à  priori d'y accéder. Mais du moment que tu as accès à  la structure du header de l'image mach-o chargée en mémoire par le lieur dynamique (mach_header), tu peux très bien parser la table de symboles toi-même (nlist) et récupérer l'adresse de l'élément non exporté.


    Pour ce qui est de l'exportation, en C :
    - le mot clef "static" permet de forcer un symbole d'être global à  un fichier objet (typiquement en .o). Par la même occasion, il permet de dire au compilateur que ce symbole n'est pas exporté (ce qui, donc, ne l'empêchera pas d'être dans la table de symboles).

    - le mot clef "export" permet de forcer un symbole d'être exporté. Ce mot clef est implicitement mis devant les déclarations de fonction, ce qui fait que quand on déclare le prototype de fonction dans un fichier .h, quand gcc tente de compiler le fichier, il ne tente pas de trouver le symbole dans l'objet local : il le laisse en attente de résolution pour le linkage.


  • RB jpRB jp Membre
    08:11 modifié #31
    dans 1201441262:

    Au contraire, il y en a eu beaucoup de modifications du runtime avec Leopard, pour le voir il te suffit de regarder dans le document Objective-C 2.0 Runtime Reference, dans la section Appendix A : Mac OS X 10.5 Delta.

    Oui, c'est plus une modification de la hiérarchie et du contenus des frameworks, du fait de l'évolution du frameworks et du langage.
    Mais ni Mach-O, ni Fat n'ont changés eux. D'ailleurs je ne pense pas qu'ils changent avant très longtemps. La souplesse du Mach-O permet de garder ce format tel quel pour encore longtemps.
Connectez-vous ou Inscrivez-vous pour répondre.