mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-05-20 13:48:02 -04:00
objdump -S file parsed and used to annotate illegal memory accesses by the ARM
This commit is contained in:
parent
b922589c5c
commit
80308af963
|
@ -20,7 +20,9 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/jetsetilly/gopher2600/curated"
|
||||
"github.com/jetsetilly/gopher2600/debugger/terminal/colorterm/easyterm/ansi"
|
||||
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge/harmony/arm7tdmi/mapfile"
|
||||
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge/harmony/arm7tdmi/objdump"
|
||||
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge/mapper"
|
||||
"github.com/jetsetilly/gopher2600/hardware/preferences"
|
||||
"github.com/jetsetilly/gopher2600/logger"
|
||||
|
@ -69,6 +71,9 @@ type ARM struct {
|
|||
registers [rCount]uint32
|
||||
status status
|
||||
|
||||
// the PC of the instruction being executed
|
||||
executingPC uint32
|
||||
|
||||
// "peripherals" connected to the variety of ARM7TDMI-S used in the Harmony
|
||||
// cartridge.
|
||||
timer timer
|
||||
|
@ -175,7 +180,10 @@ type ARM struct {
|
|||
Ncycle func(bus busAccess, addr uint32)
|
||||
|
||||
// mapfile for binary (if available)
|
||||
mapFile *mapfile.Mapfile
|
||||
mapfile *mapfile.Mapfile
|
||||
|
||||
// obj dump for binary (if available)
|
||||
objdump *objdump.ObjDump
|
||||
|
||||
// illegal accesses already encountered. duplicate accesses will not be logged.
|
||||
illegalAccesses map[string]bool
|
||||
|
@ -214,7 +222,12 @@ func NewARM(mmap MemoryMap, prefs *preferences.ARMPreferences, mem SharedMemory,
|
|||
logger.Logf("ARM7", "reset: %s", err.Error())
|
||||
}
|
||||
|
||||
arm.mapFile, err = mapfile.NewMapFile(pathToROM)
|
||||
arm.mapfile, err = mapfile.NewMapFile(pathToROM)
|
||||
if err != nil {
|
||||
logger.Logf("ARM7", err.Error())
|
||||
}
|
||||
|
||||
arm.objdump, err = objdump.NewObjDump(pathToROM)
|
||||
if err != nil {
|
||||
logger.Logf("ARM7", err.Error())
|
||||
}
|
||||
|
@ -307,6 +320,21 @@ func (arm *ARM) Step(vcsClock float32) {
|
|||
arm.timer.stepFromVCS(Clk, vcsClock)
|
||||
}
|
||||
|
||||
func (arm *ARM) lookupSource() {
|
||||
if arm.mapfile != nil {
|
||||
programLabel := arm.mapfile.FindProgramAccess(arm.executingPC)
|
||||
if programLabel != "" {
|
||||
logger.Logf("ARM7", "mapfile: access in %s%s()%s", ansi.DimPens["cyan"], programLabel, ansi.NormalPen)
|
||||
}
|
||||
}
|
||||
if arm.objdump != nil {
|
||||
src := arm.objdump.FindProgramAccess(arm.executingPC)
|
||||
if src != "" {
|
||||
logger.Logf("ARM7", "objdump:\n%s%s%s", ansi.DimPens["cyan"], src, ansi.NormalPen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (arm *ARM) read8bit(addr uint32) uint8 {
|
||||
var mem *[]uint8
|
||||
|
||||
|
@ -321,17 +349,11 @@ func (arm *ARM) read8bit(addr uint32) uint8 {
|
|||
}
|
||||
arm.memoryError = arm.abortOnIllegalMem
|
||||
|
||||
accessKey := fmt.Sprintf("%08x%08x", addr, arm.registers[rPC])
|
||||
accessKey := fmt.Sprintf("%08x%08x", addr, arm.executingPC)
|
||||
if _, ok := arm.illegalAccesses[accessKey]; !ok {
|
||||
arm.illegalAccesses[accessKey] = true
|
||||
|
||||
logger.Logf("ARM7", "read8bit: unrecognised address %08x (PC: %08x)", addr, arm.registers[rPC])
|
||||
if arm.mapFile != nil {
|
||||
programLabel := arm.mapFile.FindProgramAccess(arm.registers[rPC])
|
||||
if programLabel != "" {
|
||||
logger.Logf("ARM7", "mapfile: access in %s()", programLabel)
|
||||
}
|
||||
}
|
||||
logger.Logf("ARM7", "read8bit: unrecognised address %08x (PC: %08x)", addr, arm.executingPC)
|
||||
arm.lookupSource()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
@ -353,17 +375,11 @@ func (arm *ARM) write8bit(addr uint32, val uint8) {
|
|||
}
|
||||
arm.memoryError = arm.abortOnIllegalMem
|
||||
|
||||
accessKey := fmt.Sprintf("%08x%08x", addr, arm.registers[rPC])
|
||||
accessKey := fmt.Sprintf("%08x%08x", addr, arm.executingPC)
|
||||
if _, ok := arm.illegalAccesses[accessKey]; !ok {
|
||||
arm.illegalAccesses[accessKey] = true
|
||||
|
||||
logger.Logf("ARM7", "write8bit: unrecognised address %08x (PC: %08x)", addr, arm.registers[rPC])
|
||||
if arm.mapFile != nil {
|
||||
programLabel := arm.mapFile.FindProgramAccess(arm.registers[rPC])
|
||||
if programLabel != "" {
|
||||
logger.Logf("ARM7", "mapfile: access in %s()", programLabel)
|
||||
}
|
||||
}
|
||||
logger.Logf("ARM7", "write8bit: unrecognised address %08x (PC: %08x)", addr, arm.executingPC)
|
||||
arm.lookupSource()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -390,17 +406,11 @@ func (arm *ARM) read16bit(addr uint32) uint16 {
|
|||
}
|
||||
arm.memoryError = arm.abortOnIllegalMem
|
||||
|
||||
accessKey := fmt.Sprintf("%08x%08x", addr, arm.registers[rPC])
|
||||
accessKey := fmt.Sprintf("%08x%08x", addr, arm.executingPC)
|
||||
if _, ok := arm.illegalAccesses[accessKey]; !ok {
|
||||
arm.illegalAccesses[accessKey] = true
|
||||
|
||||
logger.Logf("ARM7", "read16bit: unrecognised address %08x (PC: %08x)", addr, arm.registers[rPC])
|
||||
if arm.mapFile != nil {
|
||||
programLabel := arm.mapFile.FindProgramAccess(arm.registers[rPC])
|
||||
if programLabel != "" {
|
||||
logger.Logf("ARM7", "mapfile: access in %s()", programLabel)
|
||||
}
|
||||
}
|
||||
logger.Logf("ARM7", "read16bit: unrecognised address %08x (PC: %08x)", addr, arm.executingPC)
|
||||
arm.lookupSource()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
@ -427,17 +437,11 @@ func (arm *ARM) write16bit(addr uint32, val uint16) {
|
|||
}
|
||||
arm.memoryError = arm.abortOnIllegalMem
|
||||
|
||||
accessKey := fmt.Sprintf("%08x%08x", addr, arm.registers[rPC])
|
||||
accessKey := fmt.Sprintf("%08x%08x", addr, arm.executingPC)
|
||||
if _, ok := arm.illegalAccesses[accessKey]; !ok {
|
||||
arm.illegalAccesses[accessKey] = true
|
||||
|
||||
logger.Logf("ARM7", "write16bit: unrecognised address %08x (PC: %08x)", addr, arm.registers[rPC])
|
||||
if arm.mapFile != nil {
|
||||
programLabel := arm.mapFile.FindProgramAccess(arm.registers[rPC])
|
||||
if programLabel != "" {
|
||||
logger.Logf("ARM7", "mapfile: access in %s()", programLabel)
|
||||
}
|
||||
}
|
||||
logger.Logf("ARM7", "write16bit: unrecognised address %08x (PC: %08x)", addr, arm.executingPC)
|
||||
arm.lookupSource()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -465,17 +469,11 @@ func (arm *ARM) read32bit(addr uint32) uint32 {
|
|||
}
|
||||
arm.memoryError = arm.abortOnIllegalMem
|
||||
|
||||
accessKey := fmt.Sprintf("%08x%08x", addr, arm.registers[rPC])
|
||||
accessKey := fmt.Sprintf("%08x%08x", addr, arm.executingPC)
|
||||
if _, ok := arm.illegalAccesses[accessKey]; !ok {
|
||||
arm.illegalAccesses[accessKey] = true
|
||||
|
||||
logger.Logf("ARM7", "read32bit: unrecognised address %08x (PC: %08x)", addr, arm.registers[rPC])
|
||||
if arm.mapFile != nil {
|
||||
programLabel := arm.mapFile.FindProgramAccess(arm.registers[rPC])
|
||||
if programLabel != "" {
|
||||
logger.Logf("ARM7", "mapfile: access in %s()", programLabel)
|
||||
}
|
||||
}
|
||||
logger.Logf("ARM7", "read32bit: unrecognised address %08x (PC: %08x)", addr, arm.executingPC)
|
||||
arm.lookupSource()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
@ -502,17 +500,11 @@ func (arm *ARM) write32bit(addr uint32, val uint32) {
|
|||
}
|
||||
arm.memoryError = arm.abortOnIllegalMem
|
||||
|
||||
accessKey := fmt.Sprintf("%08x%08x", addr, arm.registers[rPC])
|
||||
accessKey := fmt.Sprintf("%08x%08x", addr, arm.executingPC)
|
||||
if _, ok := arm.illegalAccesses[accessKey]; !ok {
|
||||
arm.illegalAccesses[accessKey] = true
|
||||
|
||||
logger.Logf("ARM7", "write32bit: unrecognised address %08x (PC: %08x)", addr, arm.registers[rPC])
|
||||
if arm.mapFile != nil {
|
||||
programLabel := arm.mapFile.FindProgramAccess(arm.registers[rPC])
|
||||
if programLabel != "" {
|
||||
logger.Logf("ARM7", "mapfile: access in %s()", programLabel)
|
||||
}
|
||||
}
|
||||
logger.Logf("ARM7", "write32bit: unrecognised address %08x (PC: %08x)", addr, arm.executingPC)
|
||||
arm.lookupSource()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -584,7 +576,7 @@ func (arm *ARM) Run(mamcr uint32) (uint32, float32, error) {
|
|||
// "The program counter points to the instruction being fetched rather than to the instruction
|
||||
// being executed. This is important because it means that the Program Counter (PC)
|
||||
// value used in an executing instruction is always two instructions ahead of the address."
|
||||
executingPC := arm.registers[rPC] - 2
|
||||
arm.executingPC = arm.registers[rPC] - 2
|
||||
|
||||
// set disasmLevel for the next instruction
|
||||
if arm.disasm == nil {
|
||||
|
@ -594,7 +586,7 @@ func (arm *ARM) Run(mamcr uint32) (uint32, float32, error) {
|
|||
arm.disasmLevel = disasmFull
|
||||
|
||||
// check cache for existing disasm entry
|
||||
if e, ok := arm.disasmCache[executingPC]; ok {
|
||||
if e, ok := arm.disasmCache[arm.executingPC]; ok {
|
||||
// use cached entry
|
||||
arm.disasmEntry = e
|
||||
|
||||
|
@ -610,7 +602,7 @@ func (arm *ARM) Run(mamcr uint32) (uint32, float32, error) {
|
|||
// in an initial state
|
||||
if arm.disasmLevel == disasmFull {
|
||||
arm.disasmEntry.Location = ""
|
||||
arm.disasmEntry.Address = fmt.Sprintf("%08x", executingPC)
|
||||
arm.disasmEntry.Address = fmt.Sprintf("%08x", arm.executingPC)
|
||||
arm.disasmEntry.Operator = ""
|
||||
arm.disasmEntry.Operand = ""
|
||||
arm.disasmEntry.Cycles = 0.0
|
||||
|
@ -620,22 +612,22 @@ func (arm *ARM) Run(mamcr uint32) (uint32, float32, error) {
|
|||
}
|
||||
|
||||
// check program counter
|
||||
memIdx := executingPC - arm.programMemoryOffset
|
||||
memIdx := arm.executingPC - arm.programMemoryOffset
|
||||
if memIdx+1 >= uint32(arm.programMemoryLen) {
|
||||
// program counter is out-of-range so find program memory again
|
||||
// (using the PC value)
|
||||
err = arm.findProgramMemory()
|
||||
if err != nil {
|
||||
// can't find memory so we say the ARM program has finished inadvertently
|
||||
logger.Logf("ARM7", "PC out of range (%#08x). aborting thumb program early", executingPC)
|
||||
logger.Logf("ARM7", "PC out of range (%#08x). aborting thumb program early", arm.executingPC)
|
||||
break // for loop
|
||||
}
|
||||
|
||||
// if it's still out-of-range then give up with an error
|
||||
memIdx = executingPC - arm.programMemoryOffset
|
||||
memIdx = arm.executingPC - arm.programMemoryOffset
|
||||
if memIdx+1 >= uint32(arm.programMemoryLen) {
|
||||
// can't find memory so we say the ARM program has finished inadvertently
|
||||
logger.Logf("ARM7", "PC out of range (%#08x). aborting thumb program early", executingPC)
|
||||
logger.Logf("ARM7", "PC out of range (%#08x). aborting thumb program early", arm.executingPC)
|
||||
break // for loop
|
||||
}
|
||||
}
|
||||
|
@ -781,11 +773,11 @@ func (arm *ARM) Run(mamcr uint32) (uint32, float32, error) {
|
|||
// operand fields and insert into cache
|
||||
arm.disasmEntry.Operator = fmt.Sprintf("%-4s", arm.disasmEntry.Operator)
|
||||
arm.disasmEntry.Operand = fmt.Sprintf("%-16s", arm.disasmEntry.Operand)
|
||||
arm.disasmCache[executingPC] = arm.disasmEntry
|
||||
arm.disasmCache[arm.executingPC] = arm.disasmEntry
|
||||
case disasmUpdateOnly:
|
||||
// entry is cached but notes may have changed so we recache
|
||||
// the entry
|
||||
arm.disasmCache[executingPC] = arm.disasmEntry
|
||||
arm.disasmCache[arm.executingPC] = arm.disasmEntry
|
||||
case disasmNone:
|
||||
}
|
||||
|
||||
|
|
142
hardware/memory/cartridge/harmony/arm7tdmi/objdump/objdump.go
Normal file
142
hardware/memory/cartridge/harmony/arm7tdmi/objdump/objdump.go
Normal file
|
@ -0,0 +1,142 @@
|
|||
// This file is part of Gopher2600.
|
||||
//
|
||||
// Gopher2600 is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Gopher2600 is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// package map file is a very basic parser for obj files as produced by
|
||||
// "objdump -S" on the base elf file that is used to create a cartridge binary
|
||||
//
|
||||
// FindDataAccess() and FindProgramAccess() will return best-guess labels for
|
||||
// the supplied address.
|
||||
package objdump
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jetsetilly/gopher2600/curated"
|
||||
)
|
||||
|
||||
// ObjDump contains the parsed information from the map file.
|
||||
type ObjDump struct {
|
||||
lines []string
|
||||
}
|
||||
|
||||
const mapFile = "armcode.obj"
|
||||
|
||||
func findObjDump(pathToROM string) *os.File {
|
||||
// current working directory
|
||||
sf, err := os.Open(mapFile)
|
||||
if err == nil {
|
||||
return sf
|
||||
}
|
||||
|
||||
dir := filepath.Dir(pathToROM)
|
||||
|
||||
// same directory as binary
|
||||
sf, err = os.Open(filepath.Join(dir, mapFile))
|
||||
if err == nil {
|
||||
return sf
|
||||
}
|
||||
|
||||
// main sub-directory
|
||||
sf, err = os.Open(filepath.Join(dir, "main", mapFile))
|
||||
if err == nil {
|
||||
return sf
|
||||
}
|
||||
|
||||
// main/bin sub-directory
|
||||
sf, err = os.Open(filepath.Join(dir, "main", "bin", mapFile))
|
||||
if err == nil {
|
||||
return sf
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewObjDump loads and parses a map file. Returns a new instance of Mapfile or
|
||||
// any errors.
|
||||
func NewObjDump(pathToROM string) (*ObjDump, error) {
|
||||
obj := &ObjDump{}
|
||||
|
||||
sf := findObjDump(pathToROM)
|
||||
if sf == nil {
|
||||
return nil, curated.Errorf("mapfile: gcc .map file not available (%s)", mapFile)
|
||||
}
|
||||
defer sf.Close()
|
||||
|
||||
data, err := io.ReadAll(sf)
|
||||
if err != nil {
|
||||
return nil, curated.Errorf("mapfile: processing error: %v", err)
|
||||
}
|
||||
obj.lines = strings.Split(string(data), "\n")
|
||||
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// FindDataAccess returns the data label for the supplied address. Addresses
|
||||
// will be matched exactly.
|
||||
func (obj *ObjDump) FindDataAccess(address uint32) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// FindProgramAccess returns the program (function) label for the supplied
|
||||
// address. Addresses may be in a range.
|
||||
func (obj *ObjDump) FindProgramAccess(address uint32) string {
|
||||
var start int
|
||||
var end int
|
||||
|
||||
src := false
|
||||
|
||||
for i, l := range obj.lines {
|
||||
flds := strings.Fields(l)
|
||||
if len(flds) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(flds) >= 2 {
|
||||
if flds[0][len(flds[0])-1] == ':' {
|
||||
if src {
|
||||
end = i
|
||||
src = false
|
||||
}
|
||||
|
||||
v, err := strconv.ParseInt(flds[0][:len(flds[0])-1], 16, 64)
|
||||
pc := uint32(v)
|
||||
if err == nil {
|
||||
if pc == address {
|
||||
break // for loop
|
||||
}
|
||||
}
|
||||
} else if !src {
|
||||
start = i
|
||||
src = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
s := strings.Builder{}
|
||||
|
||||
if end > start {
|
||||
for _, l := range obj.lines[start:end] {
|
||||
s.WriteString(l)
|
||||
s.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
return strings.TrimRight(s.String(), "\n")
|
||||
}
|
Loading…
Reference in a new issue