peterk@cbmger.UUCP (Peter Kittel GERMANY) (10/15/90)
Again that old theme '(self) modifying code': When I think about it, there HAS to be a clean way. How else could a debugger or a machine language monitor work? I must be able to poke some bytes in the memory to other values and then take this as code or data just as I want, without LoadSeg()ing this everytime. Simply put: How does LoadSeg() do it???? -- Best regards, Dr. Peter Kittel // E-Mail to \\ Only my personal opinions... Commodore Frankfurt, Germany \X/ {uunet|pyramid|rutgers}!cbmvax!cbmger!peterk
mcmahan@netcom.UUCP (Dave Mc Mahan) (10/16/90)
In a previous article, peterk@cbmger.UUCP (Peter Kittel GERMANY) writes: >Again that old theme '(self) modifying code': When I think about it, >there HAS to be a clean way. How else could a debugger or a machine >language monitor work? I must be able to poke some bytes in the >memory to other values and then take this as code or data just as >I want, without LoadSeg()ing this everytime. Debuggers on modern (68000) CPUs generally use one of two methods for tracing a program. The first will ALWAYS work, but is slow. The second runs at full speed (until the desired breakpoint is hit) but requires the code being debugged to be in RAM. 1. The debugger operates as kind of a micro-OperatingSystem. When you tell it to go, it sets the TRACE bit of the 68000. This is a hardware debug mode that Motorola thoughfully included on all CPUs. Before every instruction is executed, the CPU traps back to a special interrupt vector (the TRACE vector) and enters Supervisor mode so that no other TRACE interrupts will occur. The TRACE vector points to the start of the debugger routine that does whatever the debugger wants to do, usually decode the instruction into the assembly mneumonic and dump all the registers for the user to see. When the debugger has finished whatever it intended to do, it does an RTE instruction which causes the target opcode to be executed just like normal. When the next instruction after that is about to be executed, the TRACE interrupt is again asserted by the CPU and the whole process repeats. This method works even with code in ROM (and with a ROMed debugger, if you have one) and only requires some RAM to manage the debugger stack and local variables. Since it interrupts every instruction, the execution time is noticeably affected. 2. The second method used works only with debug code in RAM. The debugger is instructed by the user to replace the instruction at the desired address with an illegal instruction (usually, the ILLEGAL instruction itself! This is a real op-code, haveing a value of 0x8AFC. Look it up!) The debugger then replaces the location in the interrupt vector table where the ILLEGAL instruction would vector to and provides it's own internal vector for processing the breakpoint (The exception vector table must be in RAM also for this to work). The debugger then starts execution at full speed wherever the programmer tells it to. When it hits the ILLEGAL instruction, the CPU vectors to the proper spot in the debugger to handle the breakpoint. This method allows you to run at full speed, but has the side effect of never stopping if you put your breakpoint at the wrong place and that code doesn't get executed. Generally, the programmer places two breakpoints so that he can trap at the desired point when it hits, but can always force the program to hit the other one if he screwed up and didn't put it in the right spot or if the program is doing something he didn't expect (which is why he is probably debugging with a monitor in the first place). >Simply put: How does LoadSeg() do it???? Do what? I have never used LoadSeg(), but my book says that it just loads a module into memory and links the scattered segments together. All it does is load. >Best regards, Dr. Peter Kittel -dave
peterk@cbmger.UUCP (Peter Kittel GERMANY) (10/16/90)
In article <14827@netcom.UUCP> mcmahan@netcom.UUCP (Dave Mc Mahan) writes: > In a previous article, peterk@cbmger.UUCP (Peter Kittel GERMANY) writes: >>Again that old theme '(self) modifying code': When I think about it, >>there HAS to be a clean way. How else could a debugger or a machine > >2. The second method used works only with debug code in RAM. The debugger is > instructed by the user to replace the instruction at the desired address > with an illegal instruction (usually, the ILLEGAL instruction itself! This > is a real op-code, haveing a value of 0x8AFC. Look it up!) Stop. Here you again poke simply two bytes into RAM and hope they will get executed. But in the days of (separate) data and instruction caches you can't be sure that the CPU will see this! So again, the only official method to bring executable code into memory and execute it there is to LoadSeg() it. So this function must be the BIG exception that is capable of some magic trick to circumvent this cache problem under any circumstance. Simple question: How? 2nd question: If we know this trick, would it be also applyable for our own hacks/monitors/code generators? -- Best regards, Dr. Peter Kittel // E-Mail to \\ Only my personal opinions... Commodore Frankfurt, Germany \X/ {uunet|pyramid|rutgers}!cbmvax!cbmger!peterk
DXB132@psuvm.psu.edu (10/17/90)
There are functions in the 2.0 Exec for flushing the cache(s) if they exist. You simply call one of them after creating or modifying code (in this example, after poking $4AFC). LoadSeg does this. But you can't use LoadSeg for everythin g. :-) -- Dan Babcock
p554mve@mpirbn.mpifr-bonn.mpg.de (Michael van Elst) (10/18/90)
In article <14827@netcom.UUCP> mcmahan@netcom.UUCP (Dave Mc Mahan) writes: >Do what? I have never used LoadSeg(), but my book says that it just loads >a module into memory and links the scattered segments together. All it does >is load. The operation performed by LoadSeg() is strictly 'self-modifying' code. (At least if you reuse memory for a new program). Self-modifying code leads to problems when you have a non-transparent cache like in the 68020/68030 where code could be changed in memory but old code could be still executed out of the cache. The Kickstart 1.3 LoadSeg() does nothing to prevent this but the sheer length of the LoadSeg routine will force the obsolete contents of the cache to be flushed (== replaced by the LoadSeg code). I think Kickstart 2.0 does better things since the cache is supported by new Exec system calls, this allows for larger caches where you can't trust that executing code of the size of LoadSeg() will invalidate old cache entries. Regards, -- Michael van Elst UUCP: universe!local-cluster!milky-way!sol!earth!uunet!unido!mpirbn!p554mve Internet: p554mve@mpirbn.mpifr-bonn.mpg.de "A potential Snark may lurk in every tree."
mcmahan@netcom.UUCP (Dave Mc Mahan) (10/18/90)
In a previous article, peterk@cbmger.UUCP (Peter Kittel GERMANY) writes: >In article <14827@netcom.UUCP> mcmahan@netcom.UUCP (Dave Mc Mahan) writes: >> In a previous article, peterk@cbmger.UUCP (Peter Kittel GERMANY) writes: >>>Again that old theme '(self) modifying code': When I think about it, >>>there HAS to be a clean way. How else could a debugger or a machine >> >>2. The second method used works only with debug code in RAM. The debugger is >> instructed by the user to replace the instruction at the desired address >> with an illegal instruction (usually, the ILLEGAL instruction itself! This >> is a real op-code, haveing a value of 0x8AFC. Look it up!) > >Stop. Here you again poke simply two bytes into RAM and hope they will >get executed. But in the days of (separate) data and instruction caches >you can't be sure that the CPU will see this! Yes, it is possible for your program to write to memory and have the write stay in the cache instead of actually getting flushed into memory, or to have the instruction cache already loaded with the instruction location you replaced and have it execute from the cache version rather than the modified version. Now that cache machines are upon us, this fact has to be remembered by the person writing the debugger. The only method I can think of that would work is to write the desired (illegal) instruction and then flush the cache into memory to guarantee that it exists, and then to dump the entire instruction cache and force the CPU to re-read data from main memory when it is about to execute an instruction. Note that this does not mean the CPU has to read data from main memory all the time, just the first time, as it will read in the modified instruction code and store it in the cache. The other option is to disable caching entirely, and force the CPU to read each instruction from memory all the time. This method may not work if the program being debugged decides to enable the cache as part of the sequence of instructions it is to perform. You are correct in assuming that LoadSeg() MUST be able to write bytes of data into memory and then execute them as instructions, but I think that it just flushes the data cache when it is finished writing the segment being loaded, and then marks the entire instruction cache as being 'no good' so that all instructions are re-read when they are initially required by the CPU execution unit. Although Commodore only officially supports LoadSeg() as the means of loading data into memory, programmers who write debuggers must bend the rules slightly (and be willing to support future modifications) to properly do certain things on a CPU with caches. >So again, the only official method to bring executable code into >memory and execute it there is to LoadSeg() it. So this function >must be the BIG exception that is capable of some magic trick to >circumvent this cache problem under any circumstance. >Simple question: How? It just flushes the data cache and marks the instruction cache as being empty when it is finished as described above. Please remember that other functions that pass messages back and forth between seperate CPUs working off the same main memory must also ensure that any writing they do that needs to be shared is also flushed. Message passing within a one-CPU system won't have this problem, since such a message is always treated as data. >2nd question: If we know this trick, would it be also applyable >for our own hacks/monitors/code generators? I'm not sure how applicable the technique is for hacks or code generators, but for a debug monitor that allows placing breakpoints, I can think of no other way to perform the job except to flush caches. >Best regards, Dr. Peter Kittel -dave
dave@unislc.uucp (Dave Martin) (10/23/90)
From article <515@cbmger.UUCP>, by peterk@cbmger.UUCP (Peter Kittel GERMANY): > Stop. Here you again poke simply two bytes into RAM and hope they will > get executed. But in the days of (separate) data and instruction caches > you can't be sure that the CPU will see this! Invalidate the caches after modifying the instruction stream.