You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by pa...@apache.org on 2016/08/24 23:03:43 UTC

[1/2] incubator-mynewt-newt git commit: MYNEWT-317

Repository: incubator-mynewt-newt
Updated Branches:
  refs/heads/develop 428b16e06 -> 8c06bb681


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newtmgr/protocol/imagesplit.go
----------------------------------------------------------------------
diff --git a/newtmgr/protocol/imagesplit.go b/newtmgr/protocol/imagesplit.go
new file mode 100644
index 0000000..a3c28fc
--- /dev/null
+++ b/newtmgr/protocol/imagesplit.go
@@ -0,0 +1,181 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package protocol
+
+import (
+	"encoding/json"
+	"fmt"
+	"strings"
+
+	"mynewt.apache.org/newt/util"
+)
+
+const (
+	SPLIT_NMGR_OP_SPLIT = 0
+)
+
+type SplitMode int
+
+const (
+	NONE SplitMode = iota
+	TEST
+	RUN
+)
+
+var splitMode = [...]string{NONE: "none", TEST: "test", RUN: "run"}
+
+/* is the enum valid */
+func (sm SplitMode) Valid() bool {
+	for val, _ := range splitMode {
+		if int(sm) == val {
+			return true
+		}
+	}
+	return false
+}
+
+/* returns the enum as a string */
+func (sm SplitMode) String() string {
+	if sm > RUN || sm < 0 {
+		return "Invalid!"
+	}
+	return splitMode[sm]
+}
+
+type SplitStatus int
+
+const (
+	NOT_APPLICABLE SplitStatus = iota
+	NOT_MATCHING
+	MATCHING
+)
+
+/* parses the enum from a string */
+func ParseSplitMode(str string) (SplitMode, error) {
+	for val, key := range splitMode {
+		if strings.EqualFold(key, str) {
+			return SplitMode(val), nil
+		}
+	}
+	return NONE, util.NewNewtError("Invalid value for Split Mode %v" + str)
+}
+
+var splitStatus = [...]string{NOT_APPLICABLE: "N/A", NOT_MATCHING: "Non-matching", MATCHING: "matching"}
+
+/* is the enum valid */
+func (sm SplitStatus) Valid() bool {
+	for val, _ := range splitStatus {
+		if int(sm) == val {
+			return true
+		}
+	}
+	return false
+}
+
+/* returns the enum as a string */
+func (sm SplitStatus) String() string {
+	if sm > MATCHING || sm < 0 {
+		return "Unknown!"
+	}
+	return splitStatus[sm]
+}
+
+/* parses the enum from a string */
+func ParseSplitStatus(str string) (SplitStatus, error) {
+	for val, key := range splitStatus {
+		if strings.EqualFold(key, str) {
+			return SplitStatus(val), nil
+		}
+	}
+	return NOT_APPLICABLE, util.NewNewtError("Invalid value for Split Status %v" + str)
+}
+
+type Split struct {
+	Split      SplitMode   `json:"splitMode"`
+	Status     SplitStatus `json:"splitStatus"`
+	ReturnCode int         `json:"rc"`
+}
+
+func NewSplit() (*Split, error) {
+	s := &Split{}
+	return s, nil
+}
+
+func (s *Split) EncoderReadRequest() (*NmgrReq, error) {
+	msg := "{}"
+
+	data := []byte(msg)
+
+	nmr, err := NewNmgrReq()
+	if err != nil {
+		return nil, err
+	}
+
+	nmr.Op = NMGR_OP_READ
+	nmr.Flags = 0
+	nmr.Group = NMGR_GROUP_ID_SPLIT
+	nmr.Id = SPLIT_NMGR_OP_SPLIT
+	nmr.Len = uint16(len(data))
+	nmr.Data = data
+
+	return nmr, nil
+}
+
+func (s *Split) EncoderWriteRequest() (*NmgrReq, error) {
+
+	data, err := json.Marshal(s)
+
+	if err != nil {
+		return nil, err
+	}
+
+	nmr, err := NewNmgrReq()
+	if err != nil {
+		return nil, err
+	}
+
+	nmr.Op = NMGR_OP_WRITE
+	nmr.Flags = 0
+	nmr.Group = NMGR_GROUP_ID_SPLIT
+	nmr.Id = SPLIT_NMGR_OP_SPLIT
+	nmr.Len = uint16(len(data))
+	nmr.Data = data
+
+	return nmr, nil
+}
+
+func DecodeSplitReadResponse(data []byte) (*Split, error) {
+	i := &Split{}
+
+	if len(data) == 0 {
+		return i, nil
+	}
+
+	err := json.Unmarshal(data, &i)
+	if err != nil {
+		return nil, util.NewNewtError(fmt.Sprintf("Invalid incoming json: %s",
+			err.Error()))
+	}
+	if i.ReturnCode != 0 {
+		return nil, util.NewNewtError(fmt.Sprintf("Target error: %d",
+			i.ReturnCode))
+	}
+	return i, nil
+}


[2/2] incubator-mynewt-newt git commit: MYNEWT-317

Posted by pa...@apache.org.
MYNEWT-317

newt changes to support split images.  This basically builds the split image as follows.

1) builds the two apps (loader and app). A new tag "loader" was defined for the target
2) looks at common symbols between then
3) Collects them by package
4) rebuilds the loader with all of the common symbols in the common packages
5) builds a special linkable ROM from the loader image using the common symbols
5) rebuilds the app linking against the special loader elf and the non-common libraries
6) Builds two elfs and two image files


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/commit/8c06bb68
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/tree/8c06bb68
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/diff/8c06bb68

Branch: refs/heads/develop
Commit: 8c06bb681dee7ee78bc8d9b33d761ac2e9a23455
Parents: 428b16e
Author: Paul Dietrich <pa...@yahoo.com>
Authored: Wed Jul 6 11:18:18 2016 -0700
Committer: Paul Dietrich <pa...@yahoo.com>
Committed: Wed Aug 24 16:02:58 2016 -0700

----------------------------------------------------------------------
 newt/builder/build.go          | 305 +++++++++++++++---------
 newt/builder/buildpackage.go   |  10 +-
 newt/builder/buildutil.go      |  30 +--
 newt/builder/library.go        | 218 ++++++++++++++++++
 newt/builder/load.go           |  78 +++++--
 newt/builder/size.go           |  27 ++-
 newt/builder/targetbuild.go    | 445 ++++++++++++++++++++++++++++++++++++
 newt/cli/build_cmds.go         |  18 +-
 newt/cli/image_cmds.go         |  99 +++++---
 newt/cli/run_cmds.go           |  45 ++--
 newt/image/image.go            |  95 +++++---
 newt/newtutil/newtutil.go      |  24 ++
 newt/pkg/bsp_package.go        |  13 +-
 newt/project/project.go        |  13 ++
 newt/symbol/symbol.go          | 322 ++++++++++++++++++++++++++
 newt/target/target.go          |   7 +
 newt/toolchain/compiler.go     | 217 +++++++++++++++++-
 newt/toolchain/deps.go         |  89 +++++++-
 newtmgr/cli/commands.go        |   1 +
 newtmgr/cli/image.go           |  19 +-
 newtmgr/cli/split.go           | 102 +++++++++
 newtmgr/protocol/defs.go       |   1 +
 newtmgr/protocol/imagelist2.go |  30 +--
 newtmgr/protocol/imagesplit.go | 181 +++++++++++++++
 24 files changed, 2117 insertions(+), 272 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newt/builder/build.go
----------------------------------------------------------------------
diff --git a/newt/builder/build.go b/newt/builder/build.go
index 383bb95..b8cbd39 100644
--- a/newt/builder/build.go
+++ b/newt/builder/build.go
@@ -29,43 +29,44 @@ import (
 	log "github.com/Sirupsen/logrus"
 
 	"mynewt.apache.org/newt/newt/pkg"
+	"mynewt.apache.org/newt/newt/symbol"
 	"mynewt.apache.org/newt/newt/target"
 	"mynewt.apache.org/newt/newt/toolchain"
 	"mynewt.apache.org/newt/util"
 )
 
 type Builder struct {
-	Packages map[*pkg.LocalPackage]*BuildPackage
-	features map[string]bool
-	apis     map[string]*BuildPackage
-
-	appPkg       *BuildPackage
-	Bsp          *pkg.BspPackage
-	compilerPkg  *pkg.LocalPackage
-	compilerInfo *toolchain.CompilerInfo
-
+	Packages         map[*pkg.LocalPackage]*BuildPackage
+	features         map[string]bool
+	apis             map[string]*BuildPackage
+	appPkg           *BuildPackage
+	BspPkg           *pkg.LocalPackage
+	compilerInfo     *toolchain.CompilerInfo
 	featureWhiteList []map[string]interface{}
 	featureBlackList []map[string]interface{}
-
-	target *target.Target
+	target           *TargetBuilder
+	linkerScript     string
+	buildName        string
+	LinkElf          string
 }
 
