diff --git a/Cargo.lock b/Cargo.lock
index e2e5254491bb4f9c4aab15429ae02d5ff22f4bc4..d6a04eb46f5405f57cdca433dedc4b6c95cce6e4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -761,14 +761,6 @@ dependencies = [
  "sha2",
 ]
 
-[[package]]
-name = "pip-mpu-relocate"
-version = "0.1.0"
-dependencies = [
- "elf",
- "thiserror-core",
-]
-
 [[package]]
 name = "pip-tool"
 version = "0.1.0"
@@ -777,7 +769,7 @@ dependencies = [
  "cargo-scaffold",
  "clap",
  "elf",
- "pip-mpu-relocate",
+ "thiserror",
 ]
 
 [[package]]
@@ -1061,38 +1053,18 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "1.0.58"
+version = "1.0.61"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
+checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
 dependencies = [
  "thiserror-impl",
 ]
 
-[[package]]
-name = "thiserror-core"
-version = "1.0.50"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c001ee18b7e5e3f62cbf58c7fe220119e68d902bb7443179c0c8aef30090e999"
-dependencies = [
- "thiserror-core-impl",
-]
-
-[[package]]
-name = "thiserror-core-impl"
-version = "1.0.50"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4c60d69f36615a077cc7663b9cb8e42275722d23e58a7fa3d2c7f2915d09d04"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
 [[package]]
 name = "thiserror-impl"
-version = "1.0.58"
+version = "1.0.61"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
+checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/Cargo.toml b/Cargo.toml
index 7c0c5153f7d2091a997ebe52619212ef6eb977aa..375aed0a8e26704cb9a6270601a438fc058c431c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,5 +20,5 @@ anyhow = "1.0.81"
 cargo-scaffold = "0.12.0"
 clap = "4.5.2"
 elf = "0.7.4"
-pip-mpu-relocate = { path = "../pip-mpu-relocate" }
+thiserror = "1.0.61"
 
diff --git a/rust-toolchain b/rust-toolchain
deleted file mode 100644
index bf867e0ae5b6c08df1118a2ece970677bc479f1b..0000000000000000000000000000000000000000
--- a/rust-toolchain
+++ /dev/null
@@ -1 +0,0 @@
-nightly
diff --git a/src/lib.rs b/src/lib.rs
index e5339898300f9a52592315e3e1ae63eae382686a..212f2c992bbda4dbda485af0f96b0f2bf7891f49 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,7 +1,9 @@
 #![doc = include_str!("../README.md")]
 
-mod generate;
+mod new;
 mod link;
+mod symbols;
+mod rel_iter;
 
 use anyhow::Result;
 use clap::{Parser, Subcommand};
