This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

How to find out the memory address for loading symbols for dynamically linked libraries?

Hi,

We have DSP SW running on Keystone II device using dynamic loader as described in http://processors.wiki.ti.com/index.php/C6000_Dynamic_Linking.

DSP loads dynamic libraries through ARM but since the libraries are dynamic their memory location is not that obvious. 

Is there a certain variable in dynamic loader implementation which would tell me the exact address where I could load the debug symbols (.out file) for the dynamic library in CCS?

regards,

Marko

  • Hello Marko,

    In the reference implementation of the dynamic loader, the function DLIF_allocate() is used to get a chunk of DSP memory where each segment of a dynamic library is going to be loaded. On the client side of the dynamic loader (dlw_client.c) DLIF_allocate() calls DLTMM_malloc() to allocate each chunk of target memory (DLTMM indicates the Dynamic Loader Target Memory Manager). DLTMM_malloc() in turn calls trg_alloc() which will find a free target memory packet on the list of TRG_PACKET objects to which trg_mem_head points.

    One alternative is to set up your own map of dynamically loaded modules that gets updated each time DLIF_allocate() or DLIF_free() is called.

    Another possibility is to adapt the dynamic loader's generation of the "DLL View" debug list so that it works with a dynamic loader that is running on the host (the reference implementation requires that the dynamic loader resides and runs on the DSP target and defines a symbol called "_DLModules" in order to generate the DLL View debug list). See dlw_debug.[ch] for details on the DLL View debug list.

    Hope this helps.

    Regards,

    Todd Snider

    C6000 Code Generation Tools Team

    Texas Instruments Incorporated

  • Thanks for the tips. Let me just double check a few details.

    If I want to track down the address where the dll is loaded, should I be looking at TARGET_ADDRESS in DLOAD_MEMORY_SEGMENT? Is that the address I should be using when loading in the .out file with debug symbols? 

    struct DLOAD_MEMORY_SEGMENT
    {
       uint32_t       target_page;          /* requested/returned memory page    */
       TARGET_ADDRESS target_address;       /* requested/returned address        */
       uint32_t       objsz_in_bytes;       /* size of init'd part of segment    */
       uint32_t       memsz_in_bytes;       /* size of memory block for segment  */
    };

    I would like to make sure that I get the symbols to the right address manually before thinking about more complicated solutions such as the one implemented in dlw_debug.

    regards,
    Marko



  • Hi Marko,
    Yes, "target_address" is the address of the memory allocated at dynamic load time to hold whatever is being loaded.
    If you consider the code in dlw_debug.[ch], this might prove useful to you in getting the exact location of each loaded segment. Look for references to "DLL_debug" in dlw.c and dlw_client.c. When DLL_debug is enabled, the dynamic loader keeps a record of what modules are loaded and where they are. While a module is in the process of being loaded, a record for it is created and pushed onto the top of a "context stack" (allowing for dependent modules to be loaded as well). When the load of a particular module is completed, its identity and location are recorded in the debug record for it, that debug record is then popped off the context stack and added to the "mirror_debug_list".
    If you were to maintain the context stack and mirror_debug_list in host (ARM) memory, then you should be able to identify where any given loaded module is located. There is a helper function, DLDBG_dump_mirror_debug_list(), that will write out the content of the mirror_debug_list at any given time.
    Hope this proves helpful.Regards,Todd Snider
  • Hi there,

    One more thingy. I was hoping to have a simple solution that would work for dynamic libraries in the same way as "load symbols" in CCS works for static .out files. Is it even possible to use CCS to load symbols of a dynamic library to a single memory location defined by a code offset in "load symbols" dialog box? Or do I have to  somehow manually parse the .out file segment by segment and load the symbols accordingly?

    Marko

  • Hi Marko,
    If you are truly doing dynamic linking and loading, then the load address of any loaded segment will be determined at load time.
    If you are dealing with a static executable, then its load address is determined during the static link. For such an output file, you can find the actual load address of a given program segment in the program header table.
    I am not aware to what extent CCS supports dynamic linking, if at all.Regards,
    Todd Snider
  • Well, well. My question from the day one has been about using CCS to load symbols for dynamic libraries. And now after two months you are saying that you don't know anything about the topic. I am quite disappointed I must say. If you are not able to help, can you please forward this question to someone who can.

    I can dig out the memory location where segments are loaded but how do I get the debug symbols imported to CCS so that I can actually debug/step through the C code in dynamic library? Disassembler debugging works of course.

    Marko

  • Hi Marko,
    I apologize for the misunderstanding. I had been under the impression that you were using the reference implementation of the dynamic loader to do dynamic linking and loading. That is the body of source code that the C6000_Dynamic_Linking wiki article references as an example of the Bare Metal Dynamic Linking Model.

    Are you running the reference implementation of the dynamic loader on CCS? or are you trying to use CCS to perform dynamic linking and loading? Perhaps this is the crux of my confusion.
    Regards,Todd Snider
  • Thanks for coming back on this. Let me try to give you a bit more information so that we are on the same page.

    We have a DSP base image which is more or less normal, statically linked image which runs on each DSP core. ARM runs linux and DSP base image is loaded to DSP cores using mpm Linux user space application. Once this is done, I can connect CCS to target HW and load symbols for the base image (.out file) and step/debug the code normally.

    The DSP base image has capability of loading and unloading dynamic libraries when needed. These dynamically linked DSP libraries are stored in Linux file system and ARM hosts to file service so that DSP can request libraries when needed. Eventually libraries are loaded and DSP can call any functions which are in the dynamically liked libraries. This all is implemented based on the TI's reference implementation.

    The issue I am having with the dynamically linked libraries is that when I connect CCS to target HW and step from the base image to dynamic loader and then to dynamically linked library, I don't have the debug symbols available. I can step through the disassembly but that is quite painful. If that was a static library I would just use "load symbols" from CCS and load the missing debug symbols to give me access to C source view and variables (just like with the base image). The question is, how do I get the debug symbols (function names, source view, variables names etc.) for dynamically linked libraries? CCS allows loading of symbols (from .out file) to a specific address (code offset) and I was hoping that that would be the solution but apparently it is not that simple. 

    I don't know if the dynamic loader has all the ELF segments in the same order and back to back in a way that the loading of symbols with a give start address would even work.

    regards,

    Marko

  • Placeholder for the missing piece of information seems to already be in wiki page: http://processors.wiki.ti.com/index.php/C6000_Dynamic_Loader#DLL_Debug_Support

    Marko
  • Hi Marko,
    The discussion from my earlier response (Jan 5 @ 1:52pm) is referring to the "DLL Debug Support" in the reference implementation of the dynamic loader (RIDL). This might help you to determine where in target memory a given DLL is loaded. However, I think the essential problem is that CCS is only giving you visibility into the data structures of the RIDL, CCS does not have direct access to the dynamic symbols that get loaded *by* the RIDL. The RIDL does have access to these symbols via the symbol tables attached to a loaded dynamic module (see DLIMP_Dynamic_Module declaration in dload.h of the RIDL sources).
    I speculate (since I have not actually tried to debug dll code via a CCS that is running the RIDL that was used to load the dll) that you might be able to use CCS to get to the loaded dll module's symbol table in RIDL to find dynamically loaded symbols, but this doesn't sound like an effective solution to me. You may have better luck annotating the RIDL to write to stdout the dll's load address and its symbol information as it is loaded by the RIDL. Once you know the location of a given symbol, you should be able to watch that location in CCS while the dll code executes.
    Todd
  • Hi Marko,

    Marko Moberg said:
    My question from the day one has been about using CCS to load symbols for dynamic libraries

    CCS does not have any default support for this. However this can be accomplished by using some custom scripts that can determine which libraries have been loaded by the dynamic loader and load the appropriate debug symbols for debug. I once created such a script long time ago for a customer (with help from Todd). Unfortunately it is no longer supported but I'd like to attach it here because I think it may still be useful in your case too. The script is well documented with the instructions in the comments too. I've attached it here (it is a *.js file that I renamed as a *.txt):

    dllView_elf.txt
    /* 
    * dllView_elf.js
    * 
    * dllView.js port to support ELF. This script can determine which dll has been 
    * loaded by the dynamic loader and will automatically load the appropriate 
    * symbols needed for debug. 
    *
    * Versions  Author
    * 0.1       Ki-Soo Lee
    * 0.2       Ki-Soo Lee
    * 0.3       Ki-Soo Lee
    * 0.4       Ki-Soo Lee
    * 
    * ==============================================================================
    *
    * Dependencies:
    * 1) CCS v4.2.1 or greater
    * 2) Application built with a compiler version which supports dynamic linking
    * 3) Dynamic loader is being used
    * 4) Base image built with DLL debug support enabled
    *
    *
    * This script is meant to be used from the CCS4 Scripting Console. Load this 
    * file into the  Scripting Console during an active debug session using the 
    * loadJS command: 
    * 
    * js:> loadJSFile C:/path/to/dllView_elf.js
    * 
    * To have the script permanently loaded (to avoid having to load it for every
    * new debug session):
    * 
    * js:> loadJSFile C:/path/to/dllView_elf.js true
    * 
    * Once loaded, the folllowing functons in this script can be called from the
    * 'scripts' menu:
    * 
    * "DLLView-Attach to DSP": Connect to the target
    * "DLLView-Load Base Image Symbols": Load the base image symbols
    * "DLLView-Load DLL Symbols": Load symbols for currently loaded DLLs
    * "DLLView-Delete DLL Symbols": Remove symbols of all loaded DLLs
    * "DLLView-Delete All Symbols": Remove all loaded symbols (including base image symbols)
    *
    * Usage:
    * Load the symbols:
    *   Scripts -> DLLView-Load Base Image Symbols (if code already loaded)
    *   Scripts -> DLLView-Load DLL Symbols
    *
    * Note: 
    * 1) "DLLView-Load Base Image Symbols" would be used if the code is already 
    *    loaded (like with an OMAP device using DSP Bridge where the ARM would load 
    *    the code on to the DSP).
    * 2) The �DLLView-Load DLL Symbols� function has to be re-run whenever a new 
    *    DLL is loaded by the dynamic loader.
    *
    */
    var foo;
    //The 'imagepath' and 'symbolFileFolder' variables will need to be defined
    //before running the script functions. You can hardcode them here or set them 
    //from the Scripting Console:
    //Ex:     
    //js:> imagepath="C:/path/to/baseimage.out;
    //js:> symbolFileFolder=C:/path/to/symbolfiles"
    
    var imagepath = "C:/path/to/baseimage.out";
    //var symbolFileFolder="C:/path/to/symbolfiles";
    var symbolFileFolder="C:/unified_dyn_loader";
    
    // Use active debug session. This assumes that the user has started a debug
    // session in CCS4 and running this script from the Scripting Console. This is
    // the expected use case 
    var debugSession = activeDS;
    
    var filepath="";
    var ldCount=0;
    var ldSymbols = new Array();
    
    
    /**
     * Connect to the target non-intrusively
     * @param none
     */
    function dllView_attachToDSP()
    {
        // currently have to configure CCS4 to enable legacy real-time mode for 
        // non-intrusive connect
        debugSession.target.connect();  
    }
    
    
    /**
     * Load the base image symbols
     * @param none
     */
    function dllView_loadBaseImageSymbols()
    {
        // Load the base image symbols (but not the code, that's loaded from the 
        // ARM side of DSP/BIOS Bridge) from the ELF base image (imagepath) you have 
        // indicated. This image must of course correspond to the ELF image loaded 
        // by the ARM side of DSP/BIOS Bridge. 
        //
        // (Note: that the DSP is still running after this has been executed.)
        //
        // If the code is manually loaded (with CCS), then the symbols will be 
        // loaded also, eliminating the need to load the symbols for the base image
    
        // needs 'imagepath' to be properly set        
        print("imagepath: " + imagepath);
    
        debugSession.symbol.load(imagepath);
    }
    
    
    /**
     * Load symbols for currently loaded DLLs
     * @param none
     */
    function dllView_loadDynamicNodeSymbols()
    {
        /* 
        * "Load dynamic node symbols" will read the dynamic loader entry table on 
        * the target and determine the name of the ELF file it should load to get 
        * the symbols. 
        *
        * The Module Debug List Header data object provides the location and size of 
        * the first Module Debug Record on the list.  A Module Debug List Header 
        * object looks like this:
        *
        * +---------------------------------------------+
        * | target address of first module debug record | 32-bits unsigned
        * +---------------------------------------------+
        * | size (bytes) of first module debug record   | 16-bits unsigned 
        * +---------------------------------------------+
        * | update flag                                 | 16-bits unsigned
        * +---------------------------------------------+
        */
        var foo;
        // delete previously loaded module symbols
        dllView_deleteSymbols();
    
        if (symbolFileFolder=="")
        {
          print("Please specify the 'symbolFileFolder' variable to the folder location of the symbol files");
        } else {
          print("symbolFileFolder: " + symbolFileFolder);
    
          //var dll_info_head_addr="__TI_DLL_Debug";    
          var dll_info_head_addr="_DLModules";   
                
          print("dll_info_head_addr: " + dll_info_head_addr);
    
          // get address of module debug list header
          try {
            var dl_modules_addr = debugSession.symbol.getAddress(dll_info_head_addr);      
          } catch (ex) {
            print("ERROR: '" + dll_info_head_addr + "' symbol not found!");
            return;
          }
          
          print("dl_modules_addr: 0x" + dl_modules_addr.toString(16));
      
          // get address of first module debug record
          var dll_info_head = debugSession.memory.readData(0, dl_modules_addr, 32);
      
          print("dll_info_head: 0x" + dll_info_head.toString(16));
      
          if (dll_info_head==0)
          {
              print("*********************************");
              print("**** No Dynamic Node Loaded *****");
              print("*********************************");
          }
      
          // keep loading symbols for all records until finished
          while (dll_info_head!=0)
          {
              print("");
          
              // load symbols for specified debug module
              dllView_dynLoad(dll_info_head);
    
              // get address of next module debug record
              var dll_info_head = debugSession.memory.readData(0, dll_info_head, 32);
              
              ldCount++;
          }
        }
    }
    
    
    /**
     * Remove all loaded symbols (including base image symbols)
     * @param none
     */
    function dllView_deleteAllSymbols()
    {
        // remove all symbols
        // need a DSS API. Use GEL for now
        debugSession.expression.evaluate("GEL_UnloadAllSymbols()");
        
    }
    
    
    /**
     * Remove symbols of all loaded DLLs
     * @param none
     */
    function dllView_deleteSymbols()
    {
        // remove previously loaded symbols
        // need a DSS API. Use GEL for now
        var i=0;
    
        for(i=0;i<ldCount;i++)
        {
            debugSession.expression.evaluate('GEL_SymbolRemove("' + ldSymbols[i] + '")');
        }
        
        ldCount = 0; 
    }
    
    
    /**
     * Load symbols for specified DLL
     * @param dll_info_ptr Address of DLL record
     */
    function dllView_dynLoad(dll_info_ptr)
    {    
        /* 
        * Called by dllView_loadDynamicNodeSymbols(). Gets the load/run addresses 
        * for each segment in the module debug record and calls dllView_ldDll()
        * 
        * The DLL Module Debug List is a singly linked list of 
        * "module debug records".  Each module debug record in the list
        * is associated with an object module that is dynamically linked 
        * and loaded.  
        * 
        * The major difference between the DLL debug support in the DOFF 
        * dynamic loader and the ELF dynamic loader is in the details of
        * the module debug record that is generated for each module.  
        * 
        * Whereas the DOFF dynamic linker provided the name of each section
        * in the module and the section's load and run addresses, the ELF 
        * dynamic loader provides the load and run addresses for each 
        * *segment* that occupies space in target memory.
        * 
        * ELF segments do not have names, so the only string appearing at the
        * end of a given module debug record will be the module name.  The 
        * segment addresses will be listed in the module debug record in the
        * same order that the segments occur in the program header table of 
        * the ELF object file.
        * 
        * Here is the ELF version of the example module debug record 
        * (mr_pink.dll with 3 segments):
        * 
        * +--------------------------------------------+
        * | target address of next module debug record | 32-bits unsigned
        * +--------------------------------------------+
        * | size (bytes) of next module debug record   | 16-bits unsigned 
        * +--------------------------------------------+
        * | tool version identification                | 16-bits unsigned
        * +--------------------------------------------+
        * | verification word                          | 16-bits unsigned
        * +--------------------------------------------+
        * | number of segments                         | 16-bits unsigned
        * +--------------------------------------------+
        * | timestamp                                  | 32-bits unsigned
        * +--------------------------------------------+
        * | load address of 1st segment                | 32-bits unsigned
        * | run address of 1st segment                 | 32-bits unsigned
        * +--------------------------------------------+
        * | load address of 2nd segment                | 32-bits unsigned
        * | run address of 2nd segment                 | 32-bits unsigned
        * +--------------------------------------------+
        * | load address of 3rd segment                | 32-bits unsigned
        * | run address of 3rd segment                 | 32-bits unsigned
        * +--------------------------------------------+
        * | "mr_pink.dll"                              | 12 bytes
        * +--------------------------------------------+
        */
    
        print("**** Module Infomation ****");
        print("dll_info_ptr: 0x" + dll_info_ptr.toString(16));
        
        // Get number of sections for the module
        var num_sects = debugSession.memory.readData(0, (dll_info_ptr + (5 * 2)), 16);
        
        // load address of first section
        var sect_addr_ptr = dll_info_ptr + (8 * 2);
        
        // start address of module name string
        var module_name_ptr = sect_addr_ptr + ((num_sects * 4) * 2);
        
        // get the a string with the name of the module
        var module_name = dllView_dataString(module_name_ptr);
        
        // start address of the first name of the list of modules names
        //var sect_name_ptr = module_name_ptr + module_name.length + 1; 
        
        var reloc_info = "";
        
        print("num_sects: " + num_sects);
        print("module_name: " + module_name);
        
        print("** Segment Details **");    
        
        var index = 0;
        
        var sect_num_arr = new Array();
        var sect_addr_ld_arr = new Array();
        var sect_addr_run_arr = new Array();   
        
        // get the name and load/run address of each section and add to array
        while (index!=num_sects)
        {
            var sect_num = index+1;
            var sect_addr_ld = debugSession.memory.readData(0, sect_addr_ptr, 32);
            var sect_addr_run = debugSession.memory.readData(0, (sect_addr_ptr + 4), 32);
            
            print("Segment " + sect_num + "--addr ld: 0x" + sect_addr_ld.toString(16));
            print("Segment " + sect_num + "--addr run: 0x" + sect_addr_run.toString(16));        
            
            // increment pointer to start of next load address
            sect_addr_ptr = sect_addr_ptr + (4 * 2);
                    
            // put section information in arrays
            sect_num_arr[index] = sect_num;
            sect_addr_ld_arr[index] = sect_addr_ld;
            sect_addr_run_arr[index] = sect_addr_run;        
            
            index = index + 1;        
        }
        
        dllView_ldDll(module_name, sect_num_arr, sect_addr_ld_arr, sect_addr_run_arr);
    }
    
    
    /**
     * Loads symbols with relocation information
     * @param filename DLL name
     * @param sect_num_arr Array of segment numbers
     * @param sect_addr_ld_arr Array of segment load addresses
     * @param sect_addr_run_arr Array of segment run addresses  
     */
    function dllView_ldDll(filename, sect_num_arr, sect_addr_ld_arr, sect_addr_run_arr)
    {
        print("filename: " + filename);
    
        // filename without extension
        matchResults = filename.match(/(\w+).(\w+)/);
    
        var basename = RegExp.$1;
    
        print("basename: " + basename);
    
        if (debugSession.symbol.exists(basename));
        {
            // XXXTODO - remove just that particular symbol or all symbols?
            // remove all symbols from filename
            debugSession.expression.evaluate('GEL_SymbolRemove("' + filename + '")');
        }
        
        // needs 'symbolFileFolder' to be properly set    
        filepath = symbolFileFolder + "/" + filename;
        
        print("filepath: " + filepath);
        
        // check to see if the file path + file exists
        var filePath = new java.io.File(filepath);  
        var result = filePath.exists();   
      
        if(!result)
        {
            print(filepath + " does not exist! Please set 'symbolFileFolder' path to a valid location!"); 
        } else {
            // load symbols
        
            // if relocation information in symbol file    
            //debugSession.symbol.load(filepath);  
            
            // else need to specify relocation information
            var i;
            
            var cpu_name = debugSession.getCPUName();
            var board_name = debugSession.getBoardName();
            
            var load_start;
            var run_start;        
            var seg_run_addresses = "";
            
            // get load/run addresses for segments
            for(i=0;i<sect_num_arr.length;i++)
            {
                print("segment_num: "+ sect_num_arr[i]);
            
                load_start = sect_addr_ld_arr[i];
                print("load_start: 0x"+ load_start.toString(16));
                  
                run_start = sect_addr_run_arr[i];
                print("run_start: 0x"+ run_start.toString(16));
                
                seg_run_addresses = seg_run_addresses + ',' + run_start;
                
            }
    
            // Load symbol information with relocation information
            // Use GEL function until equivalent DSS API available
            
            // A new GEL function for ELF support is added:
            // GEL_SymbolAddELFRel( file_name, cpu_name, board_name, segment_run_address, ...)
    
            // The function accepts the elf file name, cpu name and the board name 
            // and a number of segment_run_address. Note  that the number of segment_
            // run_address are not fixed        
            debugSession.expression.evaluate('GEL_SymbolAddELFRel("' + filepath + '","' + cpu_name + '","' + board_name + '"' + seg_run_addresses + ')');
            
            ldSymbols[ldCount] = filepath;
        }
    }
    
    
    /**
     * Read zero-terminated string from memory, the result is a string.
     * @param stringaddr
     */
    function dllView_dataString(stringaddr)
    {
        var ch="";
        var str="";
    
        while( (debugSession.memory.readData(0, stringaddr, 8) != 0 ) )
        {    
            ch = String.fromCharCode( debugSession.memory.readData(0, stringaddr, 8) );
            stringaddr++;
            str = str + ch;
        }
        
        return str; 
    }
    
    
    // define scripts hotmenu entries
    hotmenu.addJSFunction("DLLView-Attach to DSP", "dllView_attachToDSP()");
    hotmenu.addJSFunction("DLLView-Load Base Image Symbols", "dllView_loadBaseImageSymbols()");
    hotmenu.addJSFunction("DLLView-Load DLL Symbols", "dllView_loadDynamicNodeSymbols()");
    hotmenu.addJSFunction("DLLView-Delete DLL Symbols", "dllView_deleteSymbols()");
    hotmenu.addJSFunction("DLLView-Delete All Symbols", "dllView_deleteAllSymbols()");
    

    Note that the script was written for CCSv4 (hence all the references to it in the comments) but it should still apply to later versions of CCS.

    Thanks

    ki

  • Thanks for the script file. I finally had a chance to try it out and it has been very helpful. I had to add a few debug related files from the reference implementation to our dynamic loader implementation but it seems to be working now (more or less).

    Is it really true, that CCS requires that all the objects files have .out extension? At least that seems to be the case. Our dll's were named differently which made GEL_SymbolAddELFRel function fail. But after renaming the library files as .out files the loading seems to be working.

    I'll run a few final tests and then mark this thread as answered/verified.

    regards,

    Marko

  • Marko Moberg said:
    Is it really true, that CCS requires that all the objects files have .out extension? At least that seems to be the case. Our dll's were named differently which made GEL_SymbolAddELFRel function fail. But after renaming the library files as .out files the loading seems to be working.

    No, the GEL call should be able to handle other extension names. What extension name are you using? Can you describe the failure?

  • I am not in the office right now so I can not get you the exact error message, but it is very much the same what you get when trying to load symbols from a file not ending with .out using commands from CCS menu (Load Symbols). The message tells quite clearly that the file should be .out. We have been using .lib extension but it seems that we need to change them to .out to workaround this issue. I am using CCS 5.5 on Linux.

    I'll write down the exact message and steps reproduce the issue tomorrow.
    Marko
  • I see now. The GEL call will accept any extension except .lib. So you can rename the extension to anything else. .lib. It is not the GEL call itself that has this limitation but somewhere in the debugger itself since .lib is a reserved extension name.

    Thanks

    ki