-func NewBuilder(target *target.Target) (*Builder, error) {
+func NewBuilder(t *TargetBuilder, buildName string) (*Builder, error) {
 	b := &Builder{}
 
-	if err := b.Init(target); err != nil {
-		return nil, err
-	}
+	b.buildName = buildName
+	/* TODO */
+	b.Init(t)
 
 	return b, nil
 }
 
-func (b *Builder) Init(target *target.Target) error {
-	b.target = target
+func (b *Builder) Init(t *TargetBuilder) error {
 
+	b.target = t
 	b.Packages = map[*pkg.LocalPackage]*BuildPackage{}
 	b.features = map[string]bool{}
 	b.apis = map[string]*BuildPackage{}
+	b.LinkElf = ""
 
 	return nil
 }
@@ -133,9 +134,10 @@ func (b *Builder) loadDeps() error {
 			}
 
 			if newFeatures {
-				// A new supported feature was discovered.  It is impossible to
-				// determine what new dependency and API requirements are
-				// generated as a result.  All packages need to be reprocessed.
+				// A new supported feature was discovered.  It is impossible
+				// to determine what new dependency and API requirements are
+				// generated as a result.  All packages need to be
+				// reprocessed.
 				for _, bpkg := range b.Packages {
 					bpkg.depsResolved = false
 					bpkg.apisSatisfied = false
@@ -215,11 +217,11 @@ func buildDir(srcDir string, c *toolchain.Compiler, arch string,
 func (b *Builder) newCompiler(bpkg *BuildPackage,
 	dstDir string) (*toolchain.Compiler, error) {
 
-	c, err := toolchain.NewCompiler(b.compilerPkg.BasePath(), dstDir,
-		b.target.BuildProfile)
+	c, err := b.target.NewCompiler(dstDir)
 	if err != nil {
 		return nil, err
 	}
+
 	c.AddInfo(b.compilerInfo)
 
 	if bpkg != nil {
@@ -279,12 +281,12 @@ func (b *Builder) buildPackage(bpkg *BuildPackage) error {
 	//     * src/arch/<target-arch>
 	//     * src/test/arch/<target-arch>
 	for _, dir := range srcDirs {
-		if err = buildDir(dir, c, b.Bsp.Arch, []string{"test"}); err != nil {
+		if err = buildDir(dir, c, b.target.Bsp.Arch, []string{"test"}); err != nil {
 			return err
 		}
 		if b.features["TEST"] {
 			testSrcDir := dir + "/test"
-			if err = buildDir(testSrcDir, c, b.Bsp.Arch, nil); err != nil {
+			if err = buildDir(testSrcDir, c, b.target.Bsp.Arch, nil); err != nil {
 				return err
 			}
 		}
@@ -302,24 +304,52 @@ func (b *Builder) buildPackage(bpkg *BuildPackage) error {
 	return nil
 }
 
-func (b *Builder) link(elfName string) error {
+func (b *Builder) RemovePackages(cmn map[string]bool) error {
+
+	for pkgName, _ := range cmn {
+		for lp, bpkg := range b.Packages {
+			if bpkg.Name() == pkgName {
+				delete(b.Packages, lp)
+			}
+		}
+	}
+	return nil
+}
+
+func (b *Builder) ExtractSymbolInfo() (error, *symbol.SymbolMap) {
+	syms := symbol.NewSymbolMap()
+	for _, bpkg := range b.Packages {
+		err, sm := b.ParseObjectLibrary(bpkg)
+		if err == nil {
+			syms, err = (*syms).Merge(sm)
+			if err != nil {
+				return err, nil
+			}
+		}
+	}
+	return nil, syms
+}
+
+func (b *Builder) link(elfName string, linkerScript string,
+	keepSymbols []string) error {
 	c, err := b.newCompiler(b.appPkg, b.PkgBinDir(elfName))
 	if err != nil {
 		return err
 	}
 
+	/* always used the trimmed archive files */
 	pkgNames := []string{}
+
 	for _, bpkg := range b.Packages {
-		archivePath := b.ArchivePath(bpkg.Name())
-		if util.NodeExist(archivePath) {
-			pkgNames = append(pkgNames, archivePath)
+		if util.NodeExist(b.ArchivePath(bpkg.Name())) {
+			pkgNames = append(pkgNames, b.ArchivePath(bpkg.Name()))
 		}
 	}
 
-	if b.Bsp.LinkerScript != "" {
-		c.LinkerScript = b.Bsp.BasePath() + b.Bsp.LinkerScript
+	if linkerScript != "" {
+		c.LinkerScript = b.target.Bsp.BasePath() + linkerScript
 	}
-	err = c.CompileElf(elfName, pkgNames)
+	err = c.CompileElf(elfName, pkgNames, keepSymbols, b.LinkElf)
 	if err != nil {
 		return err
 	}
@@ -330,45 +360,16 @@ func (b *Builder) link(elfName string) error {
 // Populates the builder with all the packages that need to be built and
 // configures each package's build settings.  After this function executes,
 // packages are ready to be built.
-func (b *Builder) PrepBuild() error {
-	if b.Bsp != nil {
-		// Already prepped
-		return nil
-	}
+func (b *Builder) PrepBuild(appPkg *pkg.LocalPackage,
+	bspPkg *pkg.LocalPackage, targetPkg *pkg.LocalPackage) error {
 
 	b.featureBlackList = []map[string]interface{}{}
 	b.featureWhiteList = []map[string]interface{}{}
 
-	// Collect the seed packages.
-	bspPkg := b.target.Bsp()
-	if bspPkg == nil {
-		if b.target.BspName == "" {
-			return util.NewNewtError("BSP package not specified by target")
-		} else {
-			return util.NewNewtError("BSP package not found: " +
-				b.target.BspName)
-		}
-	}
-
-	b.featureBlackList = append(b.featureBlackList, bspPkg.FeatureBlackList())
-	b.featureWhiteList = append(b.featureWhiteList, bspPkg.FeatureWhiteList())
-
-	b.Bsp = pkg.NewBspPackage(bspPkg)
-	compilerPkg := b.resolveCompiler()
-	if compilerPkg == nil {
-		if b.Bsp.CompilerName == "" {
-			return util.NewNewtError("Compiler package not specified by BSP")
-		} else {
-			return util.NewNewtError("Compiler package not found: " +
-				b.Bsp.CompilerName)
-		}
-	}
-
-	// An app package is not required (e.g., unit tests).
-	appPkg := b.target.App()
-
 	// Seed the builder with the app (if present), bsp, and target packages.
 
+	b.BspPkg = bspPkg
+
 	var appBpkg *BuildPackage
 	if appPkg != nil {
 		appBpkg = b.Packages[appPkg]
@@ -382,11 +383,15 @@ func (b *Builder) PrepBuild() error {
 	}
 
 	bspBpkg := b.Packages[bspPkg]
+
 	if bspBpkg == nil {
 		bspBpkg = b.AddPackage(bspPkg)
 	}
 
-	targetBpkg := b.AddPackage(b.target.Package())
+	b.featureBlackList = append(b.featureBlackList, bspPkg.FeatureBlackList())
+	b.featureWhiteList = append(b.featureWhiteList, bspPkg.FeatureWhiteList())
+
+	targetBpkg := b.AddPackage(targetPkg)
 
 	b.featureBlackList = append(b.featureBlackList, targetBpkg.FeatureBlackList())
 	b.featureWhiteList = append(b.featureWhiteList, targetBpkg.FeatureWhiteList())
@@ -423,7 +428,7 @@ func (b *Builder) PrepBuild() error {
 	baseCi := toolchain.NewCompilerInfo()
 
 	// Target flags.
-	log.Debugf("Generating build flags for target %s", b.target.FullName())
+	log.Debugf("Generating build flags for target %s", b.target.target.FullName())
 	targetCi, err := targetBpkg.CompilerInfo(b)
 	if err != nil {
 		return err
@@ -448,11 +453,11 @@ func (b *Builder) PrepBuild() error {
 		return err
 	}
 
-	// Define a cpp symbol indicating the BSP architecture, name of the BSP and
-	// app.
-	bspCi.Cflags = append(bspCi.Cflags, "-DARCH_"+b.Bsp.Arch)
+	// Define a cpp symbol indicating the BSP architecture, name of the
+	// BSP and app.
+	bspCi.Cflags = append(bspCi.Cflags, "-DARCH_"+b.target.Bsp.Arch)
 	bspCi.Cflags = append(bspCi.Cflags,
-		"-DBSP_NAME=\""+filepath.Base(b.Bsp.Name())+"\"")
+		"-DBSP_NAME=\""+filepath.Base(b.target.Bsp.Name())+"\"")
 	if appPkg != nil {
 		bspCi.Cflags = append(bspCi.Cflags,
 			"-DAPP_NAME=\""+filepath.Base(appPkg.Name())+"\"")
@@ -461,14 +466,6 @@ func (b *Builder) PrepBuild() error {
 
 	// Note: Compiler flags get added at the end, after the flags for library
 	// package being built are calculated.
-
-	// Read the BSP configuration.  These settings are necessary for the link
-	// step.
-	if err := b.Bsp.Reload(b.Features(b.Bsp)); err != nil {
-		return err
-	}
-
-	b.compilerPkg = compilerPkg
 	b.compilerInfo = baseCi
 
 	return nil
@@ -513,16 +510,11 @@ func (b *Builder) CheckValidFeature(pkg pkg.Package,
 	}
 }
 
-func (b *Builder) Build() error {
-	if err := b.target.Validate(true); err != nil {
-		return err
-	}
+func (b *Builder) AddCompilerInfo(info *toolchain.CompilerInfo) {
+	b.compilerInfo.AddCompilerInfo(info)
+}
 
-	// Populate the package and feature sets and calculate the base compiler
-	// flags.
-	if err := b.PrepBuild(); err != nil {
-		return err
-	}
+func (b *Builder) Build() error {
 
 	// Build the packages alphabetically to ensure a consistent order.
 	bpkgs := b.sortedBuildPackages()
@@ -532,35 +524,42 @@ func (b *Builder) Build() error {
 		}
 	}
 
-	if err := b.link(b.AppElfPath()); err != nil {
+	return nil
+}
+
+func (b *Builder) Link(linkerScript string) error {
+	if err := b.link(b.AppElfPath(), linkerScript, nil); err != nil {
 		return err
 	}
+	return nil
+}
+
+func (b *Builder) KeepLink(linkerScript string, keepMap *symbol.SymbolMap) error {
+	keepSymbols := make([]string, 0)
 
+	if keepMap != nil {
+		for _, info := range *keepMap {
+			keepSymbols = append(keepSymbols, info.Name)
+		}
+	}
+	if err := b.link(b.AppElfPath(), linkerScript, keepSymbols); err != nil {
+		return err
+	}
 	return nil
 }
 
-func (b *Builder) Test(p *pkg.LocalPackage) error {
-	if err := b.target.Validate(false); err != nil {
+func (b *Builder) TestLink(linkerScript string) error {
+	if err := b.link(b.AppTempElfPath(), linkerScript, nil); err != nil {
 		return err
 	}
+	return nil
+}
+
+func (b *Builder) Test(p *pkg.LocalPackage) error {
 
 	// Seed the builder with the package under test.
 	testBpkg := b.AddPackage(p)
 
-	// A few features are automatically supported when the test command is
-	// used:
-	//     * TEST:      ensures that the test code gets compiled.
-	//     * SELFTEST:  indicates that there is no app.
-	b.AddFeature("TEST")
-	b.AddFeature("SELFTEST")
-
-	// Populate the package and feature sets and calculate the base compiler
-	// flags.
-	err := b.PrepBuild()
-	if err != nil {
-		return err
-	}
-
 	// Define the PKG_TEST symbol while the package under test is being
 	// compiled.  This symbol enables the appropriate main function that
 	// usually comes from an app.
@@ -580,7 +579,7 @@ func (b *Builder) Test(p *pkg.LocalPackage) error {
 	}
 
 	testFilename := b.TestExePath(p.Name())
-	err = b.link(testFilename)
+	err = b.link(testFilename, "", nil)
 	if err != nil {
 		return err
 	}
@@ -604,7 +603,101 @@ func (b *Builder) Test(p *pkg.LocalPackage) error {
 
 func (b *Builder) Clean() error {
 	path := b.BinDir()
-	util.StatusMessage(util.VERBOSITY_VERBOSE, "Cleaning directory %s\n", path)
+	util.StatusMessage(util.VERBOSITY_VERBOSE, "Cleaning directory %s\n",
+		path)
 	err := os.RemoveAll(path)
 	return err
 }
+
+func (b *Builder) FetchSymbolMap() (error, *symbol.SymbolMap) {
+	loader_sm := symbol.NewSymbolMap()
+
+	for _, value := range b.Packages {
+		err, sm := b.ParseObjectLibrary(value)
+		if err == nil {
+			util.StatusMessage(util.VERBOSITY_VERBOSE,
+				"Size of %s Loader Map %d\n", value.Name(), len(*sm))
+			loader_sm, err = loader_sm.Merge(sm)
+			if err != nil {
+				return err, nil
+			}
+		}
+	}
+
+	return nil, loader_sm
+}
+
+func (b *Builder) GetTarget() *target.Target {
+	return b.target.GetTarget()
+}
+
+func (b *Builder) buildRomElf(common *symbol.SymbolMap) error {
+
+	/* check dependencies on the ROM ELF.  This is really dependent on
+	 * all of the .a files, but since we already depend on the loader
+	 * .as to build the initial elf, we only need to check the app .a */
+	c, err := b.target.NewCompiler(b.AppElfPath())
+	d := toolchain.NewDepTracker(c)
+	if err != nil {
+		return err
+	}
+
+	archNames := []string{}
+
+	/* build the set of archive file names */
+	for _, bpkg := range b.Packages {
+		archivePath := b.ArchivePath(bpkg.Name())
+		if util.NodeExist(archivePath) {
+			archNames = append(archNames, archivePath)
+		}
+	}
+
+	bld, err := d.RomElfBuldRequired(b.AppLinkerElfPath(),
+		b.AppElfPath(), archNames)
+	if err != nil {
+		return err
+	}
+
+	if !bld {
+		return nil
+	}
+
+	util.StatusMessage(util.VERBOSITY_DEFAULT,
+		"Generating ROM elf \n")
+
+	/* the linker needs these symbols kept for the split app
+	 * to initialize the loader data and bss */
+	common.Add(*symbol.NewElfSymbol("__HeapBase"))
+	common.Add(*symbol.NewElfSymbol("__bss_start__"))
+	common.Add(*symbol.NewElfSymbol("__bss_end__"))
+	common.Add(*symbol.NewElfSymbol("__etext"))
+	common.Add(*symbol.NewElfSymbol("__data_start__"))
+	common.Add(*symbol.NewElfSymbol("__data_end__"))
+
+	/* the split app may need this to access interrupts */
+	common.Add(*symbol.NewElfSymbol("__vector_tbl_reloc__"))
+	common.Add(*symbol.NewElfSymbol("__isr_vector"))
+
+	err = b.CopySymbols(common)
+	if err != nil {
+		return err
+	}
+
+	/* These symbols are needed by the split app so it can zero
+	 * bss and copy data from the loader app before it restarts,
+	 * but we have to rename them since it has its own copies of
+	 * these special linker symbols  */
+	tmp_sm := symbol.NewSymbolMap()
+	tmp_sm.Add(*symbol.NewElfSymbol("__HeapBase"))
+	tmp_sm.Add(*symbol.NewElfSymbol("__bss_start__"))
+	tmp_sm.Add(*symbol.NewElfSymbol("__bss_end__"))
+	tmp_sm.Add(*symbol.NewElfSymbol("__etext"))
+	tmp_sm.Add(*symbol.NewElfSymbol("__data_start__"))
+	tmp_sm.Add(*symbol.NewElfSymbol("__data_end__"))
+	err = c.RenameSymbols(tmp_sm, b.AppLinkerElfPath(), "_loader")
+
+	if err != nil {
+		return err
+	}
+	return nil
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newt/builder/buildpackage.go
----------------------------------------------------------------------
diff --git a/newt/builder/buildpackage.go b/newt/builder/buildpackage.go
index 287c769..b2d59fe 100644
--- a/newt/builder/buildpackage.go
+++ b/newt/builder/buildpackage.go
@@ -344,11 +344,11 @@ func (bpkg *BuildPackage) publicIncludeDirs(b *Builder) []string {
 
 	incls := []string{
 		bp + "/include",
-		bp + "/include/" + pkgBase + "/arch/" + b.Bsp.Arch,
+		bp + "/include/" + pkgBase + "/arch/" + b.target.Bsp.Arch,
 	}
 
 	if bpkg.Type() == pkg.PACKAGE_TYPE_SDK {
-		incls = append(incls, b.Bsp.BasePath()+"/include/bsp/")
+		incls = append(incls, b.target.Bsp.BasePath()+"/include/bsp/")
 
 		sdkIncls := bpkg.findSdkIncludes()
 		incls = append(incls, sdkIncls...)
@@ -362,18 +362,18 @@ func (bpkg *BuildPackage) privateIncludeDirs(b *Builder) []string {
 
 	incls := []string{}
 	incls = append(incls, srcDir)
-	incls = append(incls, srcDir+"/arch/"+b.Bsp.Arch)
+	incls = append(incls, srcDir+"/arch/"+b.target.Bsp.Arch)
 
 	if b.Features(bpkg)["TEST"] {
 		testSrcDir := srcDir + "/test"
 		incls = append(incls, testSrcDir)
-		incls = append(incls, testSrcDir+"/arch/"+b.Bsp.Arch)
+		incls = append(incls, testSrcDir+"/arch/"+b.target.Bsp.Arch)
 	}
 
 	// If pkgType == SDK, include all the items in "ext" directly into the
 	// include path
 	if bpkg.Type() == pkg.PACKAGE_TYPE_SDK {
-		incls = append(incls, b.Bsp.BasePath()+"/include/bsp/")
+		incls = append(incls, b.target.Bsp.BasePath()+"/include/bsp/")
 
 		sdkIncls := bpkg.findSdkIncludes()
 		incls = append(incls, sdkIncls...)

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newt/builder/buildutil.go
----------------------------------------------------------------------
diff --git a/newt/builder/buildutil.go b/newt/builder/buildutil.go
index c90251f..ca5a157 100644
--- a/newt/builder/buildutil.go
+++ b/newt/builder/buildutil.go
@@ -26,7 +26,6 @@ import (
 
 	log "github.com/Sirupsen/logrus"
 
-	"mynewt.apache.org/newt/newt/pkg"
 	"mynewt.apache.org/newt/newt/project"
 	"mynewt.apache.org/newt/util"
 )
@@ -36,7 +35,7 @@ func BinRoot() string {
 }
 
 func (b *Builder) BinDir() string {
-	return BinRoot() + "/" + b.target.ShortName()
+	return BinRoot() + "/" + b.target.target.Name() + "/" + b.buildName
 }
 
 func (b *Builder) PkgBinDir(pkgName string) string {
@@ -48,23 +47,33 @@ func (b *Builder) ArchivePath(pkgName string) string {
 	return b.PkgBinDir(pkgName) + "/" + filepath.Base(pkgName) + ".a"
 }
 
+func (b *Builder) AppTempElfPath() string {
+	pkgName := b.appPkg.Name()
+	return b.PkgBinDir(pkgName) + "/" + filepath.Base(pkgName) + "_tmp.elf"
+}
+
 func (b *Builder) AppElfPath() string {
-	pkgName := b.target.App().Name()
+	pkgName := b.appPkg.Name()
 	return b.PkgBinDir(pkgName) + "/" + filepath.Base(pkgName) + ".elf"
 }
 
+func (b *Builder) AppLinkerElfPath() string {
+	pkgName := b.appPkg.Name()
+	return b.PkgBinDir(pkgName) + "/" + filepath.Base(pkgName) + "linker.elf"
+}
+
 func (b *Builder) AppImgPath() string {
-	pkgName := b.target.App().Name()
+	pkgName := b.appPkg.Name()
 	return b.PkgBinDir(pkgName) + "/" + filepath.Base(pkgName) + ".img"
 }
 
 func (b *Builder) AppPath() string {
-	pkgName := b.target.App().Name()
+	pkgName := b.appPkg.Name()
 	return b.PkgBinDir(pkgName) + "/"
 }
 
 func (b *Builder) AppBinBasePath() string {
-	pkgName := b.target.App().Name()
+	pkgName := b.appPkg.Name()
 	return b.PkgBinDir(pkgName) + "/" + filepath.Base(pkgName)
 }
 
@@ -94,15 +103,6 @@ func (b *Builder) FeatureString() string {
 	return buffer.String()
 }
 
-func (b *Builder) resolveCompiler() *pkg.LocalPackage {
-	if b.Bsp.CompilerName == "" {
-		return nil
-	}
-	dep, _ := pkg.NewDependency(b.Bsp.Repo(), b.Bsp.CompilerName)
-	mypkg := project.GetProject().ResolveDependency(dep).(*pkg.LocalPackage)
-	return mypkg
-}
-
 // Makes sure all packages with required APIs have been augmented with a
 // dependency that satisfies that requirement.  If there are any unsatisfied
 // requirements, an error is returned.

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newt/builder/library.go
----------------------------------------------------------------------
diff --git a/newt/builder/library.go b/newt/builder/library.go
new file mode 100644
index 0000000..c3d7cde
--- /dev/null
+++ b/newt/builder/library.go
@@ -0,0 +1,218 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* this file parses a library file for the build and returns
+ * a list of all the symbols with their types and sizes */
+
+/* gets an objdump -t and parses into a symbolMap" */
+
+package builder
+
+import (
+	"bytes"
+	"path/filepath"
+	"regexp"
+	"strconv"
+
+	"mynewt.apache.org/newt/newt/symbol"
+	"mynewt.apache.org/newt/util"
+)
+
+/* This is a tricky thing to parse. Right now, I keep all the
+ * flags together and just store the offset, size, name and flags.
+* 00012970 l       .bss	00000000 _end
+* 00011c60 l       .init_array	00000000 __init_array_start
+* 00011c60 l       .init_array	00000000 __preinit_array_start
+* 000084b0 g     F .text	00000034 os_arch_start
+* 00000000 g       .debug_aranges	00000000 __HeapBase
+* 00011c88 g     O .data	00000008 g_os_task_list
+* 000082cc g     F .text	0000004c os_idle_task
+* 000094e0 g     F .text	0000002e .hidden __gnu_uldivmod_helper
+* 00000000 g       .svc_table	00000000 SVC_Count
+* 000125e4 g     O .bss	00000004 g_console_is_init
+* 00009514 g     F .text	0000029c .hidden __divdi3
+* 000085a8 g     F .text	00000054 os_eventq_put
+* 00000100       O *COM*	00000004 g_idle_task_stack
+
+*/
+func parseObjectLine(line string, r *regexp.Regexp) (error, *symbol.SymbolInfo) {
+
+	answer := r.FindAllStringSubmatch(line, 11)
+
+	if len(answer) == 0 {
+		return nil, nil
+	}
+
+	data := answer[0]
+
+	if len(data) != 6 {
+		util.StatusMessage(util.VERBOSITY_DEFAULT,
+			"Not enough content in object file line --- %s", line)
+		return nil, nil
+	}
+
+	si := symbol.NewSymbolInfo()
+
+	si.Name = data[5]
+
+	v, err := strconv.ParseUint(data[1], 16, 32)
+
+	if err != nil {
+		util.StatusMessage(util.VERBOSITY_DEFAULT,
+			"Could not convert location from object file line --- %s", line)
+		return nil, nil
+	}
+
+	si.Loc = int(v)
+
+	v, err = strconv.ParseUint(data[4], 16, 32)
+
+	if err != nil {
+		util.StatusMessage(util.VERBOSITY_DEFAULT,
+			"Could not convert size form object file line --- %s", line)
+		return nil, nil
+	}
+
+	si.Size = int(v)
+	si.Code = data[2]
+	si.Section = data[3]
+
+	/*  Common section has length in a different spot. Also, these
+	 * are really global variables so mark them as such */
+	if si.IsSection("*COM*") {
+		si.Size = (*si).Loc
+		si.Code = "g" + si.Code[1:]
+	}
+
+	return nil, si
+}
+
+func getParseRexeg() (error, *regexp.Regexp) {
+	r, err := regexp.Compile("^([0-9A-Fa-f]+)[\t ]+([lgu! ][w ][C ][W ][Ii ][Dd ][FfO ])[\t ]+([^\t\n\f\r ]+)[\t ]+([0-9a-fA-F]+)[\t ]([^\t\n\f\r ]+)")
+
+	if err != nil {
+		return err, nil
+	}
+
+	return nil, r
+}
+
+func (b *Builder) ParseObjectLibrary(bp *BuildPackage) (error, *symbol.SymbolMap) {
+
+	file := b.ArchivePath(bp.Name())
+	return b.ParseObjectLibraryFile(bp, file, true)
+}
+
+func (b *Builder) ParseObjectElf(elf_file string) (error, *symbol.SymbolMap) {
+	return b.ParseObjectLibraryFile(nil, elf_file, false)
+}
+
+func (b *Builder) ParseObjectLibraryFile(bp *BuildPackage,
+	file string, textDataOnly bool) (error, *symbol.SymbolMap) {
+
+	c, err := b.target.NewCompiler(b.AppElfPath())
+
+	ext := filepath.Ext(file)
+
+	if err != nil {
+		return err, nil
+	}
+
+	err, out := c.ParseLibrary(file)
+
+	if err != nil {
+		return err, nil
+	}
+
+	sm := symbol.NewSymbolMap()
+
+	buffer := bytes.NewBuffer(out)
+
+	err, r := getParseRexeg()
+
+	if err != nil {
+		return err, nil
+	}
+
+	for {
+		line, err := buffer.ReadString('\n')
+		if err != nil {
+			break
+		}
+		err, si := parseObjectLine(line, r)
+
+		if err == nil && si != nil {
+
+			/* assign the library */
+			if bp != nil {
+				(*si).Bpkg = bp.Name()
+			} else {
+				(*si).Bpkg = "elf"
+			}
+
+			/*  discard undefined */
+			if (*si).IsSection("*UND*") {
+				continue
+			}
+
+			/* discard debug symbols */
+			if (*si).IsDebug() {
+				continue
+			}
+
+			if (*si).IsFile() {
+				continue
+			}
+
+			/* if we are looking for text and data only, do a special check */
+			if textDataOnly {
+				include := (*si).IsSection(".bss") ||
+					(*si).IsSection(".text") ||
+					(*si).IsSection(".data") ||
+					(*si).IsSection("*COM*") ||
+					(*si).IsSection(".rodata")
+
+				if !include {
+					continue
+				}
+			}
+
+			/* add the symbol to the map */
+			(*si).Ext = ext
+			sm.Add(*si)
+			util.StatusMessage(util.VERBOSITY_VERBOSE,
+				"Keeping Symbol %s in package %s\n", (*si).Name, (*si).Bpkg)
+		}
+	}
+
+	return nil, sm
+}
+
+func (b *Builder) CopySymbols(sm *symbol.SymbolMap) error {
+
+	c, err := b.target.NewCompiler(b.AppElfPath())
+
+	if err != nil {
+		return err
+	}
+
+	err = c.CopySymbols(b.AppElfPath(), b.AppLinkerElfPath(), sm)
+
+	return err
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newt/builder/load.go
----------------------------------------------------------------------
diff --git a/newt/builder/load.go b/newt/builder/load.go
index 78b52d1..cdb0074 100644
--- a/newt/builder/load.go
+++ b/newt/builder/load.go
@@ -23,43 +23,63 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
-	"strings"
 
 	"mynewt.apache.org/newt/newt/project"
 	"mynewt.apache.org/newt/util"
 )
 
-func (b *Builder) Load() error {
-	if b.target.App() == nil {
+func (t *TargetBuilder) Load() error {
+
+	err := t.PrepBuild()
+
+	if err != nil {
+		return err
+	}
+
+	if t.Loader != nil {
+		err = t.App.Load(1)
+		if err == nil {
+			err = t.Loader.Load(0)
+		}
+	} else {
+		err = t.App.Load(0)
+	}
+
+	return err
+}
+
+func (b *Builder) Load(image_slot int) error {
+	if b.appPkg == nil {
 		return util.NewNewtError("app package not specified")
 	}
 
 	/*
 	 * Populate the package list and feature sets.
 	 */
-	err := b.PrepBuild()
+	err := b.target.PrepBuild()
 	if err != nil {
 		return err
 	}
 
-	if b.Bsp.DownloadScript == "" {
+	if b.target.Bsp.DownloadScript == "" {
 		/*
 		 *
 		 */
 		util.StatusMessage(util.VERBOSITY_DEFAULT,
-			"No download script for BSP %s\n", b.Bsp.Name())
+			"No download script for BSP %s\n", b.target.Bsp.Name())
 		return nil
 	}
 
-	bspPath := b.Bsp.BasePath()
-	downloadScript := filepath.Join(bspPath, b.Bsp.DownloadScript)
+	bspPath := b.target.Bsp.BasePath()
+	downloadScript := filepath.Join(bspPath, b.target.Bsp.DownloadScript)
 	binBaseName := b.AppBinBasePath()
 	featureString := b.FeatureString()
 
-	downloadCmd := fmt.Sprintf("%s %s %s %s",
-		downloadScript, bspPath, binBaseName, featureString)
+	downloadCmd := fmt.Sprintf("%s %s %s %d %s",
+		downloadScript, bspPath, binBaseName, image_slot, featureString)
 
-	util.StatusMessage(util.VERBOSITY_DEFAULT, "Loading image\n")
+	util.StatusMessage(util.VERBOSITY_DEFAULT,
+		"Loading %s image int slot %d\n", b.buildName, image_slot)
 	util.StatusMessage(util.VERBOSITY_VERBOSE, "Load command: %s\n",
 		downloadCmd)
 	rsp, err := util.ShellCommand(downloadCmd)
@@ -72,27 +92,49 @@ func (b *Builder) Load() error {
 	return nil
 }
 
-func (b *Builder) Debug() error {
-	if b.target.App() == nil {
+func (t *TargetBuilder) Debug() error {
+	//var additional_libs []string
+	err := t.PrepBuild()
+
+	if err != nil {
+		return err
+	}
+
+	//	if t.Loader != nil {
+	//		basename := t.Loader.AppElfPath()
+	//		name := strings.TrimSuffix(basename, filepath.Ext(basename))
+	//		additional_libs = append(additional_libs, name)
+	//	}
+
+	//	return t.App.Debug(additional_libs)
+	if t.Loader == nil {
+		return t.App.Debug(nil)
+	}
+	return t.Loader.Debug(nil)
+}
+
+func (b *Builder) Debug(addlibs []string) error {
+	if b.appPkg == nil {
 		return util.NewNewtError("app package not specified")
 	}
 
 	/*
 	 * Populate the package list and feature sets.
 	 */
-	err := b.PrepBuild()
+	err := b.target.PrepBuild()
 	if err != nil {
 		return err
 	}
 
-	bspPath := b.Bsp.BasePath()
-	debugScript := filepath.Join(bspPath, b.Bsp.DebugScript)
+	bspPath := b.target.Bsp.BasePath()
+	debugScript := filepath.Join(bspPath, b.target.Bsp.DebugScript)
 	binBaseName := b.AppBinBasePath()
-	featureString := strings.Split(b.FeatureString(), " ")
 
 	os.Chdir(project.GetProject().Path())
 
 	cmdLine := []string{debugScript, bspPath, binBaseName}
-	cmdLine = append(cmdLine, featureString...)
+	cmdLine = append(cmdLine, addlibs...)
+
+	fmt.Printf("%s\n", cmdLine)
 	return util.ShellInteractiveCommand(cmdLine)
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newt/builder/size.go
----------------------------------------------------------------------
diff --git a/newt/builder/size.go b/newt/builder/size.go
index cc2061e..37f1b47 100644
--- a/newt/builder/size.go
+++ b/newt/builder/size.go
@@ -315,16 +315,37 @@ func PrintSizes(libs map[string]*PkgSize,
 	return ret, nil
 }
 
+func (t *TargetBuilder) Size() error {
+
+	err := t.PrepBuild()
+
+	if err != nil {
+		return err
+	}
+
+	fmt.Printf("Size of Application Image: %s\n", t.App.buildName)
+	err = t.App.Size()
+
+	if err == nil {
+		if t.Loader != nil {
+			fmt.Printf("Size of Loader Image: %s\n", t.Loader.buildName)
+			err = t.Loader.Size()
+		}
+	}
+
+	return err
+}
+
 func (b *Builder) Size() error {
-	if b.target.App() == nil {
+	if b.appPkg == nil {
 		return util.NewNewtError("app package not specified for this target")
 	}
 
-	err := b.PrepBuild()
+	err := b.target.PrepBuild()
 	if err != nil {
 		return err
 	}
-	if b.Bsp.Arch == "sim" {
+	if b.target.Bsp.Arch == "sim" {
 		fmt.Println("'newt size' not supported for sim targets.")
 		return nil
 	}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newt/builder/targetbuild.go
----------------------------------------------------------------------
diff --git a/newt/builder/targetbuild.go b/newt/builder/targetbuild.go
new file mode 100644
index 0000000..3d9d96b
--- /dev/null
+++ b/newt/builder/targetbuild.go
@@ -0,0 +1,445 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package builder
+
+import (
+	"fmt"
+	"strings"
+
+	"mynewt.apache.org/newt/newt/interfaces"
+	"mynewt.apache.org/newt/newt/pkg"
+	"mynewt.apache.org/newt/newt/project"
+	"mynewt.apache.org/newt/newt/symbol"
+	"mynewt.apache.org/newt/newt/target"
+	"mynewt.apache.org/newt/newt/toolchain"
+	"mynewt.apache.org/newt/util"
+)
+
+type TargetBuilder struct {
+	compilerPkg *pkg.LocalPackage
+	Bsp         *pkg.BspPackage
+	target      *target.Target
+
+	App     *Builder
+	AppList interfaces.PackageList
+
+	Loader     *Builder
+	LoaderList interfaces.PackageList
+}
+
+func NewTargetBuilder(target *target.Target) (*TargetBuilder, error) {
+	t := &TargetBuilder{}
+
+	/* TODO */
+	t.target = target
+
+	return t, nil
+}
+
+func (t *TargetBuilder) NewCompiler(dstDir string) (*toolchain.Compiler, error) {
+	c, err := toolchain.NewCompiler(t.compilerPkg.BasePath(), dstDir,
+		t.target.BuildProfile)
+
+	return c, err
+}
+
+func (t *TargetBuilder) PrepBuild() error {
+
+	if t.Bsp != nil {
+		// Already prepped
+		return nil
+	}
+	// Collect the seed packages.
+	bspPkg := t.target.Bsp()
+	if bspPkg == nil {
+		if t.target.BspName == "" {
+			return util.NewNewtError("BSP package not specified by target")
+		} else {
+			return util.NewNewtError("BSP package not found: " +
+				t.target.BspName)
+		}
+	}
+	t.Bsp = pkg.NewBspPackage(bspPkg)
+
+	compilerPkg := t.resolveCompiler()
+	if compilerPkg == nil {
+		if t.Bsp.CompilerName == "" {
+			return util.NewNewtError("Compiler package not specified by BSP")
+		} else {
+			return util.NewNewtError("Compiler package not found: " +
+				t.Bsp.CompilerName)
+		}
+	}
+	t.compilerPkg = compilerPkg
+
+	appPkg := t.target.App()
+	targetPkg := t.target.Package()
+
+	app, err := NewBuilder(t, "app")
+
+	if err == nil {
+		t.App = app
+	} else {
+		return err
+	}
+
+	loaderPkg := t.target.Loader()
+
+	if loaderPkg != nil {
+		loader, err := NewBuilder(t, "loader")
+
+		if err == nil {
+			t.Loader = loader
+		} else {
+			return err
+		}
+
+		err = t.Loader.PrepBuild(loaderPkg, bspPkg, targetPkg)
+		if err != nil {
+			return err
+		}
+
+		loader_flag := toolchain.NewCompilerInfo()
+		loader_flag.Cflags = append(loader_flag.Cflags, "-DSPLIT_LOADER")
+		t.Loader.AddCompilerInfo(loader_flag)
+
+		t.LoaderList = project.ResetDeps(nil)
+	}
+
+	bsp_pkg := t.target.Bsp()
+
+	err = t.App.PrepBuild(appPkg, bsp_pkg, targetPkg)
+	if err != nil {
+		return err
+
+	}
+	if loaderPkg != nil {
+		app_flag := toolchain.NewCompilerInfo()
+		app_flag.Cflags = append(app_flag.Cflags, "-DSPLIT_APPLICATION")
+		t.App.AddCompilerInfo(app_flag)
+	}
+
+	t.AppList = project.ResetDeps(nil)
+
+	return nil
+}
+
+func (t *TargetBuilder) Build() error {
+	var err error
+	var linkerScript string
+
+	if err = t.target.Validate(true); err != nil {
+		return err
+	}
+
+	if err = t.PrepBuild(); err != nil {
+		return err
+	}
+
+	/* Build the Apps */
+	project.ResetDeps(t.AppList)
+
+	if err := t.Bsp.Reload(t.App.Features(t.App.BspPkg)); err != nil {
+		return err
+	}
+
+	err = t.App.Build()
+	if err != nil {
+		return err
+	}
+
+	/* if we have no loader, we are done here.  All of the rest of this
+	 * function is for split images */
+	if t.Loader == nil {
+		err = t.App.Link(t.Bsp.LinkerScript)
+		return err
+	}
+
+	/* Link the app as a test (using the normal single image linker script) */
+	err = t.App.TestLink(t.Bsp.LinkerScript)
+	if err != nil {
+		return err
+	}
+
+	/* rebuild the loader */
+	project.ResetDeps(t.LoaderList)
+
+	if err = t.Bsp.Reload(t.Loader.Features(t.Loader.BspPkg)); err != nil {
+		return err
+	}
+
+	err = t.Loader.Build()
+
+	if err != nil {
+		return err
+	}
+
+	/* perform a test link of the loader */
+	err = t.Loader.TestLink(t.Bsp.LinkerScript)
+
+	if err != nil {
+		return err
+	}
+
+	/* re-link the loader with app dependencies */
+	err, common_pkgs, common_syms := t.RelinkLoader()
+	if err != nil {
+		return err
+	}
+
+	/* The app can ignore these packages next time */
+	t.App.RemovePackages(common_pkgs)
+	/* add back the BSP package which needs linking in both */
+	t.App.AddPackage(t.Bsp.LocalPackage)
+
+	/* create the special elf to link the app against */
+	/* its just the elf with a set of symbols removed and renamed */
+	err = t.Loader.buildRomElf(common_syms)
+	if err != nil {
+		return err
+	}
+
+	/* set up the linker elf and linker script for the app */
+	t.App.LinkElf = t.Loader.AppLinkerElfPath()
+	linkerScript = t.Bsp.Part2LinkerScript
+
+	if linkerScript == "" {
+		return util.NewNewtError("BSP Must specify Linker script ")
+	}
+
+	/* link the app */
+	err = t.App.Link(linkerScript)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+/*
+ * This function re-links the loader adding symbols from libraries
+ * shared with the app. Returns a list of the common packages shared
+ * by the app and loader
+ */
+func (t *TargetBuilder) RelinkLoader() (error, map[string]bool, *symbol.SymbolMap) {
+
+	/* fetch symbols from the elf and from the libraries themselves */
+	err, appLibSym := t.App.ExtractSymbolInfo()
+	if err != nil {
+		return err, nil, nil
+	}
+
+	/* fetch the symbol list from the app temporary elf */
+	err, appElfSym := t.App.ParseObjectElf(t.App.AppTempElfPath())
+	if err != nil {
+		return err, nil, nil
+	}
+
+	/* extract the library symbols and elf symbols from the loader */
+	err, loaderLibSym := t.Loader.ExtractSymbolInfo()
+	if err != nil {
+		return err, nil, nil
+	}
+
+	err, loaderElfSym := t.Loader.ParseObjectElf(t.Loader.AppTempElfPath())
+	if err != nil {
+		return err, nil, nil
+	}
+
+	/* create the set of matching and non-matching symbols */
+	err, sm_match, sm_nomatch := symbol.IdenticalUnion(appLibSym,
+		loaderLibSym, true, false)
+
+	/* which packages are shared between the two */
+	common_pkgs := sm_match.Packages()
+	uncommon_pkgs := sm_nomatch.Packages()
+
+	/* ensure that the loader and app packages are never shared */
+	delete(common_pkgs, t.App.appPkg.Name())
+	uncommon_pkgs[t.App.appPkg.Name()] = true
+	ma := sm_match.FilterPkg(t.App.appPkg.Name())
+	sm_match.RemoveMap(ma)
+
+	delete(common_pkgs, t.Loader.appPkg.Name())
+	uncommon_pkgs[t.Loader.appPkg.Name()] = true
+	ml := sm_match.FilterPkg(t.Loader.appPkg.Name())
+	sm_match.RemoveMap(ml)
+
+	util.StatusMessage(util.VERBOSITY_VERBOSE,
+		"Putting %d symbols from %d packages into Loader\n",
+		len(*sm_match), len(common_pkgs))
+
+	/* This is worth a special comment.  We are building both apps as
+	 * stand-alone apps against the normal linker file, so they will both
+	 * have a Reset_Handler symbol.  We need to ignore this here.  When
+	 * we build the split app, we use a special linker file which
+	 * uses a different entry point */
+	special_sm := symbol.NewSymbolMap()
+	special_sm.Add(*symbol.NewElfSymbol("Reset_Handler(app)"))
+	special_sm.Add(*symbol.NewElfSymbol("Reset_Handler(loader)"))
+
+	var badpkgs []string
+	var symbol_str string
+	for v, _ := range uncommon_pkgs {
+		if t.App.appPkg != nil && t.App.appPkg.Name() != v &&
+			t.Loader.appPkg != nil && t.Loader.appPkg.Name() != v {
+			trouble := sm_nomatch.FilterPkg(v)
+
+			var found bool
+			for _, sym := range *trouble {
+				if _, ok := special_sm.Find(sym.Name); !ok {
+					if !sym.IsLocal() {
+						found = true
+					}
+				}
+			}
+
+			if found {
+				symbol_str = (*trouble).String("Non Matching Symbols")
+				badpkgs = append(badpkgs, v)
+				delete(common_pkgs, v)
+			}
+		}
+	}
+
+	if len(badpkgs) > 0 {
+		errStr := fmt.Sprintf("Common packages with different implementaiton\n %s \n",
+			strings.Join(badpkgs, "\n "))
+		errStr += symbol_str
+		return util.NewNewtError(errStr), nil, nil
+	}
+
+	/* for each symbol in the elf of the app, if that symbol is in
+	 * a common package, keep that symbol in the loader */
+	preserve_elf := symbol.NewSymbolMap()
+
+	/* go through each symbol in the app */
+	for _, elfsym := range *appElfSym {
+		name := elfsym.Name
+		if libsym, ok := (*appLibSym)[name]; ok {
+			if _, ok := common_pkgs[libsym.Bpkg]; ok {
+				/* if its not in the loader elf, add it as undefined */
+				if _, ok := (*loaderElfSym)[name]; !ok {
+					preserve_elf.Add(elfsym)
+				}
+			}
+		}
+	}
+
+	/* re-link loader */
+	project.ResetDeps(t.LoaderList)
+
+	util.StatusMessage(util.VERBOSITY_VERBOSE,
+		"Migrating %d unused symbols into Loader\n", len(*preserve_elf))
+
+	err = t.Loader.KeepLink(t.Bsp.LinkerScript, preserve_elf)
+
+	if err != nil {
+		return err, nil, nil
+	}
+	return err, common_pkgs, sm_match
+}
+
+func (t *TargetBuilder) Test(p *pkg.LocalPackage) error {
+	if err := t.target.Validate(false); err != nil {
+		return err
+	}
+
+	if t.Bsp != nil {
+		// Already prepped
+		return nil
+	}
+	// Collect the seed packages.
+	bspPkg := t.target.Bsp()
+	if bspPkg == nil {
+		if t.target.BspName == "" {
+			return util.NewNewtError("BSP package not specified by target")
+		} else {
+			return util.NewNewtError("BSP package not found: " +
+				t.target.BspName)
+		}
+	}
+	t.Bsp = pkg.NewBspPackage(bspPkg)
+
+	compilerPkg := t.resolveCompiler()
+	if compilerPkg == nil {
+		if t.Bsp.CompilerName == "" {
+			return util.NewNewtError("Compiler package not specified by BSP")
+		} else {
+			return util.NewNewtError("Compiler package not found: " +
+				t.Bsp.CompilerName)
+		}
+	}
+	t.compilerPkg = compilerPkg
+
+	targetPkg := t.target.Package()
+
+	app, err := NewBuilder(t, "test")
+
+	if err == nil {
+		t.App = app
+	} else {
+		return err
+	}
+
+	// A few features are automatically supported when the test command is
+	// used:
+	//     * TEST:      ensures that the test code gets compiled.
+	//     * SELFTEST:  indicates that there is no app.
+	t.App.AddFeature("TEST")
+	t.App.AddFeature("SELFTEST")
+
+	err = t.App.PrepBuild(p, bspPkg, targetPkg)
+
+	if err != nil {
+		return err
+	}
+
+	err = t.App.Test(p)
+
+	return err
+}
+
+func (t *TargetBuilder) resolveCompiler() *pkg.LocalPackage {
+	if t.Bsp.CompilerName == "" {
+		return nil
+	}
+	dep, _ := pkg.NewDependency(t.Bsp.Repo(), t.Bsp.CompilerName)
+	mypkg := project.GetProject().ResolveDependency(dep).(*pkg.LocalPackage)
+	return mypkg
+}
+
+func (t *TargetBuilder) Clean() error {
+	var err error
+
+	err = t.PrepBuild()
+
+	if err == nil && t.App != nil {
+		err = t.App.Clean()
+	}
+	if err == nil && t.Loader != nil {
+		err = t.Loader.Clean()
+	}
+	return err
+}
+
+func (t *TargetBuilder) GetTarget() *target.Target {
+	return (*t).target
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newt/cli/build_cmds.go
----------------------------------------------------------------------
diff --git a/newt/cli/build_cmds.go b/newt/cli/build_cmds.go
index 2d44ac7..bc83c8d 100644
--- a/newt/cli/build_cmds.go
+++ b/newt/cli/build_cmds.go
@@ -68,7 +68,7 @@ func buildRunCmd(cmd *cobra.Command, args []string) {
 		util.StatusMessage(util.VERBOSITY_DEFAULT, "Building target %s\n",
 			t.FullName())
 
-		b, err := builder.NewBuilder(t)
+		b, err := builder.NewTargetBuilder(t)
 		if err != nil {
 			NewtUsage(nil, err)
 		}
@@ -78,8 +78,10 @@ func buildRunCmd(cmd *cobra.Command, args []string) {
 			NewtUsage(nil, err)
 		}
 
-		util.StatusMessage(util.VERBOSITY_DEFAULT, "App successfully built: "+
-			"%s\n", b.AppElfPath())
+		util.StatusMessage(util.VERBOSITY_DEFAULT, "Target successfully built: "+
+			"%s\n", targetName)
+
+		/* TODO */
 	}
 }
 
@@ -116,7 +118,7 @@ func cleanRunCmd(cmd *cobra.Command, args []string) {
 		}
 	} else {
 		for _, t := range targets {
-			b, err := builder.NewBuilder(t)
+			b, err := builder.NewTargetBuilder(t)
 			if err != nil {
 				NewtUsage(cmd, err)
 			}
@@ -189,7 +191,7 @@ func testRunCmd(cmd *cobra.Command, args []string) {
 				TARGET_TEST_NAME))
 		}
 
-		b, err := builder.NewBuilder(t)
+		b, err := builder.NewTargetBuilder(t)
 		if err != nil {
 			NewtUsage(nil, err)
 		}
@@ -241,7 +243,7 @@ func loadRunCmd(cmd *cobra.Command, args []string) {
 		NewtUsage(cmd, util.NewNewtError("Invalid target name: "+args[0]))
 	}
 
-	b, err := builder.NewBuilder(t)
+	b, err := builder.NewTargetBuilder(t)
 	if err != nil {
 		NewtUsage(cmd, err)
 	}
@@ -265,7 +267,7 @@ func debugRunCmd(cmd *cobra.Command, args []string) {
 		NewtUsage(cmd, util.NewNewtError("Invalid target name: "+args[0]))
 	}
 
-	b, err := builder.NewBuilder(t)
+	b, err := builder.NewTargetBuilder(t)
 	if err != nil {
 		NewtUsage(cmd, err)
 	}
@@ -289,7 +291,7 @@ func sizeRunCmd(cmd *cobra.Command, args []string) {
 		NewtUsage(cmd, util.NewNewtError("Invalid target name: "+args[0]))
 	}
 
-	b, err := builder.NewBuilder(t)
+	b, err := builder.NewTargetBuilder(t)
 	if err != nil {
 		NewtUsage(cmd, err)
 	}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newt/cli/image_cmds.go
----------------------------------------------------------------------
diff --git a/newt/cli/image_cmds.go b/newt/cli/image_cmds.go
index 5057486..e3da963 100644
--- a/newt/cli/image_cmds.go
+++ b/newt/cli/image_cmds.go
@@ -29,7 +29,42 @@ import (
 	"mynewt.apache.org/newt/util"
 )
 
+func CreateImage(b *builder.Builder, version string,
+	keystr string, keyId uint8, loader *image.Image) (error, *image.Image) {
+
+	/* do the app image */
+	app_image, err := image.NewImage(b)
+	if err != nil {
+		return err, nil
+	}
+
+	err = app_image.SetVersion(version)
+	if err != nil {
+		return err, nil
+	}
+
+	if keystr != "" {
+		err = app_image.SetSigningKey(keystr, keyId)
+		if err != nil {
+			return err, nil
+		}
+	}
+
+	err = app_image.Generate(loader)
+	if err != nil {
+		return err, nil
+	}
+
+	util.StatusMessage(util.VERBOSITY_DEFAULT,
+		"App image succesfully generated: %s\n", app_image.TargetImg())
+
+	return nil, app_image
+}
+
 func createImageRunCmd(cmd *cobra.Command, args []string) {
+	var keyId uint8
+	var keystr string
+
 	if err := project.Initialize(); err != nil {
 		NewtUsage(cmd, err)
 	}
@@ -43,58 +78,58 @@ func createImageRunCmd(cmd *cobra.Command, args []string) {
 		NewtUsage(cmd, util.NewNewtError("Invalid target name: "+targetName))
 	}
 
-	b, err := builder.NewBuilder(t)
-	if err != nil {
-		NewtUsage(cmd, err)
-		return
+	version := args[1]
+
+	if len(args) > 2 {
+		if len(args) > 3 {
+			keyId64, err := strconv.ParseUint(args[3], 10, 8)
+			if err != nil {
+				NewtUsage(cmd,
+					util.NewNewtError("Key ID must be between 0-255"))
+			}
+			keyId = uint8(keyId64)
+		}
+		keystr = args[2]
 	}
 
-	err = b.PrepBuild()
+	b, err := builder.NewTargetBuilder(t)
 	if err != nil {
 		NewtUsage(cmd, err)
 		return
 	}
 
-	image, err := image.NewImage(b)
+	err = b.PrepBuild()
 	if err != nil {
 		NewtUsage(cmd, err)
 		return
 	}
 
-	err = image.SetVersion(args[1])
-	if err != nil {
-		NewtUsage(cmd, err)
-	}
+	var app_img *image.Image
+	var loader_img *image.Image
 
-	if len(args) > 2 {
-		var keyId uint8 = 0
-		if len(args) > 3 {
-			keyId64, err := strconv.ParseUint(args[3], 10, 8)
-			if err != nil {
-				NewtUsage(cmd,
-					util.NewNewtError("Key ID must be between 0-255"))
-			}
-			keyId = uint8(keyId64)
+	if b.Loader == nil {
+		err, app_img = CreateImage(b.App, version, keystr, keyId, nil)
+		if err != nil {
+			NewtUsage(cmd, err)
+			return
 		}
-		err = image.SetSigningKey(args[2], keyId)
+	} else {
+		err, loader_img = CreateImage(b.Loader, version, keystr, keyId, nil)
 		if err != nil {
 			NewtUsage(cmd, err)
+			return
 		}
-	}
 
-	err = image.Generate()
-	if err != nil {
-		NewtUsage(cmd, err)
-	}
+		err, app_img = CreateImage(b.App, version, keystr, keyId, loader_img)
+		if err != nil {
+			NewtUsage(cmd, err)
+			return
+		}
 
-	err = image.CreateManifest(t)
-	if err != nil {
-		NewtUsage(cmd, err)
 	}
-	util.StatusMessage(util.VERBOSITY_DEFAULT,
-		"App image succesfully generated: %s\n", image.TargetImg())
-	util.StatusMessage(util.VERBOSITY_DEFAULT, "Build manifest: %s\n",
-		image.ManifestFile())
+
+	build_id := image.CreateBuildId(app_img, loader_img)
+	err = image.CreateManifest(b, app_img, loader_img, build_id)
 }
 
 func AddImageCommands(cmd *cobra.Command) {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newt/cli/run_cmds.go
----------------------------------------------------------------------
diff --git a/newt/cli/run_cmds.go b/newt/cli/run_cmds.go
index 4784d28..57309a1 100644
--- a/newt/cli/run_cmds.go
+++ b/newt/cli/run_cmds.go
@@ -42,7 +42,7 @@ func runRunCmd(cmd *cobra.Command, args []string) {
 		NewtUsage(cmd, util.NewNewtError("Invalid target name: "+args[0]))
 	}
 
-	b, err := builder.NewBuilder(t)
+	b, err := builder.NewTargetBuilder(t)
 	if err != nil {
 		NewtUsage(nil, err)
 	}
@@ -58,26 +58,37 @@ func runRunCmd(cmd *cobra.Command, args []string) {
 	 * will barf if it needs an image for this type of target, instead of
 	 * downloading an older version.
 	 */
+	var app_img *image.Image
+	var loader_img *image.Image
 	if len(args) > 1 {
-		image, err := image.NewImage(b)
-		if err != nil {
-			NewtUsage(cmd, err)
-		}
-		err = image.SetVersion(args[1])
-		if err != nil {
-			NewtUsage(cmd, err)
-		}
-		err = image.Generate()
-		if err != nil {
-			NewtUsage(cmd, err)
-		}
-		err = image.CreateManifest(t)
-		if err != nil {
-			NewtUsage(cmd, err)
+		if b.Loader == nil {
+			err, app_img = CreateImage(b.App, args[1], "", 0, nil)
+			if err != nil {
+				NewtUsage(cmd, err)
+			}
+		} else {
+			err, loader_img = CreateImage(b.Loader, args[1], "", 0, nil)
+			if err != nil {
+				NewtUsage(cmd, err)
+			}
+			err, app_img = CreateImage(b.App, args[1], "", 0, loader_img)
+			if err != nil {
+				NewtUsage(cmd, err)
+			}
+
 		}
 	} else {
-		os.Remove(b.AppImgPath())
+		os.Remove(b.App.AppImgPath())
+		os.Remove(b.Loader.AppImgPath())
+	}
+
+	build_id := image.CreateBuildId(app_img, loader_img)
+
+	err = image.CreateManifest(b, app_img, loader_img, build_id)
+	if err != nil {
+		NewtUsage(cmd, err)
 	}
+
 	err = b.Load()
 	if err != nil {
 		NewtUsage(cmd, err)

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newt/image/image.go
----------------------------------------------------------------------
diff --git a/newt/image/image.go b/newt/image/image.go
index 0af34b6..d5fc091 100644
--- a/newt/image/image.go
+++ b/newt/image/image.go
@@ -29,6 +29,7 @@ import (
 	"crypto/x509"
 	"encoding/asn1"
 	"encoding/binary"
+	"encoding/hex"
 	"encoding/json"
 	"encoding/pem"
 	"fmt"
@@ -45,7 +46,6 @@ import (
 	log "github.com/Sirupsen/logrus"
 
 	"mynewt.apache.org/newt/newt/builder"
-	"mynewt.apache.org/newt/newt/target"
 	"mynewt.apache.org/newt/util"
 )
 
@@ -57,8 +57,6 @@ type ImageVersion struct {
 }
 
 type Image struct {
-	builder *builder.Builder
-
 	sourceBin    string
 	targetImg    string
 	manifestFile string
@@ -104,6 +102,7 @@ const (
 	IMAGE_F_SHA256                = 0x00000002 /* Image contains hash TLV */
 	IMAGE_F_PKCS15_RSA2048_SHA256 = 0x00000004 /* PKCS15 w/RSA2048 and SHA256 */
 	IMAGE_F_ECDSA224_SHA256       = 0x00000008 /* ECDSA224 over SHA256 */
+	IMAGE_F_NON_BOOTABLE          = 0x00000010 /* non bootable image */
 )
 
 /*
@@ -119,12 +118,16 @@ const (
  * Data that's going to go to build manifest file
  */
 type ImageManifest struct {
-	Date    string              `json:"build_time"`
-	Version string              `json:"build_version"`
-	Hash    string              `json:"id"`
-	Image   string              `json:"image"`
-	Pkgs    []*ImageManifestPkg `json:"pkgs"`
-	TgtVars []string            `json:"target"`
+	Date       string              `json:"build_time"`
+	Version    string              `json:"build_version"`
+	BuildID    string              `json:"id"`
+	Image      string              `json:"image"`
+	ImageHash  string              `json:"image_hash"`
+	Loader     string              `json:"loader"`
+	LoaderHash string              `json:"loader_hash"`
+	Pkgs       []*ImageManifestPkg `json:"pkgs"`
+	LoaderPkgs []*ImageManifestPkg `json:"loader_pkgs"`
+	TgtVars    []string            `json:"target"`
 }
 
 type ImageManifestPkg struct {
@@ -137,9 +140,7 @@ type ECDSASig struct {
 }
 
 func NewImage(b *builder.Builder) (*Image, error) {
-	image := &Image{
-		builder: b,
-	}
+	image := &Image{}
 
 	image.sourceBin = b.AppElfPath() + ".bin"
 	image.targetImg = b.AppImgPath()
@@ -246,7 +247,7 @@ func (image *Image) SetSigningKey(fileName string, keyId uint8) error {
 	return nil
 }
 
-func (image *Image) Generate() error {
+func (image *Image) Generate(loader *Image) error {
 	binFile, err := os.Open(image.sourceBin)
 	if err != nil {
 		return util.NewNewtError(fmt.Sprintf("Can't open app binary: %s",
@@ -273,6 +274,14 @@ func (image *Image) Generate() error {
 	 */
 	hash := sha256.New()
 
+	if loader != nil {
+		err = binary.Write(hash, binary.LittleEndian, loader.hash)
+		if err != nil {
+			return util.NewNewtError(fmt.Sprintf("Failed to seed hash: %s",
+				err.Error()))
+		}
+	}
+
 	/*
 	 * First the header
 	 */
@@ -288,18 +297,26 @@ func (image *Image) Generate() error {
 		Vers:  image.version,
 		Pad3:  0,
 	}
+
 	if image.signingRSA != nil {
-		hdr.TlvSz = 4 + 256 + 4 + 32
-		hdr.Flags = IMAGE_F_PKCS15_RSA2048_SHA256 | IMAGE_F_SHA256
+		hdr.TlvSz = 4 + 256
+		hdr.Flags = IMAGE_F_PKCS15_RSA2048_SHA256
 		hdr.KeyId = image.keyId
 	} else if image.signingEC != nil {
-		hdr.TlvSz = 4 + 68 + 4 + 32
-		hdr.Flags = IMAGE_F_ECDSA224_SHA256 | IMAGE_F_SHA256
+		hdr.TlvSz = 4 + 68
+		hdr.Flags = IMAGE_F_ECDSA224_SHA256
 	} else {
 		hdr.TlvSz = 4 + 32
 		hdr.Flags = IMAGE_F_SHA256
 	}
 
+	hdr.TlvSz += 4 + 32
+	hdr.Flags |= IMAGE_F_SHA256
+
+	if loader != nil {
+		hdr.Flags |= IMAGE_F_NON_BOOTABLE
+	}
+
 	err = binary.Write(imgFile, binary.LittleEndian, hdr)
 	if err != nil {
 		return util.NewNewtError(fmt.Sprintf("Failed to serialize image hdr: %s",
@@ -426,31 +443,51 @@ func (image *Image) Generate() error {
 		}
 	}
 
+	util.StatusMessage(util.VERBOSITY_VERBOSE,
+		"Computed Hash for image %s as %s \n", image.TargetImg(), hex.EncodeToString(image.hash[:]))
 	return nil
 }
 
-func (image *Image) CreateManifest(t *target.Target) error {
+func CreateBuildId(app *Image, loader *Image) []byte {
+	return app.hash
+}
+
+func CreateManifest(t *builder.TargetBuilder, app *Image, loader *Image, build_id []byte) error {
 	versionStr := fmt.Sprintf("%d.%d.%d.%d",
-		image.version.Major, image.version.Minor,
-		image.version.Rev, image.version.BuildNum)
-	hashStr := fmt.Sprintf("%x", image.hash)
+		app.version.Major, app.version.Minor,
+		app.version.Rev, app.version.BuildNum)
+	hashStr := fmt.Sprintf("%x", app.hash)
 	timeStr := time.Now().Format(time.RFC3339)
 
 	manifest := &ImageManifest{
-		Version: versionStr,
-		Hash:    hashStr,
-		Image:   filepath.Base(image.targetImg),
-		Date:    timeStr,
+		Version:   versionStr,
+		ImageHash: hashStr,
+		Image:     filepath.Base(app.targetImg),
+		Date:      timeStr,
 	}
 
-	for _, builtPkg := range image.builder.Packages {
+	for _, builtPkg := range t.App.Packages {
 		imgPkg := &ImageManifestPkg{
 			Name: builtPkg.Name(),
 		}
 		manifest.Pkgs = append(manifest.Pkgs, imgPkg)
 	}
 
-	vars := t.Vars
+	if loader != nil {
+		manifest.Loader = filepath.Base(loader.targetImg)
+		manifest.LoaderHash = fmt.Sprintf("%x", loader.hash)
+
+		for _, builtPkg := range t.Loader.Packages {
+			imgPkg := &ImageManifestPkg{
+				Name: builtPkg.Name(),
+			}
+			manifest.LoaderPkgs = append(manifest.LoaderPkgs, imgPkg)
+		}
+	}
+
+	manifest.BuildID = fmt.Sprintf("%x", build_id)
+
+	vars := t.GetTarget().Vars
 	var keys []string
 	for k := range vars {
 		keys = append(keys, k)
@@ -459,10 +496,10 @@ func (image *Image) CreateManifest(t *target.Target) error {
 	for _, k := range keys {
 		manifest.TgtVars = append(manifest.TgtVars, k+"="+vars[k])
 	}
-	file, err := os.Create(image.manifestFile)
+	file, err := os.Create(app.manifestFile)
 	if err != nil {
 		return util.NewNewtError(fmt.Sprintf("Cannot create manifest file %s: %s",
-			image.manifestFile, err.Error()))
+			app.manifestFile, err.Error()))
 	}
 	defer file.Close()
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newt/newtutil/newtutil.go
----------------------------------------------------------------------
diff --git a/newt/newtutil/newtutil.go b/newt/newtutil/newtutil.go
index 0930029..42e68fb 100644
--- a/newt/newtutil/newtutil.go
+++ b/newt/newtutil/newtutil.go
@@ -21,6 +21,8 @@ package newtutil
 
 import (
 	"fmt"
+	"io"
+	"os"
 	"sort"
 	"strconv"
 	"strings"
@@ -136,3 +138,25 @@ func BuildPackageString(repoName string, pkgName string) string {
 		return pkgName
 	}
 }
+
+func CopyFile(dst string, src string) error {
+	// open files r and w
+	r, err := os.Open(src)
+	if err != nil {
+		return err
+	}
+	defer r.Close()
+
+	w, err := os.Create(dst)
+	if err != nil {
+		return err
+	}
+	defer w.Close()
+
+	// do the actual work
+	_, err = io.Copy(w, r)
+	if err != nil {
+		return err
+	}
+	return nil
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newt/pkg/bsp_package.go
----------------------------------------------------------------------
diff --git a/newt/pkg/bsp_package.go b/newt/pkg/bsp_package.go
index b1574c6..a50a61a 100644
--- a/newt/pkg/bsp_package.go
+++ b/newt/pkg/bsp_package.go
@@ -26,11 +26,12 @@ import (
 
 type BspPackage struct {
 	*LocalPackage
-	CompilerName   string
-	Arch           string
-	LinkerScript   string
-	DownloadScript string
-	DebugScript    string
+	CompilerName      string
+	Arch              string
+	LinkerScript      string
+	Part2LinkerScript string /* script to link app to second partition */
+	DownloadScript    string
+	DebugScript       string
 }
 
 func (bsp *BspPackage) Reload(features map[string]bool) error {
@@ -40,6 +41,8 @@ func (bsp *BspPackage) Reload(features map[string]bool) error {
 		features, "pkg.arch")
 	bsp.LinkerScript = newtutil.GetStringFeatures(bsp.LocalPackage.Viper,
 		features, "pkg.linkerscript")
+	bsp.Part2LinkerScript = newtutil.GetStringFeatures(bsp.LocalPackage.Viper,
+		features, "pkg.part2linkerscript")
 	bsp.DownloadScript = newtutil.GetStringFeatures(bsp.LocalPackage.Viper,
 		features, "pkg.downloadscript")
 	bsp.DebugScript = newtutil.GetStringFeatures(bsp.LocalPackage.Viper,

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newt/project/project.go
----------------------------------------------------------------------
diff --git a/newt/project/project.go b/newt/project/project.go
index 8a45a8f..ede1717 100644
--- a/newt/project/project.go
+++ b/newt/project/project.go
@@ -124,6 +124,19 @@ func ResetProject() {
 	globalProject = nil
 }
 
+func ResetDeps(newList interfaces.PackageList) interfaces.PackageList {
+	if globalProject == nil {
+		return nil
+	}
+	oldList := globalProject.packages
+	globalProject.packages = newList
+
+	if newList == nil {
+		globalProject.loadPackageList()
+	}
+	return oldList
+}
+
 func NewProject(dir string) (*Project, error) {
 	proj := &Project{}
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newt/symbol/symbol.go
----------------------------------------------------------------------
diff --git a/newt/symbol/symbol.go b/newt/symbol/symbol.go
new file mode 100644
index 0000000..9d155ca
--- /dev/null
+++ b/newt/symbol/symbol.go
@@ -0,0 +1,322 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* this file maintains a list of all the symbols from a */
+
+package symbol
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+
+	"mynewt.apache.org/newt/util"
+)
+
+type SymbolInfo struct {
+	Bpkg    string
+	Name    string
+	Code    string
+	Section string
+	Ext     string
+	Size    int
+	Loc     int
+}
+
+type SymbolMap map[string]SymbolInfo
+
+func NewSymbolMap() *SymbolMap {
+	val := &SymbolMap{}
+	return val
+}
+
+func NewSymbolInfo() *SymbolInfo {
+	val := &SymbolInfo{}
+	return val
+}
+
+func NewElfSymbol(name string) *SymbolInfo {
+	val := NewSymbolInfo()
+	val.Name = name
+	val.Ext = ".elf"
+	return val
+}
+
+func (s *SymbolMap) Add(info SymbolInfo) {
+	(*s)[info.Name] = info
+}
+
+func (s *SymbolMap) GlobalFunctionsOnly() *SymbolMap {
+	s3 := NewSymbolMap()
+
+	for _, info1 := range *s {
+		if info1.IsFunction() && !info1.IsLocal() {
+			s3.Add(info1)
+		}
+	}
+	return s3
+}
+
+func (s *SymbolMap) GlobalDataOnly() *SymbolMap {
+	s3 := NewSymbolMap()
+
+	for _, info1 := range *s {
+		if !info1.IsFunction() && !info1.IsLocal() {
+			s3.Add(info1)
+		}
+	}
+	return s3
+}
+
+func (s *SymbolMap) Packages() map[string]bool {
+	pkg := make(map[string]bool)
+	for _, info1 := range *s {
+		pkg[info1.Bpkg] = true
+	}
+	return pkg
+}
+
+func IdenticalUnion(s1 *SymbolMap, s2 *SymbolMap, comparePkg bool,
+	compareAddr bool) (error, *SymbolMap, *SymbolMap) {
+	s3 := NewSymbolMap()
+	s_no := NewSymbolMap()
+	var err_str string
+	var err error
+	/* look through all symbols in S1 and if they are in s1,
+	 * add to new map s3 */
+
+	for name, info1 := range *s1 {
+		if info2, ok := (*s2)[name]; ok {
+			var pkg bool
+			var addr bool
+
+			if comparePkg {
+				pkg = info1.Bpkg == info2.Bpkg
+			} else {
+				pkg = true
+			}
+
+			if compareAddr {
+				addr = info1.Loc == info2.Loc
+			} else {
+				addr = true
+			}
+
+			/* compare to info 1 */
+			if info1.Code == info2.Code &&
+				info1.Size == info2.Size && pkg && addr {
+				s3.Add(info1)
+			} else if !info1.IsLocal() && !info1.IsFunction() {
+				/* Here is an unusual case.  We have a global data
+				 * symbol (bss or data) with the same name that is used
+				 * in both apps.  If code is linked against both of these
+				 * the code in the loader will call one while the code in
+				 * the app will call the other.  If the intention was for
+				 * these to be the same, then things are bad.  */
+				if err_str == "" {
+					err_str = "There are global symbols with the same name that " +
+						"are access via the loader and split application.  These " +
+						"symbols are either different sizes or from different " +
+						"packages.  Reconcile this issue before buidling.  If the " +
+						"symbols are intended to be shared by both, move the " +
+						"symbol to a package that is shared by both apps. If " +
+						"the symbols are distict (not shared), then make them " +
+						"static or rename them so they do not conflict" +
+						"\nNon Matching Symbols:\n"
+				}
+
+				err_str = err_str + fmt.Sprintf("%s-%s\n", info1.Sprintf(), info2.Sprintf())
+			} else {
+				info1.Name = info1.Name + "(app)"
+				info2.Name = info2.Name + "(loader)"
+				s_no.Add(info1)
+				s_no.Add(info2)
+			}
+		}
+	}
+
+	if err_str != "" {
+		err = util.NewNewtError(err_str)
+	}
+	return err, s3, s_no
+}
+
+type SymbolMapIterator func(s *SymbolInfo)
+
+func sprintfSi(si *SymbolInfo) string {
+	str := fmt.Sprintf("  %32s(%4s) (%8s) -- (%12s) %5d (0x%08x) from %s\n",
+		(*si).Name, (*si).Ext, (*si).Code, (*si).Section,
+		(*si).Size, (*si).Loc, (*si).Bpkg)
+	return str
+}
+
+func dumpSi(si *SymbolInfo) {
+	fmt.Printf(sprintfSi(si))
+}
+
+func (si *SymbolInfo) Dump() {
+	dumpSi(si)
+}
+
+func (si *SymbolInfo) Sprintf() string {
+	return sprintfSi(si)
+}
+
+func (si *SymbolInfo) IsLocal() bool {
+	val := (*si).Code[:1]
+
+	if val == "l" {
+		return true
+	}
+	return false
+}
+
+func (si *SymbolInfo) IsWeak() bool {
+	val := (*si).Code[1:2]
+
+	if val == "w" {
+		return true
+	}
+	return false
+}
+
+func (si *SymbolInfo) IsDebug() bool {
+	val := (*si).Code[5:6]
+
+	if val == "d" {
+		return true
+	}
+	return false
+}
+
+func (si *SymbolInfo) IsSection(section string) bool {
+	val := (*si).Section
+	return strings.HasPrefix(val, section)
+}
+
+func (si *SymbolInfo) IsFile() bool {
+	val := (*si).Code[6:7]
+
+	if val == "f" {
+		return true
+	}
+	return false
+}
+
+func (si *SymbolInfo) IsFunction() bool {
+	val := (*si).Code[6:7]
+
+	if val == "F" {
+		return true
+	}
+	return false
+}
+
+func (s *SymbolMap) FilterPkg(pname string) *SymbolMap {
+	sm := NewSymbolMap()
+	for _, info1 := range *s {
+		if pname != "" && pname == info1.Bpkg {
+			sm.Add(info1)
+		}
+	}
+	return sm
+}
+
+func (s *SymbolMap) String(name string) string {
+	// To store the keys in slice in sorted order
+	var keys []string
+	for k := range *s {
+		keys = append(keys, k)
+	}
+	sort.Strings(keys)
+
+	// To perform the opertion you want
+	out := fmt.Sprintf("Dumping symbols in file: %s\n", name)
+	for _, k := range keys {
+		info1 := (*s)[k]
+		out += info1.Sprintf()
+	}
+	return out
+}
+
+func (s *SymbolMap) Dump(name string) {
+
+	// To store the keys in slice in sorted order
+	var keys []string
+	for k := range *s {
+		keys = append(keys, k)
+	}
+	sort.Strings(keys)
+
+	// To perform the opertion you want
+	fmt.Printf("Dumping symbols in file: %s\n", name)
+	for _, k := range keys {
+		info1 := (*s)[k]
+		info1.Dump()
+	}
+}
+
+// Merge - merges given maps into 1 map
+// values will be overridden by last matching key - value
+func (s1 *SymbolMap) Merge(s2 *SymbolMap) (*SymbolMap, error) {
+
+	for k, v := range *s2 {
+
+		if val, ok := (*s1)[k]; ok {
+			/* We already have this in the MAP */
+			if val.IsWeak() && !v.IsWeak() {
+				(*s1)[k] = v
+			} else if v.IsWeak() && !val.IsWeak() {
+				/* nothing to do here as this is OK not to replace */
+			} else if v.IsLocal() && val.IsLocal() {
+				/* two locals that must conflict with name */
+				/* have to have separate instances of these */
+				util.StatusMessage(util.VERBOSITY_VERBOSE,
+					"Local Symbol Conflict: %s from packages %s and %s \n",
+					v.Name, v.Bpkg, val.Bpkg)
+				(*s2).Remove(k)
+			} else {
+				util.StatusMessage(util.VERBOSITY_QUIET,
+					"Global Symbol Conflict: %s from packages %s and %s \n",
+					v.Name, v.Bpkg, val.Bpkg)
+				return nil, util.NewNewtError("Global Symbol Conflict")
+			}
+		} else {
+			(*s1)[k] = v
+		}
+
+	}
+	return s1, nil
+}
+
+func (s *SymbolMap) Remove(name string) {
+	delete(*s, name)
+}
+
+func (s *SymbolMap) RemoveMap(subset *SymbolMap) {
+	for name, _ := range *subset {
+		(*s).Remove(name)
+	}
+}
+
+/* Returns true if the symbol is present in the symbol map */
+func (s *SymbolMap) Find(name string) (*SymbolInfo, bool) {
+	val, ok := (*s)[name]
+	return &val, ok
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newt/target/target.go
----------------------------------------------------------------------
diff --git a/newt/target/target.go b/newt/target/target.go
index 7d8b6f8..eadfa60 100644
--- a/newt/target/target.go
+++ b/newt/target/target.go
@@ -42,6 +42,7 @@ type Target struct {
 
 	BspName      string
 	AppName      string
+	LoaderName   string
 	BuildProfile string
 
 	// target.yml configuration structure
@@ -83,6 +84,7 @@ func (target *Target) Load(basePkg *pkg.LocalPackage) error {
 
 	target.BspName = target.Vars["target.bsp"]
 	target.AppName = target.Vars["target.app"]
+	target.LoaderName = target.Vars["target.loader"]
 	target.BuildProfile = target.Vars["target.build_profile"]
 
 	if target.BuildProfile == "" {
@@ -126,6 +128,7 @@ func (target *Target) Validate(appRequired bool) error {
 			return util.FmtNewtError("Could not resolve app package: %s",
 				target.AppName)
 		}
+
 		if app.Type() != pkg.PACKAGE_TYPE_APP {
 			return util.FmtNewtError("target.app package (%s) is not of "+
 				"type app; type is: %s\n", app.Name(),
@@ -181,6 +184,10 @@ func (target *Target) App() *pkg.LocalPackage {
 	return target.resolvePackageName(target.AppName)
 }
 
+func (target *Target) Loader() *pkg.LocalPackage {
+	return target.resolvePackageName(target.LoaderName)
+}
+
 func (target *Target) Bsp() *pkg.LocalPackage {
 	return target.resolvePackageName(target.BspName)
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newt/toolchain/compiler.go
----------------------------------------------------------------------
diff --git a/newt/toolchain/compiler.go b/newt/toolchain/compiler.go
index e44ff51..b032d97 100644
--- a/newt/toolchain/compiler.go
+++ b/newt/toolchain/compiler.go
@@ -27,12 +27,14 @@ import (
 	"regexp"
 	"runtime"
 	"sort"
+	"strconv"
 	"strings"
 	"time"
 
 	log "github.com/Sirupsen/logrus"
 
 	"mynewt.apache.org/newt/newt/newtutil"
+	"mynewt.apache.org/newt/newt/symbol"
 	"mynewt.apache.org/newt/util"
 	"mynewt.apache.org/newt/viper"
 )
@@ -631,19 +633,33 @@ func (c *Compiler) getObjFiles(baseObjFiles []string) string {
 //
 // @return                      (success) The command string.
 func (c *Compiler) CompileBinaryCmd(dstFile string, options map[string]bool,
-	objFiles []string) string {
+	objFiles []string, keepSymbols []string, elfLib string) string {
 
 	objList := c.getObjFiles(util.UniqueStrings(objFiles))
 
 	cmd := c.ccPath + " -o " + dstFile + " " + " " + c.cflagsString()
+
+	if elfLib != "" {
+		cmd += " -Wl,--just-symbols=" + elfLib
+	}
+
 	if c.ldResolveCircularDeps {
 		cmd += " -Wl,--start-group " + objList + " -Wl,--end-group "
 	} else {
 		cmd += " " + objList
 	}
 
+	if keepSymbols != nil {
+		for _, name := range keepSymbols {
+			cmd += " -Wl,--undefined=" + name
+		}
+	}
+
 	cmd += " " + c.lflagsString()
 
+	/* so we don't get multiple global definitions of the same vartiable */
+	//cmd += " -Wl,--warn-common "
+
 	if c.LinkerScript != "" {
 		cmd += " -T " + c.LinkerScript
 	}
@@ -662,20 +678,23 @@ func (c *Compiler) CompileBinaryCmd(dstFile string, options map[string]bool,
 //                                  gets generated.
 // @param objFiles              An array of the source .o and .a filenames.
 func (c *Compiler) CompileBinary(dstFile string, options map[string]bool,
-	objFiles []string) error {
+	objFiles []string, keepSymbols []string, elfLib string) error {
 
 	// Make sure the compiler package info is added to the global set.
 	c.ensureLclInfoAdded()
 
 	objList := c.getObjFiles(util.UniqueStrings(objFiles))
 
-	util.StatusMessage(util.VERBOSITY_DEFAULT, "Linking %s\n",
-		path.Base(dstFile))
-	util.StatusMessage(util.VERBOSITY_VERBOSE,
-		"Linking %s with input files %s\n",
+	util.StatusMessage(util.VERBOSITY_DEFAULT, "Linking %s\n", dstFile)
+	util.StatusMessage(util.VERBOSITY_VERBOSE, "Linking %s with input files %s\n",
 		dstFile, objList)
 
-	cmd := c.CompileBinaryCmd(dstFile, options, objFiles)
+	if elfLib != "" {
+		util.StatusMessage(util.VERBOSITY_VERBOSE, "Linking %s with rom image %s\n",
+			dstFile, elfLib)
+	}
+
+	cmd := c.CompileBinaryCmd(dstFile, options, objFiles, keepSymbols, elfLib)
 	_, err := util.ShellCommand(cmd)
 	if err != nil {
 		return err
@@ -764,14 +783,16 @@ func (c *Compiler) PrintSize(elfFilename string) (string, error) {
 // @param options               Some build options specifying how the elf file
 //                                  gets generated.
 // @param objFiles              An array of the source .o and .a filenames.
-func (c *Compiler) CompileElf(binFile string, objFiles []string) error {
+func (c *Compiler) CompileElf(binFile string, objFiles []string,
+	keepSymbols []string, elfLib string) error {
 	options := map[string]bool{"mapFile": c.ldMapFile,
 		"listFile": true, "binFile": c.ldBinFile}
 
 	// Make sure the compiler package info is added to the global set.
 	c.ensureLclInfoAdded()
 
-	linkRequired, err := c.depTracker.LinkRequired(binFile, options, objFiles)
+	linkRequired, err := c.depTracker.LinkRequired(binFile, options,
+		objFiles, keepSymbols, elfLib)
 	if err != nil {
 		return err
 	}
@@ -779,7 +800,7 @@ func (c *Compiler) CompileElf(binFile string, objFiles []string) error {
 		if err := os.MkdirAll(filepath.Dir(binFile), 0755); err != nil {
 			return util.NewNewtError(err.Error())
 		}
-		err := c.CompileBinary(binFile, options, objFiles)
+		err := c.CompileBinary(binFile, options, objFiles, keepSymbols, elfLib)
 		if err != nil {
 			return err
 		}
@@ -793,6 +814,35 @@ func (c *Compiler) CompileElf(binFile string, objFiles []string) error {
 	return nil
 }
 
+func (c *Compiler) RenameSymbolsCmd(sm *symbol.SymbolMap, libraryFile string, ext string) string {
+	val := c.ocPath
+
+	for s, _ := range *sm {
+		val += " --redefine-sym " + s + "=" + s + ext
+	}
+
+	val += " " + libraryFile
+	return val
+}
+
+func (c *Compiler) ParseLibraryCmd(libraryFile string) string {
+	val := c.odPath + " -t " + libraryFile
+	return val
+}
+
+func (c *Compiler) CopySymbolsCmd(infile string, outfile string, sm *symbol.SymbolMap) string {
+
+	val := c.ocPath + " -S "
+
+	for symbol, _ := range *sm {
+		val += " -K " + symbol
+	}
+
+	val += " " + infile
+	val += " " + outfile
+	return val
+}
+
 // Calculates the command-line invocation necessary to archive the specified
 // static library.
 //
@@ -807,6 +857,54 @@ func (c *Compiler) CompileArchiveCmd(archiveFile string,
 	return c.arPath + " rcs " + archiveFile + " " + objList
 }
 
+func linkerScriptFileName(archiveFile string) string {
+	ar_script_name := strings.TrimSuffix(archiveFile, filepath.Ext(archiveFile)) + "_ar.mri"
+	return ar_script_name
+}
+
+/* this create a new library combining all of the other libraries */
+func createSplitArchiveLinkerFile(archiveFile string,
+	archFiles []string) error {
+
+	/* create a name for this script */
+	ar_script_name := linkerScriptFileName(archiveFile)
+
+	// open the file and write out the script
+	f, err := os.OpenFile(ar_script_name, os.O_CREATE|os.O_WRONLY, 0666)
+	if err != nil {
+		return util.NewNewtError(err.Error())
+	}
+	defer f.Close()
+
+	if _, err := f.WriteString("CREATE " + archiveFile + "\n"); err != nil {
+		return util.NewNewtError(err.Error())
+	}
+
+	for _, arch := range archFiles {
+		if _, err := f.WriteString("ADDLIB " + arch + "\n"); err != nil {
+			return util.NewNewtError(err.Error())
+		}
+	}
+
+	if _, err := f.WriteString("SAVE\n"); err != nil {
+		return util.NewNewtError(err.Error())
+	}
+
+	if _, err := f.WriteString("END\n"); err != nil {
+		return util.NewNewtError(err.Error())
+	}
+
+	return nil
+}
+
+// calculates the command-line invocation necessary to build a split all
+// archive from the collection of archive files
+func (c *Compiler) BuildSplitArchiveCmd(archiveFile string) string {
+
+	str := c.arPath + " -M < " + linkerScriptFileName(archiveFile)
+	return str
+}
+
 // Archives the specified static library.
 //
 // @param archiveFile           The filename of the library to archive.
@@ -854,3 +952,102 @@ func (c *Compiler) CompileArchive(archiveFile string) error {
 
 	return nil
 }
+
+func getParseRexeg() (error, *regexp.Regexp) {
+	r, err := regexp.Compile("^([0-9A-Fa-f]+)[\t ]+([lgu! ][w ][C ][W ][Ii ][Dd ][FfO ])[\t ]+([^\t\n\f\r ]+)[\t ]+([0-9a-fA-F]+)[\t ]([^\t\n\f\r ]+)")
+
+	if err != nil {
+		return err, nil
+	}
+
+	return nil, r
+}
+
+/* This is a tricky thing to parse. Right now, I keep all the
+ * flags together and just store the offset, size, name and flags.
+* 00012970 l       .bss	00000000 _end
+* 00011c60 l       .init_array	00000000 __init_array_start
+* 00011c60 l       .init_array	00000000 __preinit_array_start
+* 000084b0 g     F .text	00000034 os_arch_start
+* 00000000 g       .debug_aranges	00000000 __HeapBase
+* 00011c88 g     O .data	00000008 g_os_task_list
+* 000082cc g     F .text	0000004c os_idle_task
+* 000094e0 g     F .text	0000002e .hidden __gnu_uldivmod_helper
+* 00000000 g       .svc_table	00000000 SVC_Count
+* 000125e4 g     O .bss	00000004 g_console_is_init
+* 00009514 g     F .text	0000029c .hidden __divdi3
+* 000085a8 g     F .text	00000054 os_eventq_put
+*/
+func ParseObjectLine(line string, r *regexp.Regexp) (error, *symbol.SymbolInfo) {
+
+	answer := r.FindAllStringSubmatch(line, 11)
+
+	if len(answer) == 0 {
+		return nil, nil
+	}
+
+	data := answer[0]
+
+	if len(data) != 6 {
+		util.StatusMessage(util.VERBOSITY_DEFAULT,
+			"Not enough content in object file line --- %s", line)
+		return nil, nil
+	}
+
+	si := symbol.NewSymbolInfo()
+
+	si.Name = data[5]
+
+	v, err := strconv.ParseUint(data[1], 16, 32)
+
+	if err != nil {
+		util.StatusMessage(util.VERBOSITY_DEFAULT,
+			"Could not convert location from object file line --- %s", line)
+		return nil, nil
+	}
+
+	si.Loc = int(v)
+
+	v, err = strconv.ParseUint(data[4], 16, 32)
+
+	if err != nil {
+		util.StatusMessage(util.VERBOSITY_DEFAULT,
+			"Could not convert size form object file line --- %s", line)
+		return nil, nil
+	}
+
+	si.Size = int(v)
+	si.Code = data[2]
+	si.Section = data[3]
+
+	return nil, si
+}
+
+func (c *Compiler) RenameSymbols(sm *symbol.SymbolMap, libraryFile string, ext string) error {
+
+	cmd := c.RenameSymbolsCmd(sm, libraryFile, ext)
+
+	_, err := util.ShellCommand(cmd)
+
+	return err
+}
+
+func (c *Compiler) ParseLibrary(libraryFile string) (error, []byte) {
+	cmd := c.ParseLibraryCmd(libraryFile)
+
+	out, err := util.ShellCommand(cmd)
+	if err != nil {
+		return err, nil
+	}
+	return err, out
+}
+
+func (c *Compiler) CopySymbols(infile string, outfile string, sm *symbol.SymbolMap) error {
+	cmd := c.CopySymbolsCmd(infile, outfile, sm)
+
+	_, err := util.ShellCommand(cmd)
+	if err != nil {
+		return err
+	}
+	return err
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newt/toolchain/deps.go
----------------------------------------------------------------------
diff --git a/newt/toolchain/deps.go b/newt/toolchain/deps.go
index 7b340d3..a50e265 100644
--- a/newt/toolchain/deps.go
+++ b/newt/toolchain/deps.go
@@ -270,6 +270,39 @@ func (tracker *DepTracker) ArchiveRequired(archiveFile string,
 	return false, nil
 }
 
+func (tracker *DepTracker) TrimmedArchiveRequired(dstFile string,
+	srcFile string, elfLib string) (bool, error) {
+
+	// If the .A file doesn't exist or is older than the input file, a rebuild
+	// is required.
+	dstModTime, err := util.FileModificationTime(dstFile)
+	if err != nil {
+		return false, err
+	}
+
+	// If the elf file doesn't exist or is older than any input file,
+	// a rebuild is required.
+	if elfLib != "" {
+		elfDstModTime, err := util.FileModificationTime(elfLib)
+		if err != nil {
+			return false, err
+		}
+
+		if elfDstModTime.After(dstModTime) {
+			return true, nil
+		}
+	}
+	objModTime, err := util.FileModificationTime(srcFile)
+	if err != nil {
+		return false, err
+	}
+
+	if objModTime.After(dstModTime) {
+		return true, nil
+	}
+	return false, nil
+}
+
 // Determines if the specified elf file needs to be linked.  Linking is
 // necessary if the elf file does not exist or has an older modification time
 // than any source object or library file.
@@ -281,11 +314,12 @@ func (tracker *DepTracker) ArchiveRequired(archiveFile string,
 //     * One or more source object files has a newer modification time than the
 //       library file.
 func (tracker *DepTracker) LinkRequired(dstFile string,
-	options map[string]bool, objFiles []string) (bool, error) {
+	options map[string]bool, objFiles []string,
+	keepSymbols []string, elfLib string) (bool, error) {
 
 	// If the elf file was previously built with a different set of options, a
 	// rebuild is required.
-	cmd := tracker.compiler.CompileBinaryCmd(dstFile, options, objFiles)
+	cmd := tracker.compiler.CompileBinaryCmd(dstFile, options, objFiles, keepSymbols, elfLib)
 	if commandHasChanged(dstFile, cmd) {
 		util.StatusMessage(util.VERBOSITY_VERBOSE, "%s - link required; "+
 			"different command\n", dstFile)
@@ -299,6 +333,20 @@ func (tracker *DepTracker) LinkRequired(dstFile string,
 		return false, err
 	}
 
+	// If the elf file doesn't exist or is older than any input file, a rebuild
+	// is required.
+	if elfLib != "" {
+		elfDstModTime, err := util.FileModificationTime(elfLib)
+		if err != nil {
+			return false, err
+		}
+		if elfDstModTime.After(dstModTime) {
+			util.StatusMessage(util.VERBOSITY_VERBOSE, "%s - link required; "+
+				"old elf file\n", elfLib)
+			return true, nil
+		}
+	}
+
 	// Check timestamp of each .o file in the project.
 	if tracker.MostRecent.After(dstModTime) {
 		util.StatusMessage(util.VERBOSITY_VERBOSE, "%s - link required; "+
@@ -325,3 +373,40 @@ func (tracker *DepTracker) LinkRequired(dstFile string,
 
 	return false, nil
 }
+
+/* Building a ROM elf is used for shared application linking.
+ * A ROM elf requires a rebuild if any of archives (.a files) are newer
+ * than the rom elf, or if the elf file is newer than the rom_elf */
+func (tracker *DepTracker) RomElfBuldRequired(dstFile string, elfFile string,
+	archFiles []string) (bool, error) {
+
+	// If the rom_elf file doesn't exist or is older than any input file, a rebuild
+	// is required.
+	dstModTime, err := util.FileModificationTime(dstFile)
+	if err != nil {
+		return false, err
+	}
+
+	// If the elf file doesn't exist or is older than any input file, a rebuild
+	// is required.
+	elfDstModTime, err := util.FileModificationTime(elfFile)
+	if err != nil {
+		return false, err
+	}
+
+	if elfDstModTime.After(dstModTime) {
+		return true, nil
+	}
+
+	for _, arch := range archFiles {
+		objModTime, err := util.FileModificationTime(arch)
+		if err != nil {
+			return false, err
+		}
+
+		if objModTime.After(dstModTime) {
+			return true, nil
+		}
+	}
+	return false, nil
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newtmgr/cli/commands.go
----------------------------------------------------------------------
diff --git a/newtmgr/cli/commands.go b/newtmgr/cli/commands.go
index d40dc4d..5e00eb5 100644
--- a/newtmgr/cli/commands.go
+++ b/newtmgr/cli/commands.go
@@ -55,6 +55,7 @@ func Commands() *cobra.Command {
 	nmCmd.AddCommand(connProfileCmd())
 	nmCmd.AddCommand(echoCmd())
 	nmCmd.AddCommand(imageCmd())
+	nmCmd.AddCommand(splitCmd())
 	nmCmd.AddCommand(statsCmd())
 	nmCmd.AddCommand(taskStatsCmd())
 	nmCmd.AddCommand(mempoolStatsCmd())

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newtmgr/cli/image.go
----------------------------------------------------------------------
diff --git a/newtmgr/cli/image.go b/newtmgr/cli/image.go
index 69fa922..6ddd304 100644
--- a/newtmgr/cli/image.go
+++ b/newtmgr/cli/image.go
@@ -20,6 +20,8 @@
 package cli
 
 import (
+	"encoding/base64"
+	"encoding/hex"
 	"errors"
 	"fmt"
 	"io"
@@ -124,8 +126,21 @@ func imageListCmd2(cmd *cobra.Command, args []string) {
 		nmUsage(cmd, err)
 	}
 	fmt.Println("Images:")
-	for hash, ver := range iRsp.Images {
-		fmt.Printf(" %8s %s\n", ver, hash)
+	for _, img := range iRsp.Images {
+
+		fmt.Printf(" slot=%d\n", img.Slot)
+		fmt.Printf("    version=%s\n", img.Version)
+		fmt.Printf("    bootable=%v\n", img.Bootable)
+		if img.Hash == "" {
+			fmt.Printf("    hash=Unavailable\n")
+		} else {
+			dec, err := base64.StdEncoding.DecodeString(img.Hash)
+			if err != nil {
+				fmt.Printf("    hash=Unable to Decode")
+			} else {
+				fmt.Printf("    hash=%s\n", hex.EncodeToString(dec[:]))
+			}
+		}
 	}
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newtmgr/cli/split.go
----------------------------------------------------------------------
diff --git a/newtmgr/cli/split.go b/newtmgr/cli/split.go
new file mode 100644
index 0000000..a8c77a5
--- /dev/null
+++ b/newtmgr/cli/split.go
@@ -0,0 +1,102 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package cli
+
+import (
+	"fmt"
+
+	"github.com/spf13/cobra"
+	"mynewt.apache.org/newt/newtmgr/protocol"
+	"mynewt.apache.org/newt/util"
+)
+
+func splitStatusCmd(cmd *cobra.Command, args []string) {
+	runner, err := getTargetCmdRunner()
+	if err != nil {
+		nmUsage(cmd, err)
+	}
+
+	split, err := protocol.NewSplit()
+	if err != nil {
+		nmUsage(cmd, err)
+	}
+	var nmr *protocol.NmgrReq
+	if len(args) == 0 {
+		nmr, err = split.EncoderReadRequest()
+	} else if len(args) == 1 {
+		b, err := protocol.ParseSplitMode(args[0])
+
+		if err != nil {
+			nmUsage(cmd, util.NewNewtError("Invalid Boolean Argument"))
+		}
+		split.Split = b
+		nmr, err = split.EncoderWriteRequest()
+	} else {
+		nmUsage(cmd, nil)
+		return
+	}
+
+	if err := runner.WriteReq(nmr); err != nil {
+		nmUsage(cmd, err)
+	}
+
+	rsp, err := runner.ReadResp()
+	if err != nil {
+		nmUsage(cmd, err)
+	}
+
+	srsp, err := protocol.DecodeSplitReadResponse(rsp.Data)
+	if err != nil {
+		nmUsage(cmd, err)
+	}
+
+	if len(args) == 0 {
+		fmt.Printf("Split value is %s\n", srsp.Split)
+		fmt.Printf("Split status is %s\n", srsp.Status)
+
+	}
+	if srsp.ReturnCode != 0 {
+		fmt.Printf("Error executing split command: rc=%d\n", srsp.ReturnCode)
+	}
+}
+
+func splitCmd() *cobra.Command {
+	splitImgCmd := &cobra.Command{
+		Use:   "split",
+		Short: "Manage split images on remote instance",
+		Run: func(cmd *cobra.Command, args []string) {
+			cmd.HelpFunc()(cmd, args)
+		},
+	}
+
+	splitEx := "  newtmgr -c olimex image split 1\n"
+	splitEx += "  newtmgr -c olimex image split 0\n"
+	splitEx += "  newtmgr -c olimex image split\n"
+
+	splitStatusCmd := &cobra.Command{
+		Use:     "status",
+		Short:   "Erase core on target",
+		Example: splitEx,
+		Run:     splitStatusCmd,
+	}
+	splitImgCmd.AddCommand(splitStatusCmd)
+
+	return splitImgCmd
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newtmgr/protocol/defs.go
----------------------------------------------------------------------
diff --git a/newtmgr/protocol/defs.go b/newtmgr/protocol/defs.go
index 3fb92dc..6724661 100644
--- a/newtmgr/protocol/defs.go
+++ b/newtmgr/protocol/defs.go
@@ -25,6 +25,7 @@ const (
 	NMGR_GROUP_ID_CONFIG  = 3
 	NMGR_GROUP_ID_LOGS    = 4
 	NMGR_GROUP_ID_CRASH   = 5
+	NMGR_GROUP_ID_SPLIT   = 6
 	NMGR_GROUP_ID_PERUSER = 64
 )
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/8c06bb68/newtmgr/protocol/imagelist2.go
----------------------------------------------------------------------
diff --git a/newtmgr/protocol/imagelist2.go b/newtmgr/protocol/imagelist2.go
index 173919d..159dd1b 100644
--- a/newtmgr/protocol/imagelist2.go
+++ b/newtmgr/protocol/imagelist2.go
@@ -26,13 +26,19 @@ import (
 	"mynewt.apache.org/newt/util"
 )
 
+type Image2 struct {
+	Slot     int    `json:"slot"`
+	Version  string `json:"version"`
+	Hash     string `json:"hash"`
+	Bootable bool   `json:"bootable"`
+}
+
 type ImageList2 struct {
-	Images map[string]string
+	Images []Image2 `json:"images"`
 }
 
 func NewImageList2() (*ImageList2, error) {
 	s := &ImageList2{}
-	s.Images = map[string]string{}
 	return s, nil
 }
 
@@ -52,29 +58,13 @@ func (i *ImageList2) EncodeWriteRequest() (*NmgrReq, error) {
 }
 
 func DecodeImageListResponse2(data []byte) (*ImageList2, error) {
-	type ImageInfoJson map[string]string
-
-	type ImageListJson struct {
-		Images []ImageInfoJson
-	}
 
-	list := &ImageListJson{}
+	list2 := &ImageList2{}
 
-	err := json.Unmarshal(data, &list)
+	err := json.Unmarshal(data, &list2)
 	if err != nil {
 		return nil, util.NewNewtError(fmt.Sprintf("Invalid incoming json: %s",
 			err.Error()))
 	}
-
-	list2, _ := NewImageList2()
-	for _, info := range list.Images {
-		for hash, ver := range info {
-			hash, err := HashDecode(hash)
-			if err != nil {
-				return nil, err
-			}
-			list2.Images[hash] = ver
-		}
-	}
 	return list2, nil
 }