NobleTech Software Consultancy

PenPot Design

Function Name Lookup

To produce meaningful output from a stack trace you need to find the names of the functions containing the addresses found during the stack walk. To do this you will need to either include debugging symbols in your kernel or load the map file created by your linker into the kernel's memory space. The map file shows the addresses of each of your functions. While you could include the entire map file, it is often quite large and inefficiently stored. Not only this but often functions are not listed in the order that they appear in the object file and the format is not amenable to tracing through to find a specific function.

Map Pre-processing

One possible solution is to pre-process your map file to produce a smaller, more useful format for it. You could do this in a way that allows either binary or linear searching for a particular address. The following C# code shows a way of reading the map file produced by GNU ld and outputting a binary file that allows more efficient linear searching for symbols. If you have .Net Framework 3.5 already installed then here is a 5KB zip file containing a binary for a console application that does the processing and can be called by make as a step in your build.

Dictionary<uint, string> symbols = new Dictionary<uint, string>();
try
{
    using (StreamReader sr = new StreamReader(fileName))
    {
        String line;
        while ((line = sr.ReadLine()) != null)
        {
            int addrStart = line.IndexOf("0x");
            if (addrStart < 0)
                continue;
            addrStart += 2;
            uint addr = uint.Parse(
                line.Substring(addrStart, 8), NumberStyles.AllowHexSpecifier);
            if (addr == 0)
                continue;
            int symbolStart = line.LastIndexOf(' ') + 1;
            symbols[addr] = line.Substring(symbolStart);
        }
    }
}
catch (Exception e)
{
    // Let the user know what went wrong.
   Console.Write(Properties.Resources.FileReadError, fileName, e.Message);
}
Console.Write(Properties.Resources.ReadSymbols, symbols.Count);
if (fileName.EndsWith(".map"))
    fileName = fileName.Substring(0, fileName.Length - 4);
fileName += ".bmap";
try
{
    using (FileStream fs = new FileStream(fileName, FileMode.Create))
    {
        using (BinaryWriter bw = new BinaryWriter(fs))
        {
            foreach (KeyValuePair<uint, string> symbol in symbols.OrderBy(pair => pair.Key))
            {
                bw.Write(symbol.Key);
                bw.Write(symbol.Value.ToCharArray());
                bw.Write((byte)0);
            }
            bw.Write(0u);
        }
    }
}
catch (Exception e)
{
    // Let the user know what went wrong.
    Console.Write(Properties.Resources.FileWriteError, fileName, e.Message);
}

Map Scanning

Given a pre-processed map file in the format produced by the example above, code similar to the following will allow you to look up the name of a function. You will need to load the binary map file into the kernel address space and provide a pointer to the start of the loaded file as pBMap. It's a slow process without a binary search implemented, but you shouldn't need to do it very often.

const char * Debug::GetFunctionName(unsigned int Address)
{
    // Default string returned if Address is before the first function
    const char * fnName = "Before first fn";
    while(true)
    {
        // Read the address of the function
        unsigned int startAddr = *reinterpret_cast<unsigned int *>(pBMap);
        if(startAddr > Address)
            // This one starts after Address, so Address is in the previous fn
            return fnName;
        if(startAddr == 0)
            // We have reached the end of the map
            // Address could still be a valid address within the last function
            //  but it's more likely that something has gone wrong
            return "After last fn";
        // Skip the address to the function name
        pBMap += 4;
        fnName = reinterpret_cast<char *>(pBMap);    // Stored for next iteration
        // Hunt for end of string
        while(*pBMap != 0)
            ++pBMap;
        ++pBMap;    // Skip the final null character
    }
}

Questions?

As always, any questions, comments or improvements to this article are welcomed. Please contact me using the company's contact details.

Hope this helped!
Nathan Phillips.

This article last updated: 08/05/2009