@Override
	public SourceMapEntry addSourceMapEntry(SourceFile sourceFile, int lineNumber, Address baseAddr,
			long length) throws LockException, AddressOverflowException {
		if (lineNumber < 0) {
			throw new IllegalArgumentException("lineNumber cannot be negative");
		}
		if (length < 0) {
			throw new IllegalArgumentException("length cannot be negative");
		}
		Objects.requireNonNull(sourceFile, "sourceFile cannot be null");
		Objects.requireNonNull(baseAddr, "baseAddr cannot be null");
		MemoryBlock startBlock = program.getMemory().getBlock(baseAddr);
		if (startBlock == null) {
			throw new AddressOutOfBoundsException(baseAddr + " is not in a defined memory block");
		}
		program.checkExclusiveAccess();

		lock.acquire();
		try {
			Long sourceFileId = getKeyForSourceFile(sourceFile);
			if (sourceFileId == null) {
				throw new IllegalArgumentException(
					sourceFile.toString() + " not associated with program");
			}
			if (length == 0) {
				return addZeroLengthEntry(sourceFileId, lineNumber, baseAddr);
			}
			Address endAddr = baseAddr.addNoWrap(length - 1);

			// check that the entry's range is entirely contained within defined memory blocks
			if (!startBlock.contains(endAddr)) {
				if (!program.getMemory().contains(baseAddr, endAddr)) {
					throw new AddressOutOfBoundsException(
						baseAddr + "," + endAddr + " spans undefined memory");
				}
			}
			SourceMapEntryDB entry = null;

			RecordIterator recIter =
				sourceMapTableAdapter.getSourceMapRecordIterator(endAddr, false);
			while (recIter.hasPrevious()) {
				DBRecord rec = recIter.previous();
				long entryLength = getLength(rec);
				if (entryLength == 0) {
					continue; // length 0 entries can't conflict
				}
				Address entryBase = getStartAddress(rec);
				if (entryBase.equals(baseAddr)) {
					if (entryLength != length) {
						throw new IllegalArgumentException(
							"new entry must have the same length as existing entry");
					}
					if ((sourceFileId << 32 | lineNumber) == getFileAndLine(rec)) {
						return getSourceMapEntry(rec);  // entry is already in the DB
					}
					continue; // non-conflicting entry found, continue checking
				}
				if (isLessOrEqualInSameSpace(baseAddr, entryBase)) {
					throw new IllegalArgumentException(
						"new entry would overlap entry " + getSourceMapEntry(rec).toString());
				}
				if (isLessOrEqualInSameSpace(entryBase, baseAddr)) {
					if (getEndAddress(entryBase, entryLength).compareTo(baseAddr) >= 0) {
						throw new IllegalArgumentException(
							"new entry would overlap entry " + getSourceMapEntry(rec).toString());
					}
				}
				break; // safe to add new entry
			}
			DBRecord rec =
				sourceMapTableAdapter.addMapEntry(sourceFileId, lineNumber, baseAddr, length);
			entry = new SourceMapEntryDB(this, rec, addrMap);
			program.setChanged(ProgramEvent.SOURCE_MAP_CHANGED, null, entry);
			return entry;
		}
		catch (IOException e) {
			dbError(e);
			throw new AssertionError("addSourceMapEntry unsuccessful - possible database error");
		}
		finally {
			lock.release();
		}
	}
{@inheritDoc}<br>
Note: this method will move any source map entry which is entirely contained within the
source address range.  Entries which also contain addresses outside of this range will 
be split and parts within the range to move will be moved.
@param fromAddr first address of range to be moved
@param toAddr target address
@param length number of addresses to move
@param monitor task monitor
@throws AddressOverflowException if overflow occurs when computing new addresses
@throws CancelledException if {@code monitor} is cancelled
Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/sourcemap/SourceFileManagerDB.java