@@ -16,7 +18,7 @@ pub struct Opts {
 #[derive(Subcommand, Debug)]
 pub enum PipToolSubcommand {
     /// Generate a new pip partition project 
-    Generate(generate::Opts),
+    New(new::Opts),
     /// Link elf files together and generate a binary blob ready to be
     /// flashed/loaded
     Link(link::Opts),
@@ -24,7 +26,7 @@ pub enum PipToolSubcommand {
 
 pub fn execute(opts: Opts) -> Result<()> {
     match opts.command {
-        PipToolSubcommand::Generate(opts) => generate::generate(opts)?,
+        PipToolSubcommand::New(opts) => new::generate(opts)?,
         PipToolSubcommand::Link(opts) => link::link(opts)?,
     }
     Ok(())
diff --git a/src/link.rs b/src/link.rs
index d137250c1b77eeb06da6e60ab8585d43ab6011b7..85a449826b99b25083bd89c8fa987f1b12d82162 100644
--- a/src/link.rs
+++ b/src/link.rs
@@ -14,7 +14,8 @@
 
 use anyhow::{anyhow, Result};
 use clap::Parser;
-use elf::endian::AnyEndian;
+use elf::endian::{AnyEndian, EndianParse};
+use elf::ElfBytes;
 use std::path::PathBuf;
 
 // use pip_mpu_relocate::reloc::RelIter;
@@ -22,10 +23,21 @@ use std::path::PathBuf;
 // use pip_mpu_relocate::symbols::SymbolsQuery;
 use std::io::Write;
 
+use crate::symbols::SymbolsIter;
+
 const PADDING_SIZE: usize = 32;
 const PADDING_BUFFER: [u8; PADDING_SIZE] = [0xff; PADDING_SIZE];
 const PIP_EXPORT_SECTIONS: &'static [&str] = &[".vector_table", ".text", ".ARM.exidx", ".data"];
 const CRT0_EXPORT_SECTIONS: &'static [&str] = &[".text", ".rodata", ".ARM.exidx"];
+const USER_CODE_INFO_SYMBOLS: &'static [&str] = &[
+    "start",
+    "__romSize",
+    "__romRamSize",
+    "__ramSize",
+    "__gotStart",
+    "__gotSize",
+    "__romRamEnd",
+];
 
 /// Options for the link subcommand
 #[derive(Parser, Debug)]
@@ -73,6 +85,36 @@ fn align(value: usize, align: usize) -> usize {
     (value + (align - 1) & !(align - 1)) - value
 }
 
+/// Exports the user code information to the output file
+///
+/// User code information are stored as symbols in the elf file.
+/// Symbols value are always considered as 32 bits values due to the
+/// purpose of this program at the moment (processing elf files for
+/// ARM Thumb architecture). For the same reason, the values are ouput
+/// in little endian format.
+///
+/// The order of the output values matches the symbol names order in the
+/// `symbols_name` parameter.
+///
+/// An error is returned if *any* requested symbol is missing
+pub fn export_user_info<'data, T: Write, E: EndianParse>(
+    elf: &'data ElfBytes<'data, E>,
+    symbols_name: &[&'static str],
+    output_file: &mut T,
+) -> Result<()> {
+    let mut symbols = SymbolsIter::new(elf, symbols_name.iter().map(|s| *s))?;
+
+    symbols.try_for_each(|symbol| -> Result<()> {
+        let symbol = symbol?;
+        let val = symbol.st_value as u32;
+        let bytes = val.to_le_bytes();
+        eprintln!("Writing {:x} ({} bytes)", val, symbol.st_size);
+        output_file.write(&bytes)?;
+        Ok(())
+    })?;
+    Ok(())
+}
+
 pub fn link(opts: Opts) -> Result<()> {
     // First, open and parse both crt0 and user_code elf files
 
@@ -110,6 +152,10 @@ pub fn link(opts: Opts) -> Result<()> {
     // Then, dump crt0
     // no need for output size here, padding is not necessary for crt0
     dump_sections(&crt0_elf, CRT0_EXPORT_SECTIONS, &mut output)?;
+
+    // Now, export the needed user code information
+    export_user_info(&_user_code_elf, USER_CODE_INFO_SYMBOLS, &mut output)?;
+
     
     Ok(())
 }
diff --git a/src/generate.rs b/src/new.rs
similarity index 100%
rename from src/generate.rs
rename to src/new.rs
diff --git a/src/rel_iter.rs b/src/rel_iter.rs
new file mode 100644
index 0000000000000000000000000000000000000000..7a339a6ae1e9cc9f6ac1b6ff8a77a49179df9e2a
--- /dev/null
+++ b/src/rel_iter.rs
@@ -0,0 +1,48 @@
+//! This modules provides ways to process relocation sections
+//! to produce a relocation binary suitable for pip-mpu crt0
+
+use elf::endian::EndianParse;
+use elf::relocation::Rel;
+use elf::relocation::RelIterator;
+use elf::ElfBytes;
+use thiserror::Error;
+
+pub struct RelIter<'data, E: EndianParse> {
+    relocs: RelIterator<'data, E>,
+}
+
+#[derive(Error, Debug)]
+pub enum RelError {
+    #[error("elf parse error")]
+    ElfParseError(elf::parse::ParseError),
+
+    #[error("section not found")]
+    SectionNotFound,
+}
+
+impl From<elf::parse::ParseError> for RelError {
+    fn from(value: elf::parse::ParseError) -> Self {
+        RelError::ElfParseError(value)
+    }
+}
+
+pub type Result<T> = core::result::Result<T, RelError>;
+
+impl<'data, E: EndianParse> RelIter<'data, E> {
+    pub fn new(elf: &'data ElfBytes<E>, relname: &str) -> Result<Self> {
+        let sec = match elf.section_header_by_name(relname)? {
+            Some(s) => s,
+            None => return Err(RelError::SectionNotFound),
+        };
+        let relocs = elf.section_data_as_rels(&sec)?;
+        Ok(Self { relocs })
+    }
+}
+
+impl<'data, E: EndianParse> Iterator for RelIter<'data, E> {
+    type Item = Rel;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.relocs.next()
+    }
+}
diff --git a/src/symbols.rs b/src/symbols.rs
new file mode 100644
index 0000000000000000000000000000000000000000..66f8ba37a5978975d55d19694da312d38a534bea
--- /dev/null
+++ b/src/symbols.rs
@@ -0,0 +1,131 @@
+//! This module provides functions to deal with symbols table in elf files
+
+use elf::endian::EndianParse;
+use elf::ElfBytes;
+
+use elf::string_table::StringTable;
+use elf::symbol::Symbol;
+use elf::symbol::SymbolTable;
+
+use thiserror::Error;
+
+/// Error type for the symbols module
+#[derive(Error, Debug)]
+pub enum SymbolsError<'data> {
+    #[error("elf parse error")]
+    ElfParseError(elf::parse::ParseError),
+
+    #[error("no symbol table found")]
+    SymbolTableNotFound,
+
+    #[error("no string table found")]
+    StringTableNotFound,
+
+    #[error("symbol name {0} not found")]
+    SymbolNameNotFound(&'data str),
+}
+
+impl<'data> From<elf::parse::ParseError> for SymbolsError<'data> {
+    fn from(value: elf::parse::ParseError) -> Self {
+        SymbolsError::ElfParseError(value)
+    }
+}
+
+/// Symbols related specific result
+pub type Result<'data, T> = core::result::Result<T, SymbolsError<'data>>;
+
+/// A helper struct that provide queries on symbol tables.
+pub struct SymbolsQuery<'data, E: EndianParse> {
+    symtab: SymbolTable<'data, E>,
+    strtab: StringTable<'data>,
+}
+
+impl<'data, E: EndianParse> SymbolsQuery<'data, E> {
+    /// Returns a new symbol table query object from a symbol table
+    /// and string table.
+    pub fn new(symtab: SymbolTable<'data, E>, strtab: StringTable<'data>) -> Self {
+        Self { symtab, strtab }
+    }
+
+    pub fn from_elf(elf: &'data ElfBytes<'data, E>) -> Result<'_, Self> {
+        let common_sections = elf.find_common_data()?;
+
+        let symbols_table = match common_sections.symtab {
+            Some(s) => s,
+            None => return Err(SymbolsError::SymbolTableNotFound),
+        };
+
+        let string_table = match common_sections.symtab_strs {
+            Some(s) => s,
+            None => return Err(SymbolsError::StringTableNotFound),
+        };
+
+        Ok(SymbolsQuery::new(symbols_table, string_table))
+    }
+
+    /// Finds a symbol by name
+    pub fn find<'a>(&'data self, symbol_name: &'a str) -> Result<'a, Symbol> {
+        for symbol in self.symtab.iter() {
+            let symbol_name_offset = symbol.st_name as usize;
+            let sname = self.strtab.get(symbol_name_offset)?;
+            if sname == symbol_name {
+                return Ok(symbol);
+            }
+        }
+        Err(SymbolsError::SymbolNameNotFound(symbol_name))
+    }
+}
+
+/// A symbols iterator that returns symbols from a symbol name list
+pub struct SymbolsIter<'data, 's, E, I>
+where
+    E: EndianParse,
+    I: Iterator<Item = &'s str>,
+{
+    symbols_names: I,
+    symbols_query: SymbolsQuery<'data, E>,
+}
+
+impl<'data, 's, E, I> SymbolsIter<'data, 's, E, I>
+where
+    E: EndianParse,
+    I: Iterator<Item = &'s str>,
+{
+    /// Creates a new symbol iterator from a symbol name iterator.  The
+    /// symbols will be emitted in the same order as the symbol name
+    /// iterators emits the symbol names
+    pub fn new(elf: &'data ElfBytes<'data, E>, symbols_names: I) -> Result<'s, Self> {
+        let common_sections = elf.find_common_data()?;
+
+        let symbols_table = match common_sections.symtab {
+            Some(s) => s,
+            None => return Err(SymbolsError::SymbolTableNotFound),
+        };
+
+        let string_table = match common_sections.symtab_strs {
+            Some(s) => s,
+            None => return Err(SymbolsError::StringTableNotFound),
+        };
+
+        let symbols_query = SymbolsQuery::new(symbols_table, string_table);
+        Ok(Self {
+            symbols_names,
+            symbols_query,
+        })
+    }
+}
+
+impl<'data, 's, E, I> Iterator for SymbolsIter<'data, 's, E, I>
+where
+    E: EndianParse,
+    I: Iterator<Item = &'s str>,
+{
+    type Item = Result<'s, Symbol>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match self.symbols_names.next() {
+            None => None,
+            Some(name) => Some(self.symbols_query.find(name)),
+        }
+    }
+}