From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 29228 invoked by alias); 10 Jan 2008 05:10:05 -0000 Received: (qmail 29169 invoked by uid 22791); 10 Jan 2008 05:10:02 -0000 X-Spam-Status: No, hits=4.1 required=5.0 tests=BAYES_40,J_CHICKENPOX_44,J_CHICKENPOX_45,J_CHICKENPOX_47,J_CHICKENPOX_48,J_CHICKENPOX_64,J_CHICKENPOX_74,SPF_FAIL X-Spam-Check-By: sourceware.org Received: from simmts6-srv.bellnexxia.net (HELO simmts6-srv.bellnexxia.net) (206.47.199.164) by sourceware.org (qpsmtpd/0.31) with ESMTP; Thu, 10 Jan 2008 05:09:45 +0000 Received: from simip11.srvr.bell.ca ([206.47.199.91]) by simmts6-srv.bellnexxia.net (InterMail vM.5.01.06.13 201-253-122-130-113-20050324) with ESMTP id <20080110050942.FZQN1655.simmts6-srv.bellnexxia.net@simip11.srvr.bell.ca> for ; Thu, 10 Jan 2008 00:09:42 -0500 X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: AgAAAP42hUdGMMSZ/2dsb2JhbAAIqVo Received: from bas9-toronto12-1177601177.dsl.bell.ca (HELO [192.168.1.100]) ([70.48.196.153]) by alconsout.srvr.bell.ca with ESMTP; 10 Jan 2008 00:11:05 -0500 Message-ID: <4785A895.9080501@redhat.com> Date: Thu, 10 Jan 2008 05:10:00 -0000 From: Mike Cvet User-Agent: Thunderbird 2.0.0.6 (X11/20071022) MIME-Version: 1.0 To: frysk@sourceware.org Subject: Fix for #5260 Content-Type: multipart/mixed; boundary="------------080205010205050109020103" X-Virus-Checked: Checked by ClamAV on sourceware.org X-IsSubscribed: yes Mailing-List: contact frysk-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Post: List-Help: , Sender: frysk-owner@sourceware.org X-SW-Source: 2008-q1/txt/msg00017.txt.bz2 This is a multi-part message in MIME format. --------------080205010205050109020103 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Content-length: 418 I've attached some code which fixes the "stepping through a plt" problem listed in #5260. It identifies if the user has stepped into a .plt section in the program, and steps through the dynamic library linking code, landing the user back in their program. I've included a test for it and ensured that it does not break any current tests; if nobody has any problems I'll commit it in a day or two. Thanks! - Mike --------------080205010205050109020103 Content-Type: text/x-patch; name="5260.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="5260.patch" Content-length: 21404 diff --git a/frysk-core/frysk/hpd/StepInstructionCommand.java b/frysk-core/frysk/hpd/StepInstructionCommand.java index 42d55e8..f683f1d 100644 --- a/frysk-core/frysk/hpd/StepInstructionCommand.java +++ b/frysk-core/frysk/hpd/StepInstructionCommand.java @@ -70,7 +70,7 @@ public class StepInstructionCommand extends ParameterizedCommand { } catch (InterruptedException ie) { } } - Iterator stepped = taskList.iterator(); + Iterator stepped = ptset.getTasks(); while (stepped.hasNext()) { Task task = (Task) stepped.next(); DebugInfoFrame rf = cli.getTaskFrame(task); diff --git a/frysk-core/frysk/stepping/InstructionStepState.java b/frysk-core/frysk/stepping/InstructionStepState.java index 9557a77..8903147 100644 --- a/frysk-core/frysk/stepping/InstructionStepState.java +++ b/frysk-core/frysk/stepping/InstructionStepState.java @@ -39,23 +39,48 @@ package frysk.stepping; +import frysk.dwfl.ElfSectionCache; import frysk.proc.Task; +import lib.dwfl.DwflLine; +import lib.dwfl.ElfSectionHeader; public class InstructionStepState extends State { + + private ElfSectionCache elfCache; + private final String PLT_DL_FIXUP = "_dl_fixup"; + public InstructionStepState(Task task) { this.task = task; } /** - * When the instruction observer from SteppingEngine returns, a single - * step has been performed. All that is left to do is to reset the State - * for this Task back to a StoppedState. - * - * @param tse - * The TaskStepEngine for this State. - * @return new StoppedState - */ + * When the instruction observer from SteppingEngine returns, a single + * step has been performed. All that is left to do is to reset the State + * for this Task back to a StoppedState. + * + * @param tse + * The TaskStepEngine for this State. + * @return new StoppedState + */ public State handleUpdate(TaskStepEngine tse) { + + long addr = this.task.getIsa().pc(task); + this.elfCache = new ElfSectionCache(this.task); + ElfSectionHeader header = this.elfCache.getSectionHeader(".plt", addr); + + /* If the user steps into a function call, the following catches the .plt section of the program, and + * ensures that Frysk steps past it, so that the user is landed back into their own code after the call. */ + if ((header != null && header.addr <= addr && (header.addr + header.offset) >= addr) + && (header.type == ElfSectionHeader.ELF_SHT_PROGBITS || header.type == ElfSectionHeader.ELF_SHT_NOBITS)) { + + DwflLine line = tse.getDwflLine(); + + if (line == null) { + tse.getSteppingEngine().continueForStepping(this.task, true); + return new InstructionStepThroughState(task, PLT_DL_FIXUP); + } + } + return new StoppedState(this.task); } diff --git a/frysk-core/frysk/stepping/LineStepState.java b/frysk-core/frysk/stepping/LineStepState.java index 59d9539..19ba783 100644 --- a/frysk-core/frysk/stepping/LineStepState.java +++ b/frysk-core/frysk/stepping/LineStepState.java @@ -41,8 +41,11 @@ package frysk.stepping; import lib.dwfl.DwflLine; import frysk.proc.Task; +//import frysk.stack.Frame; +//import frysk.stack.StackFactory; public class LineStepState extends State { + public LineStepState(Task task) { this.task = task; } @@ -56,15 +59,25 @@ public class LineStepState extends State { * @return this If the line has not changed yet */ public State handleUpdate(TaskStepEngine tse) { - int lineNum; + int lineNum = 0; DwflLine line = tse.getDwflLine(); if (line == null) /* We're in no-debuginfo land */ { tse.setLine(0); - /* Returned a StoppedState because line-stepping has no meaning - * when there is no debug information to relate the 'lines' to. */ - return new StoppedState(this.task); + + /* Assume the user has stepped into some sort of library call for which + * there is no debugging information. Continue stepping through this + * area until either new code with debuginfo is encountered, or the original + * calling code is returned to. */ + + //Frame frame = StackFactory.createFrame(task); + //System.err.println("LineStepState:handleUpdate - no line " + frame.getSymbol().getDemangledName() + " 0x" + Long.toHexString(frame.getSymbol(). + + //tse.getSteppingEngine().continueForStepping(this.task, true); + + //return this; + return new StoppedState(this.task); } else lineNum = line.getLineNum(); diff --git a/frysk-core/frysk/stepping/SteppingEngine.java b/frysk-core/frysk/stepping/SteppingEngine.java index 68584ca..2b71fa7 100644 --- a/frysk-core/frysk/stepping/SteppingEngine.java +++ b/frysk-core/frysk/stepping/SteppingEngine.java @@ -487,9 +487,9 @@ public class SteppingEngine { * @param task The Task to be stepped * @param frame The frame to step out of. */ - public void stepOut(Task task, DebugInfoFrame frame) { + public void stepOut(Task task, Frame frame) { - long address = frame.getOuterDebugInfoFrame().getAddress(); + long address = frame.getOuter().getAddress(); TaskStepEngine tse = (TaskStepEngine) this.taskStateMap.get(task); tse.setState(new StepOutState(task)); @@ -502,6 +502,22 @@ public class SteppingEngine { this.breakpointMap.put(task, this.breakpoint); task.requestAddCodeObserver(this.breakpoint, address); } + + public void stepOut(Task task, Frame frame, State state) { + + long address = frame.getOuter().getAddress(); + + TaskStepEngine tse = (TaskStepEngine) this.taskStateMap.get(task); + tse.setState(state); + this.steppingObserver.notifyNotBlocked(tse); + + int i = ((Integer) this.contextMap.get(task.getProc())).intValue(); + this.contextMap.put(task.getProc(), new Integer(++i)); + + this.breakpoint = new SteppingBreakpoint(this, address); + this.breakpointMap.put(task, this.breakpoint); + task.requestAddCodeObserver(this.breakpoint, address); + } /** * Performs a step-out operation on a list of Tasks - runs each Task until diff --git a/frysk-core/frysk/stepping/TaskStepEngine.java b/frysk-core/frysk/stepping/TaskStepEngine.java index 6f35c67..6a84c14 100644 --- a/frysk-core/frysk/stepping/TaskStepEngine.java +++ b/frysk-core/frysk/stepping/TaskStepEngine.java @@ -54,6 +54,7 @@ import lib.dwfl.DwflLine; * State.handleUpdate(). */ public class TaskStepEngine { + private SteppingEngine steppingEngine; /* The Task for this TaskStepEngine */ diff --git a/frysk-core/frysk/stepping/TestStepping.java b/frysk-core/frysk/stepping/TestStepping.java index 40df4e9..0ca8037 100644 --- a/frysk-core/frysk/stepping/TestStepping.java +++ b/frysk-core/frysk/stepping/TestStepping.java @@ -43,6 +43,7 @@ import java.io.File; import java.util.HashMap; import java.util.Observable; import java.util.Observer; + import frysk.testbed.Offspring; import frysk.testbed.SynchronizedOffspring; import frysk.Config; @@ -1009,6 +1010,81 @@ public class TestStepping extends TestLib { assertRunUntilStop("Running test"); cleanup(); } + + public void testInstructionStepThroughSection() { + + class InstructionStepThroughSectionTest implements SteppingTest { + + Task testTask = null; + String callingFrame = "foo"; + boolean first = true; + int endLine = 0; + + public InstructionStepThroughSectionTest(Task task, int lineNum) { + testTask = task; + endLine = lineNum; + } + + public void runAssertions() { + + DebugInfoFrame frame = DebugInfoStackFactory + .createDebugInfoStackTrace(testTask); + + String s = frame.getSymbol().getDemangledName(); + + if (first) { + se.stepLine(this.testTask); + this.first = false; + return; + } + + assertEquals("calling frame", callingFrame, s); + assertEquals("line number", endLine, frame.getLine().getLine()); + Manager.eventLoop.requestStop(); + } + } + + /** Variable setup */ + String source = Config.getRootSrcDir() + + "frysk-core/frysk/pkglibdir/funit-libcall.c"; + + this.scanner = new TestfileTokenScanner(new File(source)); + + int startLine = this.scanner.findTokenLine("_testIStepThrough_"); + + int endLine = startLine + 1; + + /* The test process */ + dbae = new DaemonBlockedAtEntry(Config.getPkgLibFile("funit-libcall")); + + Task theTask = dbae.getMainTask(); + + this.testStarted = false; + + initTaskWithTask(theTask, source, startLine, endLine); + + this.currentTest = new InstructionStepThroughSectionTest(theTask, endLine); + + DebugInfoFrame frame = DebugInfoStackFactory + .createDebugInfoStackTrace(theTask); + assertTrue("Line information present", + frame.getLine() != SourceLocation.UNKNOWN); + + /** The stepping operation */ + this.se.stepLine(theTask); + + this.testStarted = true; + /** Run to completion */ + assertRunUntilStop("Running test"); + cleanup(); + } +// This file is part of the program FRYSK. +// +// Copyright 2007, Red Hat Inc. +// +// FRYSK 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; version 2 of the License. +// +// FRYSK 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 FRYSK; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// In addition, as a special exception, Red Hat, Inc. gives You the +// additional right to link the code of FRYSK with code not covered +// under the GNU General Public License ("Non-GPL Code") and to +// distribute linked combinations including the two, subject to the +// limitations in this paragraph. Non-GPL Code permitted under this +// exception must only link to the code of FRYSK through those well +// defined interfaces identified in the file named EXCEPTION found in +// the source code files (the "Approved Interfaces"). The files of +// Non-GPL Code may instantiate templates or use macros or inline +// functions from the Approved Interfaces without causing the +// resulting work to be covered by the GNU General Public +// License. Only Red Hat, Inc. may make changes or additions to the +// list of Approved Interfaces. You must obey the GNU General Public +// License in all respects for all of the FRYSK code and other code +// used in conjunction with FRYSK except the Non-GPL Code covered by +// this exception. If you modify this file, you may extend this +// exception to your version of the file, but you are not obligated to +// do so. If you do not wish to provide this exception without +// modification, you must delete this exception statement from your +// version and license this file solely under the GPL without +// exception. + +package frysk.dwfl; + +import java.util.WeakHashMap; + +import frysk.proc.Task; +import lib.dwfl.Dwfl; +import lib.dwfl.DwflModule; +import lib.dwfl.Elf; +import lib.dwfl.ElfSection; +import lib.dwfl.ElfSectionHeader; +import lib.dwfl.ModuleElfBias; + +/** + * Class to provide and cache requested ElfSectionHeaders based on name. + */ +public class ElfSectionCache { + + private Task task; + private WeakHashMap sectionMap; + + public ElfSectionCache (Task task) { + this.task = task; + this.sectionMap = new WeakHashMap(); + } + + /** + * Returns an ElfSectionHeader representing the ELF header for this class' Task object as + * given by the section name and module address in the parameters to this method. Also + * caches the headers in a WeakHashMap for later use. + * + * @param name The name of the section + * @param addr The module address + * @return The corresponding ElfSectionHeader + */ + public ElfSectionHeader getSectionHeader(String name, long addr) { + + if (this.sectionMap.containsKey(name)) + return (ElfSectionHeader) this.sectionMap.get(name); + + Dwfl dwfl = DwflCache.getDwfl(this.task); + DwflModule dwflModule = dwfl.getModule(addr); + ModuleElfBias elfBias = dwflModule.getElf(); + Elf elf = elfBias.elf; + + for (ElfSection section = elf.getSection(0); + section != null; + section = elf.getNextSection(section)) { + + ElfSectionHeader sheader = section.getSectionHeader(); + if (sheader.name != null && sheader.name.equals(name)) { + + this.sectionMap.put(name, sheader); + return sheader; + } + } + + return null; + } + + public Task getTask() { + return this.task; + } + +} +// This file is part of the program FRYSK. +// +// Copyright 2007, Red Hat Inc. +// +// FRYSK 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; version 2 of the License. +// +// FRYSK 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 FRYSK; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// In addition, as a special exception, Red Hat, Inc. gives You the +// additional right to link the code of FRYSK with code not covered +// under the GNU General Public License ("Non-GPL Code") and to +// distribute linked combinations including the two, subject to the +// limitations in this paragraph. Non-GPL Code permitted under this +// exception must only link to the code of FRYSK through those well +// defined interfaces identified in the file named EXCEPTION found in +// the source code files (the "Approved Interfaces"). The files of +// Non-GPL Code may instantiate templates or use macros or inline +// functions from the Approved Interfaces without causing the +// resulting work to be covered by the GNU General Public +// License. Only Red Hat, Inc. may make changes or additions to the +// list of Approved Interfaces. You must obey the GNU General Public +// License in all respects for all of the FRYSK code and other code +// used in conjunction with FRYSK except the Non-GPL Code covered by +// this exception. If you modify this file, you may extend this +// exception to your version of the file, but you are not obligated to +// do so. If you do not wish to provide this exception without +// modification, you must delete this exception statement from your +// version and license this file solely under the GPL without +// exception. + +package frysk.dwfl; + +import lib.dwfl.ElfSectionHeader; +import frysk.Config; +import frysk.proc.Task; +import frysk.testbed.DaemonBlockedAtEntry; +import frysk.testbed.TestLib; + +public class TestElfSectionCache extends TestLib { + + public void testGetSectionHeader() { + + DaemonBlockedAtEntry dbae = new DaemonBlockedAtEntry(Config.getPkgLibFile("funit-libcall")); + Task task = dbae.getMainTask(); + + long addr = task.getIsa().pc(task); + ElfSectionCache e = new ElfSectionCache(task); + + ElfSectionHeader plt = e.getSectionHeader(".plt", addr); + + assertTrue("Section not null", (plt != null)); + assertTrue("Section name not null", (plt.name != null)); + assertTrue("Section addr not zero", (plt.addr != 0)); + assertTrue("Section offset not zero", (plt.offset != 0)); + } + +} +// This file is part of the program FRYSK. +// +// Copyright 2007, Red Hat Inc. +// +// FRYSK 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; version 2 of the License. +// +// FRYSK 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 FRYSK; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// In addition, as a special exception, Red Hat, Inc. gives You the +// additional right to link the code of FRYSK with code not covered +// under the GNU General Public License ("Non-GPL Code") and to +// distribute linked combinations including the two, subject to the +// limitations in this paragraph. Non-GPL Code permitted under this +// exception must only link to the code of FRYSK through those well +// defined interfaces identified in the file named EXCEPTION found in +// the source code files (the "Approved Interfaces"). The files of +// Non-GPL Code may instantiate templates or use macros or inline +// functions from the Approved Interfaces without causing the +// resulting work to be covered by the GNU General Public +// License. Only Red Hat, Inc. may make changes or additions to the +// list of Approved Interfaces. You must obey the GNU General Public +// License in all respects for all of the FRYSK code and other code +// used in conjunction with FRYSK except the Non-GPL Code covered by +// this exception. If you modify this file, you may extend this +// exception to your version of the file, but you are not obligated to +// do so. If you do not wish to provide this exception without +// modification, you must delete this exception statement from your +// version and license this file solely under the GPL without +// exception. + +package frysk.stepping; + +import lib.dwfl.DwflLine; +import frysk.proc.Task; +import frysk.stack.Frame; +import frysk.stack.StackFactory; + +public class InstructionStepThroughState extends State { + + private String name; + private int steppingOut = 0; + + public InstructionStepThroughState(Task task) { + this.task = task; + this.name = null; + } + + public InstructionStepThroughState(Task task, String name) { + this.task = task; + this.name = name; + } + + /** + * State used to represent when a task has entered some section of the program (such + * as a .plt section) which must be carefully stepped through. + * + * @param tse + * The TaskStepEngine for this State. + * @return this If more stepping needs to be done. + * @return StoppedState If we have returned to user code. + */ + public State handleUpdate(TaskStepEngine tse) { + + Frame frame = StackFactory.createFrame(task); + + /* This object has already performed a step-out operation on the task. + * Here, clean up and perform another to return to user code. */ + if (this.steppingOut == 1) { + + this.steppingOut = 2; + tse.getSteppingEngine().removeBreakpoint(this.task); + + /* Here, we're back in _dl_runtime_resolve - step out of this as well. */ + tse.getSteppingEngine().stepOut(this.task, frame, this); + return this; + + } else if (this.steppingOut == 2) { + + this.steppingOut = 0; + tse.getSteppingEngine().removeBreakpoint(this.task); + } + + /* The frame being searched for has been reached. Perform a step-out. */ + if (frame.getSymbol().getDemangledName().equals(this.name)) { + + this.steppingOut = 1; + tse.getSteppingEngine().stepOut(this.task, frame, this); + return this; + + } else if (frame.getSymbol().getDemangledName().contains("_start")) { + /* Avoid handling any code having to do with the initialization of a process */ + + return new StoppedState(this.task); + + } else { + + /* Inside some code without debuginfo, and isn't the function we're interested in. + * Continue stepping through it. */ + if (tse.getLine() == 0) { + + DwflLine line = tse.getDwflLine(); + + if (line == null) { + tse.getSteppingEngine() + .continueForStepping(this.task, true); + return this; + } else { + /* Back in debuginfo. Finish the stepping operation. */ + return new StoppedState(this.task); + } + } else { + /* Back in debuginfo. Finish the stepping operation. */ + return new StoppedState(this.task); + } + } + } + + public boolean isStopped() { + return false; + } + + public boolean isAlive() { + return true; + } +} --------------080205010205050109020103--