Skip to content

Commit

Permalink
Let arithmetic overflows wrap when executing line programs
Browse files Browse the repository at this point in the history
The DWARF spec isn't clear on how to handle overflow, but there is a small
chance some random toolchain in the wild relies on overflow to do hacky
subtraction of the current row's address. And wrapping is faster than checking
for overflow and returning an error anyways. So we just do wrapping.

/me shrugs

Fixes gimli-rs#506
  • Loading branch information
fitzgen committed May 18, 2020
1 parent 895fb64 commit 2b23089
Showing 1 changed file with 75 additions and 40 deletions.
115 changes: 75 additions & 40 deletions src/read/line.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use alloc::vec::Vec;
use core::fmt;
use core::num::Wrapping;
use core::result;

use crate::common::{
Expand Down Expand Up @@ -634,10 +635,10 @@ pub type LineNumberRow = LineRow;
/// Each row is a copy of the registers of the state machine, as defined in section 6.2.2.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct LineRow {
address: u64,
op_index: u64,
address: Wrapping<u64>,
op_index: Wrapping<u64>,
file: u64,
line: u64,
line: Wrapping<u64>,
column: u64,
is_stmt: bool,
basic_block: bool,
Expand All @@ -654,10 +655,10 @@ impl LineRow {
LineRow {
// "At the beginning of each sequence within a line number program, the
// state of the registers is:" -- Section 6.2.2
address: 0,
op_index: 0,
address: Wrapping(0),
op_index: Wrapping(0),
file: 1,
line: 1,
line: Wrapping(1),
column: 0,
// "determined by default_is_stmt in the line number program header"
is_stmt: header.line_encoding.default_is_stmt,
Expand All @@ -678,7 +679,7 @@ impl LineRow {
/// generated by the compiler."
#[inline]
pub fn address(&self) -> u64 {
self.address
self.address.0
}

/// > An unsigned integer representing the index of an operation within a VLIW
Expand All @@ -690,7 +691,7 @@ impl LineRow {
/// > instruction stream.
#[inline]
pub fn op_index(&self) -> u64 {
self.op_index
self.op_index.0
}

/// "An unsigned integer indicating the identity of the source file
Expand All @@ -714,10 +715,10 @@ impl LineRow {
/// instruction cannot be attributed to any source line."
#[inline]
pub fn line(&self) -> Option<u64> {
if self.line == 0 {
if self.line.0 == 0 {
None
} else {
Some(self.line)
Some(self.line.0)
}
}

Expand Down Expand Up @@ -858,8 +859,8 @@ impl LineRow {
}

LineInstruction::FixedAddPc(operand) => {
self.address += u64::from(operand);
self.op_index = 0;
self.address += Wrapping(u64::from(operand));
self.op_index.0 = 0;
false
}

Expand All @@ -884,8 +885,8 @@ impl LineRow {
}

LineInstruction::SetAddress(address) => {
self.address = address;
self.op_index = 0;
self.address.0 = address;
self.op_index.0 = 0;
false
}

Expand Down Expand Up @@ -930,13 +931,13 @@ impl LineRow {
fn apply_line_advance(&mut self, line_increment: i64) {
if line_increment < 0 {
let decrement = -line_increment as u64;
if decrement <= self.line {
self.line -= decrement;
if decrement <= self.line.0 {
self.line.0 -= decrement;
} else {
self.line = 0;
self.line.0 = 0;
}
} else {
self.line += line_increment as u64;
self.line += Wrapping(line_increment as u64);
}
}

Expand All @@ -946,13 +947,18 @@ impl LineRow {
operation_advance: u64,
header: &LineProgramHeader<R>,
) {
let operation_advance = Wrapping(operation_advance);

let minimum_instruction_length = u64::from(header.line_encoding.minimum_instruction_length);
let minimum_instruction_length = Wrapping(minimum_instruction_length);

let maximum_operations_per_instruction =
u64::from(header.line_encoding.maximum_operations_per_instruction);
let maximum_operations_per_instruction = Wrapping(maximum_operations_per_instruction);

if maximum_operations_per_instruction == 1 {
if maximum_operations_per_instruction.0 == 1 {
self.address += minimum_instruction_length * operation_advance;
self.op_index = 0;
self.op_index.0 = 0;
} else {
let op_index_with_advance = self.op_index + operation_advance;
self.address += minimum_instruction_length
Expand Down Expand Up @@ -1886,6 +1892,7 @@ mod tests {
use crate::endianity::LittleEndian;
use crate::read::{EndianSlice, Error};
use crate::test_util::GimliSectionMethods;
use core::u64;
use core::u8;
use test_assembler::{Endian, Label, LabelMaker, Section};

Expand Down Expand Up @@ -2470,12 +2477,12 @@ mod tests {
let header = make_test_header(EndianSlice::new(&[], LittleEndian));

let mut initial_registers = LineRow::new(&header);
initial_registers.line = 10;
initial_registers.line.0 = 10;

let opcode = LineInstruction::Special(13);

let mut expected_registers = initial_registers;
expected_registers.line -= 3;
expected_registers.line.0 -= 3;

assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
}
Expand All @@ -2489,7 +2496,7 @@ mod tests {
let opcode = LineInstruction::Special(19);

let mut expected_registers = initial_registers;
expected_registers.line += 3;
expected_registers.line.0 += 3;

assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
}
Expand All @@ -2503,7 +2510,7 @@ mod tests {
let opcode = LineInstruction::Special(52);

let mut expected_registers = initial_registers;
expected_registers.address += 3;
expected_registers.address.0 += 3;

assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
}
Expand All @@ -2517,8 +2524,8 @@ mod tests {
let opcode = LineInstruction::Special(55);

let mut expected_registers = initial_registers;
expected_registers.address += 3;
expected_registers.line += 3;
expected_registers.address.0 += 3;
expected_registers.line.0 += 3;

assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
}
Expand All @@ -2528,13 +2535,13 @@ mod tests {
let header = make_test_header(EndianSlice::new(&[], LittleEndian));

let mut initial_registers = LineRow::new(&header);
initial_registers.line = 10;
initial_registers.line.0 = 10;

let opcode = LineInstruction::Special(49);

let mut expected_registers = initial_registers;
expected_registers.address += 3;
expected_registers.line -= 3;
expected_registers.address.0 += 3;
expected_registers.line.0 -= 3;

assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
}
Expand All @@ -2544,15 +2551,15 @@ mod tests {
let header = make_test_header(EndianSlice::new(&[], LittleEndian));

let mut initial_registers = LineRow::new(&header);
initial_registers.line = 2;
initial_registers.line.0 = 2;

// -3 line advance.
let opcode = LineInstruction::Special(13);

let mut expected_registers = initial_registers;
// Clamp at 0. No idea if this is the best way to handle this situation
// or not...
expected_registers.line = 0;
expected_registers.line.0 = 0;

assert_exec_opcode(header, initial_registers, opcode, expected_registers, true);
}
Expand All @@ -2562,8 +2569,8 @@ mod tests {
let header = make_test_header(EndianSlice::new(&[], LittleEndian));

let mut initial_registers = LineRow::new(&header);
initial_registers.address = 1337;
initial_registers.line = 42;
initial_registers.address.0 = 1337;
initial_registers.line.0 = 42;

let opcode = LineInstruction::Copy;

Expand All @@ -2579,7 +2586,21 @@ mod tests {
let opcode = LineInstruction::AdvancePc(42);

let mut expected_registers = initial_registers;
expected_registers.address += 42;
expected_registers.address.0 += 42;

assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
}

#[test]
fn test_exec_advance_pc_overflow() {
let header = make_test_header(EndianSlice::new(&[], LittleEndian));
let opcode = LineInstruction::AdvancePc(42);

let mut initial_registers = LineRow::new(&header);
initial_registers.address.0 = u64::MAX;

let mut expected_registers = initial_registers;
expected_registers.address.0 = 41;

assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
}
Expand All @@ -2591,7 +2612,21 @@ mod tests {
let opcode = LineInstruction::AdvanceLine(42);

let mut expected_registers = initial_registers;
expected_registers.line += 42;
expected_registers.line.0 += 42;

assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
}

#[test]
fn test_exec_advance_line_overflow() {
let header = make_test_header(EndianSlice::new(&[], LittleEndian));
let opcode = LineInstruction::AdvanceLine(42);

let mut initial_registers = LineRow::new(&header);
initial_registers.line.0 = u64::MAX;

let mut expected_registers = initial_registers;
expected_registers.line.0 = 41;

assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
}
Expand Down Expand Up @@ -2700,7 +2735,7 @@ mod tests {
let opcode = LineInstruction::ConstAddPc;

let mut expected_registers = initial_registers;
expected_registers.address += 20;
expected_registers.address.0 += 20;

assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
}
Expand All @@ -2710,13 +2745,13 @@ mod tests {
let header = make_test_header(EndianSlice::new(&[], LittleEndian));

let mut initial_registers = LineRow::new(&header);
initial_registers.op_index = 1;
initial_registers.op_index.0 = 1;

let opcode = LineInstruction::FixedAddPc(10);

let mut expected_registers = initial_registers;
expected_registers.address += 10;
expected_registers.op_index = 0;
expected_registers.address.0 += 10;
expected_registers.op_index.0 = 0;

assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
}
Expand Down Expand Up @@ -2797,7 +2832,7 @@ mod tests {
let opcode = LineInstruction::SetAddress(3030);

let mut expected_registers = initial_registers;
expected_registers.address = 3030;
expected_registers.address.0 = 3030;

assert_exec_opcode(header, initial_registers, opcode, expected_registers, false);
}
Expand Down

0 comments on commit 2b23089

Please sign in to comment.