0

I'm preparing a script for PDB information extraction. Currently I cannot use info variable even once because of its short lifetime.

I get 'borrowed value does not live long enough' error with the code below.

I understand this happens because of info.symbols()? borrow, but I ran out of ideas on how this could be fixed.

How can I append the items into mod_symbols?

Relevant library code (github.com/willglynn/pdb):

pub struct PDB<'s, S> {
    msf: Box<dyn Msf<'s, S> + 's>,
    dbi_header: Option<DBIHeader>,
    dbi_extra_streams: Option<DBIExtraStreams>,
}

impl<'s, S: Source<'s> + 's> PDB<'s, S> {
    pub fn module_info<'m>(&mut self, module: &Module<'m>) -> Result<Option<ModuleInfo<'s>>> {
        Ok(self
            .raw_stream(module.info().stream)?
            .map(|stream| ModuleInfo::parse(stream, module)))
    }
}


pub struct ModuleInfo<'s> {
    stream: Stream<'s>,
    symbols_size: usize,
    lines_size: LinesSize,
}

impl<'s> ModuleInfo<'s> {
    /// Get an iterator over the all symbols in this module.
    pub fn symbols(&self) -> Result<SymbolIter<'_>> {
        let mut buf = self.stream.parse_buffer();
        buf.truncate(self.symbols_size)?;
        if self.symbols_size > 0 {
            let sig = buf.parse_u32()?;
            if sig != constants::CV_SIGNATURE_C13 {
                return Err(Error::UnimplementedFeature(
                    "Unsupported symbol data format",
                ));
            }
        }
        Ok(SymbolIter::new(buf))
    }
}

My script:

fn dump_pdb<'a>(filename: &str, imagebase: u64) -> pdb::Result<()> {
    let file: File = std::fs::File::open(filename)?;
    let mut pdb: PDB<File> = pdb::PDB::open(file)?;
    
    // ...
    
    let dbi: DebugInformation = pdb.debug_information()?;
    let mut modules: ModuleIter = dbi.modules()?;
    
    // merge the module functions
    let mut mod_symbols: Vec<pdb::Symbol> = Vec::new();
    
    while let Some(module) = modules.next()? {
        println!("Module: {}", module.object_file_name());
        let info: ModuleInfo = match pdb.module_info(&module)? {
            Some(info) => info,
            None => {
                println!("no module info");
                continue;
            }
        };

        while let Some(symbol) = info.symbols()?.next()? {  // `info` does not live long enough
            mod_symbols.push(symbol);                       // borrow later used here
        }
    }
    
    // ...
}

Error:

error[E0597]: `info` does not live long enough
   --> examples\pdb_symbols2json.rs:251:34
    |
251 |         while let Some(symbol) = info.symbols()?.next()? {
    |                                  ^^^^^^^^^^^^^^ borrowed value does not live long enough
252 |             mod_symbols.push(symbol);
    |             ------------------------ borrow later used here
...
282 |     }
    |     - `info` dropped here while still borrowed


mimak
  • 211
  • 2
  • 9

1 Answers1

2

The problem is that it looks like most types in pdb have a hidden lifetime argument.

IMO, you should write pdb::Symbol<'_> and pdb::Module<'_> to make that apparent. I expect that a future Rust edition will make not writing the lifetime argument a hard error.

About your particular situation, I don't know the details of that crate, but I guess that your mod_symbols is storing values ultimately borrowed from info, so one should outlive the other. But you are creating and destroying info with each iteration of the loop, and that would invalidate the values stored in the mod_symbols.

You can try storing the info values in an outer scope. Something like this (untested):

    // infos lives longer than mod_symbols!
    let mut infos: Vec<ModuleInfo<'_>> = Vec::new();
    let mut mod_symbols: Vec<pdb::Symbol<'_>> = Vec::new();

    while let Some(module) = modules.next()? {
        println!("Module: {}", module.object_file_name());
        let info = match pdb.module_info(&module)? {
            Some(info) => info,
            None => {
                println!("no module info");
                continue;
            }
        };
        infos.push(info);
    }
    for info in &infos {
        while let Some(symbol) = info.symbols()?.next()? {
            mod_symbols.push(symbol);
        }
    }
rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • sadly this results in 'cannot borrow `infos` as mutable because it is also borrowed as immutable' at `infos.push(info)` – mimak May 22 '23 at 22:44
  • fyi, moving the inner `while` loop outside fixed the errors (it resulted in a 25GB allocation runtime error when testing but that's out of scope for this question) – mimak May 22 '23 at 23:09
  • @mimak: Indeed, I should have seen that. Let me fix the answer. About the 25GB I guess that the intended use is to just store the data you need from the `Symbol` and not the symbol itself? – rodrigo May 23 '23 at 07:53
  • i wanted to devide the symbol data into equal chunks for parallel processing, but for large pdb files that results in up to a million items, i guess i accumulated too much in a single vector. anyway thanks for help – mimak May 23 '23 at 11:10
  • 1
    @mimak: I think I've done something similar with `rayon`: it required the `Item` type to be `Send` but not `'static`, so it might work for you. – rodrigo May 23 '23 at 12:07