Index: d.diff =================================================================== --- d.diff +++ /dev/null @@ -1,712 +0,0 @@ -diff --git a/debuginfo-tests/dexter/Commands.md b/debuginfo-tests/dexter/Commands.md -index c30a0d7214c..2e2fecfed92 100644 ---- a/debuginfo-tests/dexter/Commands.md -+++ b/debuginfo-tests/dexter/Commands.md -@@ -173,6 +173,34 @@ Expect the source line this is found on will never be stepped on to. - [TODO] - - -+---- -+## DexLimitSteps -+ DexLimitSteps(expr, *values [, **from_line=1],[,**to_line=Max] -+ [,**on_line]) -+ -+ Args: -+ expr (str): variable or value to compare. -+ -+ Arg list: -+ values (str): At least one potential value the expr may evaluate to. -+ -+ Keyword args: -+ from_line (int): Define the start of the limited step range. -+ to_line (int): Define the end of the limited step range. -+ on_line (int): Define a range with length 1 starting and ending on the -+ same line. -+ -+### Description -+Define a limited stepping range that is predicated on a condition. When -+'(expr) == (values[n])', set a range of temporary, unconditional break points within -+the test file defined by the range from_line and to_line or on_line. -+ -+The condition is only evaluated on the line 'from_line' or 'on_line'. If the -+condition is not true at the start of the range, the whole range is ignored. -+ -+DexLimitSteps commands are useful for reducing the amount of steps gathered in -+large test cases that would normally take much longer to complete. -+ - ---- - ## DexLabel - DexLabel(name) -diff --git a/debuginfo-tests/dexter/dex/command/ParseCommand.py b/debuginfo-tests/dexter/dex/command/ParseCommand.py -index 4cc9ae12592..8246ea9e3cf 100644 ---- a/debuginfo-tests/dexter/dex/command/ParseCommand.py -+++ b/debuginfo-tests/dexter/dex/command/ParseCommand.py -@@ -24,6 +24,7 @@ from dex.command.commands.DexExpectStepOrder import DexExpectStepOrder - from dex.command.commands.DexExpectWatchType import DexExpectWatchType - from dex.command.commands.DexExpectWatchValue import DexExpectWatchValue - from dex.command.commands.DexLabel import DexLabel -+from dex.command.commands.DexLimitSteps import DexLimitSteps - from dex.command.commands.DexUnreachable import DexUnreachable - from dex.command.commands.DexWatch import DexWatch - from dex.utils import Timer -@@ -42,6 +43,7 @@ def _get_valid_commands(): - DexExpectWatchType.get_name() : DexExpectWatchType, - DexExpectWatchValue.get_name() : DexExpectWatchValue, - DexLabel.get_name() : DexLabel, -+ DexLimitSteps.get_name() : DexLimitSteps, - DexUnreachable.get_name() : DexUnreachable, - DexWatch.get_name() : DexWatch - } -diff --git a/debuginfo-tests/dexter/dex/command/commands/DexLimitSteps.py b/debuginfo-tests/dexter/dex/command/commands/DexLimitSteps.py -new file mode 100644 -index 00000000000..d66401b5599 ---- /dev/null -+++ b/debuginfo-tests/dexter/dex/command/commands/DexLimitSteps.py -@@ -0,0 +1,54 @@ -+# DExTer : Debugging Experience Tester -+# ~~~~~~ ~ ~~ ~ ~~ -+# -+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -+# See https://llvm.org/LICENSE.txt for license information. -+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -+"""A Command that enables test writers to specify a limited number of break -+points using an start condition and range. -+""" -+ -+from dex.command.CommandBase import CommandBase -+ -+class DexLimitSteps(CommandBase): -+ def __init__(self, *args, **kwargs): -+ self.expression = args[0] -+ self.values = [str(arg) for arg in args[1:]] -+ try: -+ on_line = kwargs.pop('on_line') -+ self.from_line = on_line -+ self.to_line = on_line -+ except KeyError: -+ self.from_line = kwargs.pop('from_line', 1) -+ self.to_line = kwargs.pop('to_line', 999999) -+ if kwargs: -+ raise TypeError('unexpected named args: {}'.format( -+ ', '.join(kwargs))) -+ super(DexLimitSteps, self).__init__() -+ -+ def resolve_label(self, label_line_pair): -+ label, lineno = label_line_pair -+ if isinstance(self.from_line, str): -+ if self.from_line == label: -+ self.from_line = lineno -+ if isinstance(self.to_line, str): -+ if self.to_line == label: -+ self.to_line = lineno -+ -+ def has_labels(self): -+ return len(self.get_label_args()) > 0 -+ -+ def get_label_args(self): -+ return [label for label in (self.from_line, self.to_line) -+ if isinstance(label, str)] -+ -+ def eval(self): -+ raise NotImplementedError('DexLimitSteps commands cannot be evaled.') -+ -+ @staticmethod -+ def get_name(): -+ return __class__.__name__ -+ -+ @staticmethod -+ def get_subcommands() -> dict: -+ return None -diff --git a/debuginfo-tests/dexter/dex/debugger/DebuggerBase.py b/debuginfo-tests/dexter/dex/debugger/DebuggerBase.py -index 2261396b94b..12f4f4ab7a0 100644 ---- a/debuginfo-tests/dexter/dex/debugger/DebuggerBase.py -+++ b/debuginfo-tests/dexter/dex/debugger/DebuggerBase.py -@@ -120,6 +120,14 @@ class DebuggerBase(object, metaclass=abc.ABCMeta): - def add_breakpoint(self, file_, line): - pass - -+ @abc.abstractmethod -+ def add_conditional_breakpoint(self, file_, line, condition): -+ pass -+ -+ @abc.abstractmethod -+ def delete_conditional_breakpoint(self, file_, line, condition): -+ pass -+ - @abc.abstractmethod - def launch(self): - pass -diff --git a/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ConditionalController.py b/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ConditionalController.py -new file mode 100644 -index 00000000000..4e4327b53f8 ---- /dev/null -+++ b/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ConditionalController.py -@@ -0,0 +1,127 @@ -+# DExTer : Debugging Experience Tester -+# ~~~~~~ ~ ~~ ~ ~~ -+# -+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -+# See https://llvm.org/LICENSE.txt for license information. -+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -+"""Conditional Controller Class for DExTer.-""" -+ -+ -+import os -+import time -+from collections import defaultdict -+from itertools import chain -+ -+from dex.debugger.DebuggerControllers.ControllerHelpers import in_source_file, update_step_watches -+from dex.debugger.DebuggerControllers.DebuggerControllerBase import DebuggerControllerBase -+from dex.debugger.DebuggerBase import DebuggerBase -+from dex.utils.Exceptions import DebuggerException -+ -+ -+class ConditionalBpRange: -+ """Represents a conditional range of breakpoints within a source file descending from -+ one line to another.""" -+ -+ def __init__(self, expression: str, path: str, range_from: int, range_to: int, values: list): -+ self.expression = expression -+ self.path = path -+ self.range_from = range_from -+ self.range_to = range_to -+ self.conditional_values = values -+ -+ def get_conditional_expression_list(self): -+ conditional_list = [] -+ for value in self.conditional_values: -+ # () == () -+ conditional_expression = '({}) == ({})'.format(self.expression, value) -+ conditional_list.append(conditional_expression) -+ return conditional_list -+ -+ -+class ConditionalController(DebuggerControllerBase): -+ def __init__(self, context, step_collection): -+ self.context = context -+ self.step_collection = step_collection -+ self._conditional_bps = None -+ self._watches = set() -+ self._step_index = 0 -+ self._build_conditional_bps() -+ self._path_and_line_to_conditional_bp = defaultdict(list) -+ self._pause_between_steps = context.options.pause_between_steps -+ self._max_steps = context.options.max_steps -+ -+ def _build_conditional_bps(self): -+ commands = self.step_collection.commands -+ self._conditional_bps = [] -+ try: -+ limit_commands = commands['DexLimitSteps'] -+ for lc in limit_commands: -+ conditional_bp = ConditionalBpRange( -+ lc.expression, -+ lc.path, -+ lc.from_line, -+ lc.to_line, -+ lc.values) -+ self._conditional_bps.append(conditional_bp) -+ except KeyError: -+ raise DebuggerException('Missing DexLimitSteps commands, cannot conditionally step.') -+ -+ def _set_conditional_bps(self): -+ # When we break in the debugger we need a quick and easy way to look up -+ # which conditional bp we've breaked on. -+ for cbp in self._conditional_bps: -+ conditional_bp_list = self._path_and_line_to_conditional_bp[(cbp.path, cbp.range_from)] -+ conditional_bp_list.append(cbp) -+ -+ # Set break points only on the first line of any conditional range, we'll set -+ # more break points for a range when the condition is satisfied. -+ for cbp in self._conditional_bps: -+ for cond_expr in cbp.get_conditional_expression_list(): -+ self.debugger.add_conditional_breakpoint(cbp.path, cbp.range_from, cond_expr) -+ -+ def _conditional_met(self, cbp): -+ for cond_expr in cbp.get_conditional_expression_list(): -+ valueIR = self.debugger.evaluate_expression(cond_expr) -+ if valueIR.type_name == 'bool' and valueIR.value == 'true': -+ return True -+ return False -+ -+ def _run_debugger_custom(self): -+ # TODO: Add conditional and unconditional breakpoint support to dbgeng. -+ if self.debugger.get_name() == 'dbgeng': -+ raise DebuggerException('DexLimitSteps commands are not supported by dbgeng') -+ -+ self.step_collection.clear_steps() -+ self._set_conditional_bps() -+ -+ for command_obj in chain.from_iterable(self.step_collection.commands.values()): -+ self._watches.update(command_obj.get_watches()) -+ -+ self.debugger.launch() -+ time.sleep(self._pause_between_steps) -+ while not self.debugger.is_finished: -+ while self.debugger.is_running: -+ pass -+ -+ step_info = self.debugger.get_step_info(self._watches, self._step_index) -+ if step_info.current_frame: -+ self._step_index += 1 -+ update_step_watches(step_info, self._watches, self.step_collection.commands) -+ self.step_collection.new_step(self.context, step_info) -+ -+ loc = step_info.current_location -+ conditional_bp_key = (loc.path, loc.lineno) -+ if conditional_bp_key in self._path_and_line_to_conditional_bp: -+ -+ conditional_bps = self._path_and_line_to_conditional_bp[conditional_bp_key] -+ for cbp in conditional_bps: -+ if self._conditional_met(cbp): -+ # Unconditional range should ignore first line as that's the -+ # conditional bp we just hit and should be inclusive of final line -+ for line in range(cbp.range_from + 1, cbp.range_to + 1): -+ self.debugger.add_conditional_breakpoint(cbp.path, line, condition='') -+ -+ # Clear any uncondtional break points at this loc. -+ self.debugger.delete_conditional_breakpoint(file_=loc.path, line=loc.lineno, condition='') -+ self.debugger.go() -+ time.sleep(self._pause_between_steps) -diff --git a/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ControllerHelpers.py b/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ControllerHelpers.py -new file mode 100644 -index 00000000000..adac7674aff ---- /dev/null -+++ b/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ControllerHelpers.py -@@ -0,0 +1,37 @@ -+# DExTer : Debugging Experience Tester -+# ~~~~~~ ~ ~~ ~ ~~ -+# -+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -+# See https://llvm.org/LICENSE.txt for license information. -+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -+ -+import os -+from itertools import chain -+ -+def in_source_file(source_files, step_info): -+ if not step_info.current_frame: -+ return False -+ if not step_info.current_location.path: -+ return False -+ if not os.path.exists(step_info.current_location.path): -+ return False -+ return any(os.path.samefile(step_info.current_location.path, f) \ -+ for f in source_files) -+ -+def update_step_watches(step_info, watches, commands): -+ watch_cmds = ['DexUnreachable', 'DexExpectStepOrder'] -+ towatch = chain.from_iterable(commands[x] -+ for x in watch_cmds -+ if x in commands) -+ try: -+ # Iterate over all watches of the types named in watch_cmds -+ for watch in towatch: -+ loc = step_info.current_location -+ if (os.path.exists(loc.path) -+ and os.path.samefile(watch.path, loc.path) -+ and watch.lineno == loc.lineno): -+ result = watch.eval(step_info) -+ step_info.watches.update(result) -+ break -+ except KeyError: -+ pass -diff --git a/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DebuggerControllerBase.py b/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DebuggerControllerBase.py -index ff98baa2d0e..87b13fc7f3a 100644 ---- a/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DebuggerControllerBase.py -+++ b/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DebuggerControllerBase.py -@@ -4,7 +4,7 @@ - # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. - # See https://llvm.org/LICENSE.txt for license information. - # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception --"""Default class for controlling debuggers.""" -+"""Abstract Base class for controlling debuggers.""" - - import abc - -diff --git a/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DefaultController.py b/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DefaultController.py -index 0077a19e601..c41a3eff0d3 100644 ---- a/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DefaultController.py -+++ b/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DefaultController.py -@@ -4,61 +4,37 @@ - # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. - # See https://llvm.org/LICENSE.txt for license information. - # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception --"""Base class for controlling debuggers.""" -+"""Default class for controlling debuggers.""" - - from itertools import chain - import os - import time - - from dex.debugger.DebuggerControllers.DebuggerControllerBase import DebuggerControllerBase --from dex.utils.Exceptions import DebuggerException -+from dex.debugger.DebuggerControllers.ControllerHelpers import in_source_file, update_step_watches -+from dex.utils.Exceptions import DebuggerException, LoadDebuggerException - - class DefaultController(DebuggerControllerBase): - def __init__(self, context, step_collection): - self.context = context - self.step_collection = step_collection -+ self.source_files = self.context.options.source_files - self.watches = set() - self.step_index = 0 - -- def _update_step_watches(self, step_info): -- watch_cmds = ['DexUnreachable', 'DexExpectStepOrder'] -- towatch = chain.from_iterable(self.step_collection.commands[x] -- for x in watch_cmds -- if x in self.step_collection.commands) -- try: -- # Iterate over all watches of the types named in watch_cmds -- for watch in towatch: -- loc = step_info.current_location -- if (os.path.exists(loc.path) -- and os.path.samefile(watch.path, loc.path) -- and watch.lineno == loc.lineno): -- result = watch.eval(step_info) -- step_info.watches.update(result) -- break -- except KeyError: -- pass -- - def _break_point_all_lines(self): - for s in self.context.options.source_files: - with open(s, 'r') as fp: - num_lines = len(fp.readlines()) - for line in range(1, num_lines + 1): -- self.debugger.add_breakpoint(s, line) -- -- def _in_source_file(self, step_info): -- if not step_info.current_frame: -- return False -- if not step_info.current_location.path: -- return False -- if not os.path.exists(step_info.current_location.path): -- return False -- return any(os.path.samefile(step_info.current_location.path, f) \ -- for f in self.context.options.source_files) -+ try: -+ self.debugger.add_breakpoint(s, line) -+ except DebuggerException: -+ raise LoadDebuggerException(DebuggerException.msg) - - def _run_debugger_custom(self): - self.step_collection.debugger = self.debugger.debugger_info - self._break_point_all_lines() -- - self.debugger.launch() - - for command_obj in chain.from_iterable(self.step_collection.commands.values()): -@@ -76,10 +52,10 @@ class DefaultController(DebuggerControllerBase): - step_info = self.debugger.get_step_info(self.watches, self.step_index) - - if step_info.current_frame: -- self._update_step_watches(step_info) -+ update_step_watches(step_info, self.watches, self.step_collection.commands) - self.step_collection.new_step(self.context, step_info) - -- if self._in_source_file(step_info): -+ if in_source_file(self.source_files, step_info): - self.debugger.step() - else: - self.debugger.go() -diff --git a/debuginfo-tests/dexter/dex/debugger/dbgeng/dbgeng.py b/debuginfo-tests/dexter/dex/debugger/dbgeng/dbgeng.py -index 0afc748aecb..d812fd974f7 100644 ---- a/debuginfo-tests/dexter/dex/debugger/dbgeng/dbgeng.py -+++ b/debuginfo-tests/dexter/dex/debugger/dbgeng/dbgeng.py -@@ -77,11 +77,21 @@ class DbgEng(DebuggerBase): - self.client.Control.RemoveBreakpoint(x) - - def add_breakpoint(self, file_, line): -- # This is something to implement in the future -- as it stands, Dexter -- # doesn't test for such things as "I can set a breakpoint on this line". -- # This is only called AFAICT right now to ensure we break on every step. -+ # Breakpoint setting/deleting is not supported by dbgeng at this moment -+ # but is something that should be considered in the future. -+ # TODO: this method is called in the DefaultController but has no effect. - pass - -+ def add_conditional_breakpoint(self, file_, line, condition): -+ # breakpoint setting/deleting is not supported by dbgeng at this moment -+ # but is something that should be considered in the future. -+ raise NotImplementedError('add_conditional_breakpoint is not yet implemented by dbgeng') -+ -+ def delete_conditional_breakpoint(self, file_, line, condition): -+ # breakpoint setting/deleting is not supported by dbgeng at this moment -+ # but is something that should be considered in the future. -+ raise NotImplementedError('delete_conditional_breakpoint is not yet implemented by dbgeng') -+ - def launch(self): - # We are, by this point, already launched. - self.step_info = probe_process.probe_state(self.client) -diff --git a/debuginfo-tests/dexter/dex/debugger/lldb/LLDB.py b/debuginfo-tests/dexter/dex/debugger/lldb/LLDB.py -index a943431c888..c7bb74681d9 100644 ---- a/debuginfo-tests/dexter/dex/debugger/lldb/LLDB.py -+++ b/debuginfo-tests/dexter/dex/debugger/lldb/LLDB.py -@@ -105,9 +105,48 @@ class LLDB(DebuggerBase): - - def add_breakpoint(self, file_, line): - if not self._target.BreakpointCreateByLocation(file_, line): -- raise LoadDebuggerException( -+ raise DebuggerException( - 'could not add breakpoint [{}:{}]'.format(file_, line)) - -+ def add_conditional_breakpoint(self, file_, line, condition): -+ bp = self._target.BreakpointCreateByLocation(file_, line) -+ if bp: -+ bp.SetCondition(condition) -+ else: -+ raise DebuggerException( -+ 'could not add breakpoint [{}:{}]'.format(file_, line)) -+ -+ def delete_conditional_breakpoint(self, file_, line, condition): -+ bp_count = self._target.GetNumBreakpoints() -+ bps = [self._target.GetBreakpointAtIndex(ix) for ix in range(0, bp_count)] -+ -+ for bp in bps: -+ bp_cond = bp.GetCondition() -+ bp_cond = bp_cond if bp_cond is not None else '' -+ -+ if bp_cond != condition: -+ continue -+ -+ # If one of the bound bp locations for this bp is bound to the same -+ # line in file_ above, then delete the entire parent bp and all -+ # bp locs. -+ # https://lldb.llvm.org/python_reference/lldb.SBBreakpoint-class.html -+ for breakpoint_location in bp: -+ sb_address = breakpoint_location.GetAddress() -+ -+ sb_line_entry = sb_address.GetLineEntry() -+ bl_line = sb_line_entry.GetLine() -+ -+ sb_file_entry = sb_line_entry.GetFileSpec() -+ bl_dir = sb_file_entry.GetDirectory() -+ bl_file_name = sb_file_entry.GetFilename() -+ -+ bl_file_path = os.path.join(bl_dir, bl_file_name) -+ -+ if bl_file_path == file_ and bl_line == line: -+ self._target.BreakpointDelete(bp.GetID()) -+ break -+ - def launch(self): - self._process = self._target.LaunchSimple(None, None, os.getcwd()) - if not self._process or self._process.GetNumThreads() == 0: -diff --git a/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio.py b/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio.py -index b9816f84f72..40a902bd205 100644 ---- a/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio.py -+++ b/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio.py -@@ -82,6 +82,9 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst - - @property - def _location(self): -+ #TODO: Find a better way of determining path, line and column info -+ # that doesn't require reading break points. This method requires -+ # all lines to have a break point on them. - bp = self._debugger.BreakpointLastHit - return { - 'path': getattr(bp, 'File', None), -@@ -111,8 +114,20 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst - def add_breakpoint(self, file_, line): - self._debugger.Breakpoints.Add('', file_, line) - -+ def add_conditional_breakpoint(self, file_, line, condition): -+ column = 1 -+ self._debugger.Breakpoints.Add('', file_, line, column, condition) -+ -+ def delete_conditional_breakpoint(self, file_, line, condition): -+ for bp in self._debugger.Breakpoints: -+ for bound_bp in bp.Children: -+ if (bound_bp.File == file_ and bound_bp.FileLine == line and -+ bound_bp.Condition == condition): -+ bp.Delete() -+ break -+ - def launch(self): -- self.step() -+ self._fn_go() - - def step(self): - self._fn_step() -diff --git a/debuginfo-tests/dexter/dex/tools/test/Tool.py b/debuginfo-tests/dexter/dex/tools/test/Tool.py -index a615c8cad90..43191fd44bd 100644 ---- a/debuginfo-tests/dexter/dex/tools/test/Tool.py -+++ b/debuginfo-tests/dexter/dex/tools/test/Tool.py -@@ -16,6 +16,7 @@ from dex.builder import run_external_build_script - from dex.command.ParseCommand import get_command_infos - from dex.debugger.Debuggers import run_debugger_subprocess - from dex.debugger.DebuggerControllers.DefaultController import DefaultController -+from dex.debugger.DebuggerControllers.ConditionalController import ConditionalController - from dex.dextIR.DextIR import DextIR - from dex.heuristic import Heuristic - from dex.tools import TestToolBase -@@ -136,9 +137,15 @@ class Tool(TestToolBase): - executable_path=self.context.options.executable, - source_paths=self.context.options.source_files, - dexter_version=self.context.version) -+ - step_collection.commands = get_command_infos( - self.context.options.source_files) -- debugger_controller = DefaultController(self.context, step_collection) -+ -+ if 'DexLimitSteps' in step_collection.commands: -+ debugger_controller = ConditionalController(self.context, step_collection) -+ else: -+ debugger_controller = DefaultController(self.context, step_collection) -+ - return debugger_controller - - def _get_steps(self, builderIR): -diff --git a/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_check_json_step_count.cpp b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_check_json_step_count.cpp -new file mode 100644 -index 00000000000..45683fced2d ---- /dev/null -+++ b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_check_json_step_count.cpp -@@ -0,0 +1,20 @@ -+// Purpose: -+// Check number of step lines are correctly reported in json output. -+// -+// REQUIRES: system-linux -+// -+// RUN: %dexter_regression_test --verbose -- %s | FileCheck %s -+// CHECK: limit_steps_check_json_step_count.cpp -+// CHECK: ## BEGIN ## -+// CHECK-COUNT-3: json_step_count.cpp", -+ -+int main() { -+ int result = 0; -+ for(int ix = 0; ix != 10; ++ix) { -+ int index = ix; -+ result += index; // DexLabel('check') -+ } -+} -+ -+// DexExpectWatchValue('index', 2, 7, 9, on_line='check') -+// DexLimitSteps('ix', 2, 7, 9, on_line='check') -diff --git a/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_expect_loop.cpp b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_expect_loop.cpp -new file mode 100644 -index 00000000000..5946fa6ba46 ---- /dev/null -+++ b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_expect_loop.cpp -@@ -0,0 +1,20 @@ -+// Purpose: -+// Check the DexLimit steps only gathers step info for 2 iterations of a -+// for loop. -+// -+// REQUIRES: system-linux -+// -+// RUN: %dexter_regression_test -- %s | FileCheck %s -+// CHECK: limit_steps_expect_loop.cpp: -+ -+int main(const int argc, const char * argv[]) { -+ unsigned int sum = 1; -+ for(unsigned int ix = 0; ix != 5; ++ix) { -+ unsigned thing_to_add = ix + ix - ix; // DexLabel('start') -+ sum += ix; // DexLabel('end') -+ } -+ return sum; -+} -+ -+// DexLimitSteps('ix', 0, 3, from_line='start', to_line='end') -+// DexExpectWatchValue('ix', 0, 3, from_line='start', to_line='end') -diff --git a/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_expect_value.cpp b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_expect_value.cpp -new file mode 100644 -index 00000000000..2715e28d66b ---- /dev/null -+++ b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_expect_value.cpp -@@ -0,0 +1,18 @@ -+// Purpose: -+// Ensure that limited stepping breaks for all expected values. -+// -+// REQUIRES: system-linux -+// -+// RUN: %dexter_regression_test -- %s | FileCheck %s -+// CHECK: limit_steps_expect_value.cpp -+ -+int main() { -+ int i = 0; -+ i = 1; // DexLabel('from') -+ i = 2; -+ i = 3; -+ return 0; // DexLabel('long_range') -+} -+ -+// DexLimitSteps('i', '0', from_line='from', to_line='long_range') -+// DexExpectWatchValue('i', 0, 1, 2, 3, from_line='from', to_line='long_range') -diff --git a/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_overlapping_ranges.cpp b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_overlapping_ranges.cpp -new file mode 100644 -index 00000000000..3200fe0979b ---- /dev/null -+++ b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_overlapping_ranges.cpp -@@ -0,0 +1,36 @@ -+// Purpose: -+// Ensure that multiple overlapping \DexLimitSteps ranges do not interfere. -+// -+// REQUIRES: system-linux -+// -+// RUN: %dexter_regression_test -- %s | FileCheck %s -+// CHECK: limit_steps_overlapping_ranges.cpp -+ -+int main() { -+ int val1; -+ int val2; -+ int placeholder; -+ for (int ix = 0; ix != 10; ++ix) { -+ placeholder=val1+val2; // DexLabel('from') -+ if (ix == 0) { -+ val1 = ix; -+ val2 = ix; // DexLabel('val1_check') -+ placeholder=val1+val2; // DexLabel('val1_check_to') -+ } -+ else if (ix == 2) { -+ val2 = ix; -+ val1 = ix; // DexLabel('val2_check') -+ placeholder=val1+val2; // DexLabel('val2_check_to') -+ } -+ placeholder=val1+val2; // DexLabel('to') -+ } -+ return val1 + val2; -+} -+ -+// DexExpectWatchValue('ix', 0, 2, 5, from_line='from', to_line='to') -+// DexExpectWatchValue('val1', 0, from_line='val1_check', to_line='val1_check_to') -+// DexExpectWatchValue('val2', 2, from_line='val2_check', to_line='val2_check_to') -+ -+// DexLimitSteps('ix', 5, from_line='from', to_line='to') -+// DexLimitSteps('val1', 0, from_line='val1_check', to_line='val1_check_to') -+// DexLimitSteps('val2', 2, from_line='val2_check', to_line='val2_check_to') -diff --git a/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_same_line_conditional.cpp b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_same_line_conditional.cpp -new file mode 100644 -index 00000000000..060ff0d5fe7 ---- /dev/null -+++ b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_same_line_conditional.cpp -@@ -0,0 +1,26 @@ -+// Purpose: -+// Test that LimitStep commands can exist on the same from line. -+// -+// REQUIRES: system-linux -+// -+// RUN: %dexter_regression_test -- %s | FileCheck %s -+// CHECK: limit_steps_same_line_conditional.cpp -+ -+int main() { -+ int val1 = 0; -+ -+ int placeholder; -+ for(int ix = 0; ix != 4; ++ix) { -+ val1 = ix; -+ placeholder = ix; // DexLabel('from') -+ placeholder = ix; -+ val1 += 2; // DexLabel('to') -+ placeholder = ix; // DexLabel('extended_to') -+ } -+ return val1 + placeholder; -+} -+ -+// DexExpectWatchValue('val1', 0, 1, 3, from_line='from', to_line='extended_to') -+ -+// DexLimitSteps('ix', 0, from_line='from', to_line='to') -+// DexLimitSteps('ix', 1, from_line='from', to_line='extended_to') Index: llvm/include/llvm/ObjectYAML/ELFYAML.h =================================================================== --- llvm/include/llvm/ObjectYAML/ELFYAML.h +++ llvm/include/llvm/ObjectYAML/ELFYAML.h @@ -93,6 +93,7 @@ struct SectionHeaderTable { std::vector Sections; + Optional> Excluded; }; struct SectionName { Index: llvm/lib/ObjectYAML/ELFEmitter.cpp =================================================================== --- llvm/lib/ObjectYAML/ELFEmitter.cpp +++ llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -128,6 +128,8 @@ NameToIdxMap DynSymN2I; ELFYAML::Object &Doc; + StringSet<> ExcludedSectionHeaders; + uint64_t LocationCounter = 0; bool HasError = false; yaml::ErrorHandler ErrHandler; @@ -233,6 +235,8 @@ uint64_t alignToOffset(ContiguousBlobAccumulator &CBA, uint64_t Align, llvm::Optional Offset); + uint64_t getSectionNameOffset(StringRef Name); + public: static bool writeELF(raw_ostream &OS, ELFYAML::Object &Doc, yaml::ErrorHandler EH); @@ -353,7 +357,8 @@ if (Doc.Header.SHStrNdx) Header.e_shstrndx = *Doc.Header.SHStrNdx; - else if (!Doc.SectionHeaders || !Doc.SectionHeaders->Sections.empty()) + else if ((!Doc.SectionHeaders || !Doc.SectionHeaders->Sections.empty()) && + !ExcludedSectionHeaders.count(".shstrtab")) Header.e_shstrndx = SN2I.get(".shstrtab"); else Header.e_shstrndx = 0; @@ -400,18 +405,32 @@ template unsigned ELFState::toSectionIndex(StringRef S, StringRef LocSec, StringRef LocSym) { + assert(LocSec.empty() || LocSym.empty()); + unsigned Index; - if (SN2I.lookup(S, Index) || to_integer(S, Index)) + if (!SN2I.lookup(S, Index) && !to_integer(S, Index)) { + if (!LocSym.empty()) + reportError("unknown section referenced: '" + S + "' by YAML symbol '" + + LocSym + "'"); + else + reportError("unknown section referenced: '" + S + "' by YAML section '" + + LocSec + "'"); + return 0; + } + + if (!Doc.SectionHeaders || !Doc.SectionHeaders->Excluded) return Index; - assert(LocSec.empty() || LocSym.empty()); - if (!LocSym.empty()) - reportError("unknown section referenced: '" + S + "' by YAML symbol '" + - LocSym + "'"); - else - reportError("unknown section referenced: '" + S + "' by YAML section '" + - LocSec + "'"); - return 0; + assert(!Doc.SectionHeaders->Sections.empty()); + if (Index >= Doc.SectionHeaders->Sections.size()) { + if (LocSym.empty()) + reportError("unable to link '" + LocSec + "' to excluded section '" + S + + "'"); + else + reportError("excluded section referenced: '" + S + "' by symbol '" + + LocSym + "'"); + } + return Index; } template @@ -501,6 +520,15 @@ return S.substr(0, SuffixPos - 1); } +template +uint64_t ELFState::getSectionNameOffset(StringRef Name) { + // If a section is excluded from section headers, we do not save its name in + // the string table. + if (ExcludedSectionHeaders.count(Name)) + return 0; + return DotShStrtab.getOffset(Name); +} + template void ELFState::initSectionHeaders(std::vector &SHeaders, ContiguousBlobAccumulator &CBA) { @@ -534,7 +562,7 @@ "implicit sections should already have been handled above."); SHeader.sh_name = - DotShStrtab.getOffset(ELFYAML::dropUniqueSuffix(Sec->Name)); + getSectionNameOffset(ELFYAML::dropUniqueSuffix(Sec->Name)); SHeader.sh_type = Sec->Type; if (Sec->Flags) SHeader.sh_flags = *Sec->Flags; @@ -717,7 +745,7 @@ } zero(SHeader); - SHeader.sh_name = DotShStrtab.getOffset(IsStatic ? ".symtab" : ".dynsym"); + SHeader.sh_name = getSectionNameOffset(IsStatic ? ".symtab" : ".dynsym"); if (YAMLSec) SHeader.sh_type = YAMLSec->Type; @@ -734,10 +762,13 @@ // added implicitly and we should be able to leave the Link zeroed if // .dynstr is not defined. unsigned Link = 0; - if (IsStatic) - Link = SN2I.get(".strtab"); - else - SN2I.lookup(".dynstr", Link); + if (IsStatic) { + if (!ExcludedSectionHeaders.count(".strtab")) + Link = SN2I.get(".strtab"); + } else { + if (!ExcludedSectionHeaders.count(".dynstr")) + SN2I.lookup(".dynstr", Link); + } SHeader.sh_link = Link; } @@ -778,7 +809,7 @@ ContiguousBlobAccumulator &CBA, ELFYAML::Section *YAMLSec) { zero(SHeader); - SHeader.sh_name = DotShStrtab.getOffset(Name); + SHeader.sh_name = getSectionNameOffset(Name); SHeader.sh_type = YAMLSec ? YAMLSec->Type : ELF::SHT_STRTAB; SHeader.sh_addralign = YAMLSec ? (uint64_t)YAMLSec->AddressAlign : 1; @@ -831,7 +862,7 @@ ContiguousBlobAccumulator &CBA, ELFYAML::Section *YAMLSec) { zero(SHeader); - SHeader.sh_name = DotShStrtab.getOffset(ELFYAML::dropUniqueSuffix(Name)); + SHeader.sh_name = getSectionNameOffset(ELFYAML::dropUniqueSuffix(Name)); SHeader.sh_type = YAMLSec ? YAMLSec->Type : ELF::SHT_PROGBITS; SHeader.sh_addralign = YAMLSec ? (uint64_t)YAMLSec->AddressAlign : 1; SHeader.sh_offset = alignToOffset(CBA, SHeader.sh_addralign, @@ -865,9 +896,8 @@ else if (Name == ".debug_str") SHeader.sh_flags = ELF::SHF_MERGE | ELF::SHF_STRINGS; - unsigned Link = 0; - if (YAMLSec && !YAMLSec->Link.empty() && SN2I.lookup(YAMLSec->Link, Link)) - SHeader.sh_link = Link; + if (YAMLSec && !YAMLSec->Link.empty()) + SHeader.sh_link = toSectionIndex(YAMLSec->Link, Name); assignSectionAddress(SHeader, YAMLSec); } @@ -1020,7 +1050,8 @@ // For relocation section set link to .symtab by default. unsigned Link = 0; - if (Section.Link.empty() && SN2I.lookup(".symtab", Link)) + if (Section.Link.empty() && !ExcludedSectionHeaders.count(".symtab") && + SN2I.lookup(".symtab", Link)) SHeader.sh_link = Link; if (!Section.RelocatableSec.empty()) @@ -1093,7 +1124,8 @@ "Section type is not SHT_GROUP"); unsigned Link = 0; - if (Section.Link.empty() && SN2I.lookup(".symtab", Link)) + if (Section.Link.empty() && !ExcludedSectionHeaders.count(".symtab") && + SN2I.lookup(".symtab", Link)) SHeader.sh_link = Link; SHeader.sh_entsize = 4; @@ -1218,7 +1250,8 @@ SHeader.sh_entsize = 16; unsigned Link = 0; - if (Section.Link.empty() && SN2I.lookup(".symtab", Link)) + if (Section.Link.empty() && !ExcludedSectionHeaders.count(".symtab") && + SN2I.lookup(".symtab", Link)) SHeader.sh_link = Link; raw_ostream &OS = CBA.getOS(); @@ -1246,7 +1279,8 @@ const ELFYAML::HashSection &Section, ContiguousBlobAccumulator &CBA) { unsigned Link = 0; - if (Section.Link.empty() && SN2I.lookup(".dynsym", Link)) + if (Section.Link.empty() && !ExcludedSectionHeaders.count(".dynsym") && + SN2I.lookup(".dynsym", Link)) SHeader.sh_link = Link; raw_ostream &OS = CBA.getOS(); @@ -1436,7 +1470,8 @@ const ELFYAML::AddrsigSection &Section, ContiguousBlobAccumulator &CBA) { unsigned Link = 0; - if (Section.Link.empty() && SN2I.lookup(".symtab", Link)) + if (Section.Link.empty() && !ExcludedSectionHeaders.count(".symtab") && + SN2I.lookup(".symtab", Link)) SHeader.sh_link = Link; raw_ostream &OS = CBA.getOS(); @@ -1502,7 +1537,8 @@ const ELFYAML::GnuHashSection &Section, ContiguousBlobAccumulator &CBA) { unsigned Link = 0; - if (Section.Link.empty() && SN2I.lookup(".dynsym", Link)) + if (Section.Link.empty() && !ExcludedSectionHeaders.count(".dynsym") && + SN2I.lookup(".dynsym", Link)) SHeader.sh_link = Link; raw_ostream &OS = CBA.getOS(); @@ -1584,12 +1620,20 @@ DenseMap Ret; size_t SecNdx = 0; StringSet<> Seen; - for (const ELFYAML::SectionHeader &Hdr : Doc.SectionHeaders->Sections) { + + auto AddSection = [&](const ELFYAML::SectionHeader &Hdr) { if (!Ret.try_emplace(Hdr.Name, ++SecNdx).second) reportError("repeated section name: '" + Hdr.Name + "' in the section header description"); Seen.insert(Hdr.Name); - } + }; + + for (const ELFYAML::SectionHeader &Hdr : Doc.SectionHeaders->Sections) + AddSection(Hdr); + + if (Doc.SectionHeaders->Excluded) + for (const ELFYAML::SectionHeader &Hdr : *Doc.SectionHeaders->Excluded) + AddSection(Hdr); for (const ELFYAML::Section *S : Doc.getSections()) { // Ignore special first SHT_NULL section. @@ -1597,7 +1641,7 @@ continue; if (!Seen.count(S->Name)) reportError("section '" + S->Name + - "' should be present in the 'Sections' list"); + "' should be present in the 'Sections' or 'Excluded' lists"); Seen.erase(S->Name); } @@ -1607,21 +1651,36 @@ return Ret; } +static bool hasSectionHeader(const ELFYAML::Object &Doc, size_t SecNdx) { + if (!Doc.SectionHeaders) + return true; + return SecNdx < Doc.SectionHeaders->Sections.size(); +} + template void ELFState::buildSectionIndex() { - // A YAML description can have an explicit section header declaration that allows - // to change the order of section headers. + // A YAML description can have an explicit section header declaration that + // allows to change the order of section headers. DenseMap ReorderMap = buildSectionHeaderReorderMap(); + if (HasError) + return; + + // Build excluded section headers map. + if (Doc.SectionHeaders && Doc.SectionHeaders->Excluded) + for (const ELFYAML::SectionHeader &Hdr : *Doc.SectionHeaders->Excluded) + if (!ExcludedSectionHeaders.insert(Hdr.Name).second) + llvm_unreachable("buildSectionIndex() failed"); + size_t SecNdx = -1; - for (const std::unique_ptr &C : Doc.Chunks) { - if (!isa(C.get())) - continue; + for (const ELFYAML::Section *S : Doc.getSections()) { ++SecNdx; - size_t Index = ReorderMap.empty() ? SecNdx : ReorderMap.lookup(C->Name); - if (!SN2I.addName(C->Name, Index)) + size_t Index = ReorderMap.empty() ? SecNdx : ReorderMap.lookup(S->Name); + if (!SN2I.addName(S->Name, Index)) llvm_unreachable("buildSectionIndex() failed"); - DotShStrtab.add(ELFYAML::dropUniqueSuffix(C->Name)); + + if (!ExcludedSectionHeaders.count(S->Name)) + DotShStrtab.add(ELFYAML::dropUniqueSuffix(S->Name)); } DotShStrtab.finalize(); @@ -1691,6 +1750,9 @@ State.buildSectionIndex(); State.buildSymbolIndexes(); + if (State.HasError) + return false; + std::vector PHeaders; State.initProgramHeaders(PHeaders); Index: llvm/lib/ObjectYAML/ELFYAML.cpp =================================================================== --- llvm/lib/ObjectYAML/ELFYAML.cpp +++ llvm/lib/ObjectYAML/ELFYAML.cpp @@ -840,6 +840,7 @@ void MappingTraits::mapping( IO &IO, ELFYAML::SectionHeaderTable &SectionHeader) { IO.mapRequired("Sections", SectionHeader.Sections); + IO.mapOptional("Excluded", SectionHeader.Excluded); } void MappingTraits::mapping(IO &IO, Index: llvm/test/tools/yaml2obj/ELF/section-headers-exclude.yaml =================================================================== --- /dev/null +++ llvm/test/tools/yaml2obj/ELF/section-headers-exclude.yaml @@ -0,0 +1,478 @@ +## Check how we can use the "Excluded" key of the "SectionHeaderTable" tag to exclude +## entries from the section header table.. + +## Check we can use the "Excluded" key to omit a section from the section header table. +## Check we do not include the name of the excluded section in the string table. +# RUN: yaml2obj %s -DINCLUDED=.foo -DEXCLUDED=.bar --docnum=1 -o %t1 +# RUN: llvm-readelf --section-headers -p .shstrtab %t1 | \ +# RUN: FileCheck %s -DSEC=.foo --check-prefixes=INCLUDE-SEC,INCLUDE-FOO +# RUN: yaml2obj %s -DINCLUDED=.bar -DEXCLUDED=.foo --docnum=1 -o %t2 +# RUN: llvm-readelf --section-headers -p .shstrtab %t2 | \ +# RUN: FileCheck %s -DSEC=.bar --check-prefixes=INCLUDE-SEC,INCLUDE-BAR + +# INCLUDE-SEC: [Nr] Name +# INCLUDE-SEC: [ 1] [[SEC]] +# INCLUDE-SEC-NEXT: [ 2] .strtab +# INCLUDE-SEC-NEXT: [ 3] .shstrtab + +# INCLUDE-SEC: String dump of section '.shstrtab': +# INCLUDE-FOO-NEXT: [ 1] .foo +# INCLUDE-BAR-NEXT: [ 1] .bar +# INCLUDE-SEC-NEXT: [ 6] .shstrtab +# INCLUDE-SEC-NEXT: [ 10] .strtab +# INCLUDE-SEC-NOT: {{.}} + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS + - Name: .bar + Type: SHT_PROGBITS +SectionHeaderTable: + Sections: + - Name: [[INCLUDED]] + - Name: .strtab + - Name: .shstrtab + Excluded: + - Name: [[EXCLUDED]] + +## Check we report an error when a section is in both the "Sections" and "Excluded" lists at the same time. +## Also check that we report an error if a section is missing from the lists. +# RUN: not yaml2obj %s -DINCLUDED=.bar -DEXCLUDED=.strtab --docnum=1 -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=EXCLUDE-INCLUDED + +# EXCLUDE-INCLUDED: error: repeated section name: '.strtab' in the section header description +# EXCLUDE-INCLUDED: error: section '.foo' should be present in the 'Sections' or 'Excluded' lists + +## Check we report an error when the `Excluded` key mentions an unknown section. +# RUN: not yaml2obj %s -DINCLUDED=.bar -DEXCLUDED=.unknown --docnum=1 -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=EXCLUDE-UNKNOWN + +# EXCLUDE-UNKNOWN: error: section '.foo' should be present in the 'Sections' or 'Excluded' lists +# EXCLUDE-UNKNOWN: error: section header contains undefined section '.unknown' + +## Check we report an error when the `Excluded` key mentions a section more than once. +# RUN: not yaml2obj %s --docnum=2 -o /dev/null 2>&1 | FileCheck %s --check-prefix=EXCLUDE-TWICE + +# EXCLUDE-TWICE: error: repeated section name: '.strtab' in the section header description +# EXCLUDE-TWICE: error: repeated section name: '.strtab' in the section header description + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +SectionHeaderTable: + Sections: + - Name: .strtab + - Name: .shstrtab + Excluded: + - Name: .strtab + - Name: .strtab + +## Check we are able to exclude all sections. +# RUN: yaml2obj %s --docnum=3 -o %t3 +# RUN: llvm-readelf --section-headers %t3 | FileCheck %s --check-prefix=NO-SECTIONS + +# NO-SECTIONS: There are 0 section headers, starting at offset 0x0: + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +SectionHeaderTable: + Sections: [] + Excluded: + - Name: .strtab + +## Check how we handle cases when a section is excluded, but its section index is needed. +## The general rule is: when a section is explicitly linked with another section, which is +## excluded, then we report an error. In the case when it is linked implicitly with an excluded +## section, we use 0 as index value. + +## Case A: check we report an error when a regular section has a Link field which +## points to an excluded section. +# RUN: not yaml2obj %s --docnum=4 -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=LINK -DSEC=.foo -DTARGET=.bar + +# LINK: error: unable to link '[[SEC]]' to excluded section '[[TARGET]]' + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS + Link: .bar + - Name: .bar + Type: SHT_PROGBITS +SectionHeaderTable: + Sections: + - Name: .foo + - Name: .strtab + - Name: .shstrtab + Excluded: + - Name: .bar + +## Case B.1: check we report an error when a symbol table section has a Link field which +## points to an excluded section. +# RUN: not yaml2obj %s --docnum=5 -DNAME=.symtab -DTYPE=SHT_SYMTAB -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=LINK -DSEC=.symtab -DTARGET=.foo +# RUN: not yaml2obj %s --docnum=5 -DNAME=.dynsym -DTYPE=SHT_DYNSYM -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=LINK -DSEC=.dynsym -DTARGET=.foo + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: [[NAME]] + Type: [[TYPE]] + Link: .foo + - Name: .foo + Type: SHT_PROGBITS +SectionHeaderTable: + Sections: + - Name: [[NAME]] + - Name: .strtab + - Name: .shstrtab + Excluded: + - Name: .foo + +## Case B.2: check we do not link .dynsym with .dynstr implicitly when the latter is excluded. +# RUN: yaml2obj %s --docnum=6 -o %t4 +# RUN: llvm-readelf %t4 --section-headers | FileCheck %s --check-prefix=LINK-DYNSYM + +# LINK-DYNSYM: [Nr] Name Type Address Off Size ES Flg Lk +# LINK-DYNSYM: [ 1] .dynsym DYNSYM 0000000000000000 000040 000018 18 A 0 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .dynsym + Type: SHT_DYNSYM + - Name: .dynstr + Type: SHT_PROGBITS +SectionHeaderTable: + Sections: + - Name: .dynsym + - Name: .strtab + - Name: .shstrtab + Excluded: + - Name: .dynstr + +## Case B.3: check we do not link .symtab with .strtab implicitly when the latter is excluded. +# RUN: yaml2obj %s --docnum=7 -o %t5 +# RUN: llvm-readelf %t5 --section-headers | FileCheck %s --check-prefix=LINK-SYMTAB + +# LINK-SYMTAB: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# LINK-SYMTAB: [ 1] .symtab SYMTAB 0000000000000000 000040 000018 18 0 1 0 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .symtab + Type: SHT_SYMTAB + - Name: .strtab + Type: SHT_PROGBITS +SectionHeaderTable: + Sections: + - Name: .symtab + - Name: .shstrtab + Excluded: + - Name: .strtab + +## Case C: check we report an error when a debug section has a Link field which +## points to an excluded section. +# RUN: not yaml2obj %s --docnum=8 -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=LINK -DSEC=.debug_unknown -DTARGET=.strtab + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .debug_unknown + Type: SHT_PROGBITS + Link: .strtab +SectionHeaderTable: + Sections: + - Name: .debug_unknown + - Name: .shstrtab + Excluded: + - Name: .strtab + +## Case D.1: check we report an error when a relocatable section has an Info field which +## points to an excluded section. +# RUN: not yaml2obj %s --docnum=9 -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=LINK -DSEC=.rela -DTARGET=.strtab + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .rela + Type: SHT_RELA + Info: .strtab + Relocations: [] +SectionHeaderTable: + Sections: + - Name: .rela + - Name: .shstrtab + Excluded: + - Name: .strtab + +## Case D.2: check we report an error when the SHT_REL[A] section is linked +## with an excluded section explicitly. +# RUN: not yaml2obj %s --docnum=10 -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=LINK -DSEC=.rela -DTARGET=.symtab + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .rela + Type: SHT_RELA + Link: .symtab + Relocations: [] + - Name: .symtab + Type: SHT_PROGBITS +SectionHeaderTable: + Sections: + - Name: .rela + - Name: .strtab + - Name: .shstrtab + Excluded: + - Name: .symtab + +## Case E: check we report an error when a symbol references an excluded section. +# RUN: not yaml2obj %s --docnum=11 -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=SYMBOL-SECTION + +# SYMBOL-SECTION: error: excluded section referenced: '.foo' by symbol 'foo' + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS +Symbols: + - Name: foo + Type: STT_OBJECT + Section: .foo +SectionHeaderTable: + Sections: + - Name: .symtab + - Name: .strtab + - Name: .shstrtab + Excluded: + - Name: .foo + +## Case F.1: check we report an error when a group section +## contains an excluded section member. +# RUN: not yaml2obj %s --docnum=12 -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=LINK -DSEC=.group -DTARGET=.strtab + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .group + Type: SHT_GROUP + Members: + - SectionOrType: .strtab +SectionHeaderTable: + Sections: + - Name: .group + - Name: .shstrtab + Excluded: + - Name: .strtab + +## Case F.2: check we report an error when the group section is linked +## with an excluded section explicitly. +# RUN: not yaml2obj %s --docnum=13 -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=LINK -DSEC=.group -DTARGET=.symtab + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .group + Type: SHT_GROUP + Link: .symtab + Members: [] + - Name: .symtab + Type: SHT_SYMTAB +SectionHeaderTable: + Sections: + - Name: .group + - Name: .strtab + - Name: .shstrtab + Excluded: + - Name: .symtab + +## Case G: check we do not link SHT_LLVM_CALL_GRAPH_PROFILE/SHT_LLVM_ADDRSIG/SHT_GROUP/SHT_REL[A] sections +## with .symtab implicitly when the latter is excluded. +# RUN: yaml2obj %s --docnum=14 -o %t6 +# RUN: llvm-readelf %t6 --section-headers | FileCheck %s --check-prefix=LINK-IMPLICIT + +# LINK-IMPLICIT: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# LINK-IMPLICIT: [ 1] .cgp LLVM_CALL_GRAPH_PROFILE 0000000000000000 000040 000000 10 0 0 0 +# LINK-IMPLICIT-NEXT: [ 2] .llvm_addrsig LLVM_ADDRSIG 0000000000000000 000040 000000 00 0 0 0 +# LINK-IMPLICIT-NEXT: [ 3] .group GROUP 0000000000000000 000040 000000 04 0 0 0 +# LINK-IMPLICIT-NEXT: [ 4] .rela RELA 0000000000000000 000040 000000 18 0 0 0 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .cgp + Type: SHT_LLVM_CALL_GRAPH_PROFILE + Content: "" + - Name: .llvm_addrsig + Type: SHT_LLVM_ADDRSIG + Content: "" + - Name: .group + Type: SHT_GROUP + Members: [] + - Name: .rela + Type: SHT_RELA + Relocations: [] + - Name: .symtab + Type: SHT_SYMTAB +SectionHeaderTable: + Sections: + - Name: .cgp + - Name: .llvm_addrsig + - Name: .group + - Name: .rela + - Name: .strtab + - Name: .shstrtab + Excluded: + - Name: .symtab + +## Case H: check we do not link SHT_HASH/SHT_GNU_HASH sections with .dynsym +## implicitly when the latter is excluded. +# RUN: yaml2obj %s --docnum=15 -o %t7 +# RUN: llvm-readelf %t7 --section-headers | FileCheck %s --check-prefix=LINK-HASH + +# LINK-HASH: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# LINK-HASH: [ 1] .hash HASH 0000000000000000 000040 000000 00 0 0 0 +# LINK-HASH-NEXT: [ 2] .gnu_hash GNU_HASH 0000000000000000 000040 000000 00 0 0 0 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .hash + Type: SHT_HASH + Content: "" + - Name: .gnu_hash + Type: SHT_GNU_HASH + Content: "" + - Name: .dynsym + Type: SHT_DYNSYM +SectionHeaderTable: + Sections: + - Name: .hash + - Name: .gnu_hash + - Name: .strtab + - Name: .shstrtab + Excluded: + - Name: .dynsym + +## Case I: document the case when an excluded section is explicitly linked to another excluded section. +## We report an error in this case, because: +## 1) It is a reasonable behavior, as it is perhaps usually a result of a mistake +## in a YAML description. +## 2) Helps to keep the code simpler. +# RUN: not yaml2obj %s --docnum=16 -o /dev/null 2>&1 | FileCheck %s --check-prefix=CROSS-LINK + +# CROSS-LINK: error: unable to link '.foo' to excluded section '.bar' +# CROSS-LINK-NEXT: error: unable to link '.bar' to excluded section '.foo' + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS + Link: .bar + - Name: .bar + Type: SHT_PROGBITS + Link: .foo +SectionHeaderTable: + Sections: + - Name: .strtab + - Name: .shstrtab + Excluded: + - Name: .foo + - Name: .bar + +## Check we set e_shstrndx field to 0 when the section header string table is excluded. +## Check that the e_shnum field is adjusted properly when a section is removed. +# RUN: yaml2obj --docnum=17 %s -o %t9 +# RUN: llvm-readelf --file-headers %t9 | FileCheck %s --check-prefix=SHSTRTAB + +# SHSTRTAB: Number of section headers: 2 +# SHSTRTAB: Section header string table index: 0 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +SectionHeaderTable: + Sections: + - Name: .strtab + Excluded: + - Name: .shstrtab Index: llvm/test/tools/yaml2obj/ELF/section-headers.yaml =================================================================== --- llvm/test/tools/yaml2obj/ELF/section-headers.yaml +++ llvm/test/tools/yaml2obj/ELF/section-headers.yaml @@ -68,15 +68,15 @@ # RUN: FileCheck %s --check-prefix=ERR2 # ERR1: error: repeated section name: '.section.foo' in the section header description -# ERR1-NEXT: error: section '.section (1)' should be present in the 'Sections' list -# ERR1-NEXT: error: section '.section (2)' should be present in the 'Sections' list +# ERR1-NEXT: error: section '.section (1)' should be present in the 'Sections' or `Excluded` lists +# ERR1-NEXT: error: section '.section (2)' should be present in the 'Sections' or `Excluded` lists # ERR1-NEXT: error: section header contains undefined section 'unknown' # ERR2: error: repeated section name: '.strtab' in the section header description # ERR2-NEXT: error: repeated section name: '.shstrtab' in the section header description -# ERR2-NEXT: error: section '.section (1)' should be present in the 'Sections' list -# ERR2-NEXT: error: section '.section (2)' should be present in the 'Sections' list -# ERR2-NEXT: error: section '.section.foo' should be present in the 'Sections' list +# ERR2-NEXT: error: section '.section (1)' should be present in the 'Sections' or `Excluded` lists +# ERR2-NEXT: error: section '.section (2)' should be present in the 'Sections' or `Excluded` lists +# ERR2-NEXT: error: section '.section.foo' should be present in the 'Sections' or `Excluded` lists # ERR2-NEXT: error: section header contains undefined section '.filler' ## Test that we are able to specify an empty sections list for