| 1 | #[cfg (feature = "read" )] |
| 2 | use alloc::borrow::Cow; |
| 3 | use core::convert::TryInto; |
| 4 | use core::fmt::Debug; |
| 5 | use core::hash::Hash; |
| 6 | use core::ops::{Add, AddAssign, Sub}; |
| 7 | |
| 8 | use crate::common::Format; |
| 9 | use crate::endianity::Endianity; |
| 10 | use crate::leb128; |
| 11 | use crate::read::{Error, Result}; |
| 12 | |
| 13 | /// An identifier for an offset within a section reader. |
| 14 | /// |
| 15 | /// This is used for error reporting. The meaning of this value is specific to |
| 16 | /// each reader implementation. The values should be chosen to be unique amongst |
| 17 | /// all readers. If values are not unique then errors may point to the wrong reader. |
| 18 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
| 19 | pub struct ReaderOffsetId(pub u64); |
| 20 | |
| 21 | /// A trait for offsets with a DWARF section. |
| 22 | /// |
| 23 | /// This allows consumers to choose a size that is appropriate for their address space. |
| 24 | pub trait ReaderOffset: |
| 25 | Debug + Copy + Eq + Ord + Hash + Add<Output = Self> + AddAssign + Sub<Output = Self> |
| 26 | { |
| 27 | /// Convert a u8 to an offset. |
| 28 | fn from_u8(offset: u8) -> Self; |
| 29 | |
| 30 | /// Convert a u16 to an offset. |
| 31 | fn from_u16(offset: u16) -> Self; |
| 32 | |
| 33 | /// Convert an i16 to an offset. |
| 34 | fn from_i16(offset: i16) -> Self; |
| 35 | |
| 36 | /// Convert a u32 to an offset. |
| 37 | fn from_u32(offset: u32) -> Self; |
| 38 | |
| 39 | /// Convert a u64 to an offset. |
| 40 | /// |
| 41 | /// Returns `Error::UnsupportedOffset` if the value is too large. |
| 42 | fn from_u64(offset: u64) -> Result<Self>; |
| 43 | |
| 44 | /// Convert an offset to a u64. |
| 45 | fn into_u64(self) -> u64; |
| 46 | |
| 47 | /// Wrapping (modular) addition. Computes `self + other`. |
| 48 | fn wrapping_add(self, other: Self) -> Self; |
| 49 | |
| 50 | /// Checked subtraction. Computes `self - other`. |
| 51 | fn checked_sub(self, other: Self) -> Option<Self>; |
| 52 | } |
| 53 | |
| 54 | impl ReaderOffset for u64 { |
| 55 | #[inline ] |
| 56 | fn from_u8(offset: u8) -> Self { |
| 57 | u64::from(offset) |
| 58 | } |
| 59 | |
| 60 | #[inline ] |
| 61 | fn from_u16(offset: u16) -> Self { |
| 62 | u64::from(offset) |
| 63 | } |
| 64 | |
| 65 | #[inline ] |
| 66 | fn from_i16(offset: i16) -> Self { |
| 67 | offset as u64 |
| 68 | } |
| 69 | |
| 70 | #[inline ] |
| 71 | fn from_u32(offset: u32) -> Self { |
| 72 | u64::from(offset) |
| 73 | } |
| 74 | |
| 75 | #[inline ] |
| 76 | fn from_u64(offset: u64) -> Result<Self> { |
| 77 | Ok(offset) |
| 78 | } |
| 79 | |
| 80 | #[inline ] |
| 81 | fn into_u64(self) -> u64 { |
| 82 | self |
| 83 | } |
| 84 | |
| 85 | #[inline ] |
| 86 | fn wrapping_add(self, other: Self) -> Self { |
| 87 | self.wrapping_add(other) |
| 88 | } |
| 89 | |
| 90 | #[inline ] |
| 91 | fn checked_sub(self, other: Self) -> Option<Self> { |
| 92 | self.checked_sub(other) |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | impl ReaderOffset for u32 { |
| 97 | #[inline ] |
| 98 | fn from_u8(offset: u8) -> Self { |
| 99 | u32::from(offset) |
| 100 | } |
| 101 | |
| 102 | #[inline ] |
| 103 | fn from_u16(offset: u16) -> Self { |
| 104 | u32::from(offset) |
| 105 | } |
| 106 | |
| 107 | #[inline ] |
| 108 | fn from_i16(offset: i16) -> Self { |
| 109 | offset as u32 |
| 110 | } |
| 111 | |
| 112 | #[inline ] |
| 113 | fn from_u32(offset: u32) -> Self { |
| 114 | offset |
| 115 | } |
| 116 | |
| 117 | #[inline ] |
| 118 | fn from_u64(offset64: u64) -> Result<Self> { |
| 119 | let offset = offset64 as u32; |
| 120 | if u64::from(offset) == offset64 { |
| 121 | Ok(offset) |
| 122 | } else { |
| 123 | Err(Error::UnsupportedOffset) |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | #[inline ] |
| 128 | fn into_u64(self) -> u64 { |
| 129 | u64::from(self) |
| 130 | } |
| 131 | |
| 132 | #[inline ] |
| 133 | fn wrapping_add(self, other: Self) -> Self { |
| 134 | self.wrapping_add(other) |
| 135 | } |
| 136 | |
| 137 | #[inline ] |
| 138 | fn checked_sub(self, other: Self) -> Option<Self> { |
| 139 | self.checked_sub(other) |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | impl ReaderOffset for usize { |
| 144 | #[inline ] |
| 145 | fn from_u8(offset: u8) -> Self { |
| 146 | offset as usize |
| 147 | } |
| 148 | |
| 149 | #[inline ] |
| 150 | fn from_u16(offset: u16) -> Self { |
| 151 | offset as usize |
| 152 | } |
| 153 | |
| 154 | #[inline ] |
| 155 | fn from_i16(offset: i16) -> Self { |
| 156 | offset as usize |
| 157 | } |
| 158 | |
| 159 | #[inline ] |
| 160 | fn from_u32(offset: u32) -> Self { |
| 161 | offset as usize |
| 162 | } |
| 163 | |
| 164 | #[inline ] |
| 165 | fn from_u64(offset64: u64) -> Result<Self> { |
| 166 | let offset = offset64 as usize; |
| 167 | if offset as u64 == offset64 { |
| 168 | Ok(offset) |
| 169 | } else { |
| 170 | Err(Error::UnsupportedOffset) |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | #[inline ] |
| 175 | fn into_u64(self) -> u64 { |
| 176 | self as u64 |
| 177 | } |
| 178 | |
| 179 | #[inline ] |
| 180 | fn wrapping_add(self, other: Self) -> Self { |
| 181 | self.wrapping_add(other) |
| 182 | } |
| 183 | |
| 184 | #[inline ] |
| 185 | fn checked_sub(self, other: Self) -> Option<Self> { |
| 186 | self.checked_sub(other) |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | /// A trait for addresses within a DWARF section. |
| 191 | /// |
| 192 | /// Currently this is a simple extension trait for `u64`, but it may be expanded |
| 193 | /// in the future to support user-defined address types. |
| 194 | pub(crate) trait ReaderAddress: Sized { |
| 195 | /// Add a length to an address of the given size. |
| 196 | /// |
| 197 | /// Returns an error for overflow. |
| 198 | fn add_sized(self, length: u64, size: u8) -> Result<Self>; |
| 199 | |
| 200 | /// Add a length to an address of the given size. |
| 201 | /// |
| 202 | /// Wraps the result to the size of the address to allow for the possibility |
| 203 | /// that the length is a negative value. |
| 204 | fn wrapping_add_sized(self, length: u64, size: u8) -> Self; |
| 205 | |
| 206 | /// The all-ones value of an address of the given size. |
| 207 | fn ones_sized(size: u8) -> Self; |
| 208 | } |
| 209 | |
| 210 | impl ReaderAddress for u64 { |
| 211 | #[inline ] |
| 212 | fn add_sized(self, length: u64, size: u8) -> Result<Self> { |
| 213 | let address: u64 = self.checked_add(length).ok_or(err:Error::AddressOverflow)?; |
| 214 | let mask: u64 = Self::ones_sized(size); |
| 215 | if address & !mask != 0 { |
| 216 | return Err(Error::AddressOverflow); |
| 217 | } |
| 218 | Ok(address) |
| 219 | } |
| 220 | |
| 221 | #[inline ] |
| 222 | fn wrapping_add_sized(self, length: u64, size: u8) -> Self { |
| 223 | let mask: u64 = Self::ones_sized(size); |
| 224 | self.wrapping_add(length) & mask |
| 225 | } |
| 226 | |
| 227 | #[inline ] |
| 228 | fn ones_sized(size: u8) -> Self { |
| 229 | !0 >> (64 - size * 8) |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | #[cfg (not(feature = "read" ))] |
| 234 | pub(crate) mod seal_if_no_alloc { |
| 235 | #[derive (Debug)] |
| 236 | pub struct Sealed; |
| 237 | } |
| 238 | |
| 239 | /// A trait for reading the data from a DWARF section. |
| 240 | /// |
| 241 | /// All read operations advance the section offset of the reader |
| 242 | /// unless specified otherwise. |
| 243 | /// |
| 244 | /// ## Choosing a `Reader` Implementation |
| 245 | /// |
| 246 | /// `gimli` comes with a few different `Reader` implementations and lets you |
| 247 | /// choose the one that is right for your use case. A `Reader` is essentially a |
| 248 | /// view into the raw bytes that make up some DWARF, but this view might borrow |
| 249 | /// the underlying data or use reference counting ownership, and it might be |
| 250 | /// thread safe or not. |
| 251 | /// |
| 252 | /// | Implementation | Ownership | Thread Safe | Notes | |
| 253 | /// |:------------------|:------------------|:------------|:------| |
| 254 | /// | [`EndianSlice`](./struct.EndianSlice.html) | Borrowed | Yes | Fastest, but requires that all of your code work with borrows. | |
| 255 | /// | [`EndianRcSlice`](./struct.EndianRcSlice.html) | Reference counted | No | Shared ownership via reference counting, which alleviates the borrow restrictions of `EndianSlice` but imposes reference counting increments and decrements. Cannot be sent across threads, because the reference count is not atomic. | |
| 256 | /// | [`EndianArcSlice`](./struct.EndianArcSlice.html) | Reference counted | Yes | The same as `EndianRcSlice`, but uses atomic reference counting, and therefore reference counting operations are slower but `EndianArcSlice`s may be sent across threads. | |
| 257 | /// | [`EndianReader<T>`](./struct.EndianReader.html) | Same as `T` | Same as `T` | Escape hatch for easily defining your own type of `Reader`. | |
| 258 | pub trait Reader: Debug + Clone { |
| 259 | /// The endianity of bytes that are read. |
| 260 | type Endian: Endianity; |
| 261 | |
| 262 | /// The type used for offsets and lengths. |
| 263 | type Offset: ReaderOffset; |
| 264 | |
| 265 | /// Return the endianity of bytes that are read. |
| 266 | fn endian(&self) -> Self::Endian; |
| 267 | |
| 268 | /// Return the number of bytes remaining. |
| 269 | fn len(&self) -> Self::Offset; |
| 270 | |
| 271 | /// Set the number of bytes remaining to zero. |
| 272 | fn empty(&mut self); |
| 273 | |
| 274 | /// Set the number of bytes remaining to the specified length. |
| 275 | fn truncate(&mut self, len: Self::Offset) -> Result<()>; |
| 276 | |
| 277 | /// Return the offset of this reader's data relative to the start of |
| 278 | /// the given base reader's data. |
| 279 | /// |
| 280 | /// May panic if this reader's data is not contained within the given |
| 281 | /// base reader's data. |
| 282 | fn offset_from(&self, base: &Self) -> Self::Offset; |
| 283 | |
| 284 | /// Return an identifier for the current reader offset. |
| 285 | fn offset_id(&self) -> ReaderOffsetId; |
| 286 | |
| 287 | /// Return the offset corresponding to the given `id` if |
| 288 | /// it is associated with this reader. |
| 289 | fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<Self::Offset>; |
| 290 | |
| 291 | /// Find the index of the first occurrence of the given byte. |
| 292 | /// The offset of the reader is not changed. |
| 293 | fn find(&self, byte: u8) -> Result<Self::Offset>; |
| 294 | |
| 295 | /// Discard the specified number of bytes. |
| 296 | fn skip(&mut self, len: Self::Offset) -> Result<()>; |
| 297 | |
| 298 | /// Split a reader in two. |
| 299 | /// |
| 300 | /// A new reader is returned that can be used to read the next |
| 301 | /// `len` bytes, and `self` is advanced so that it reads the remainder. |
| 302 | fn split(&mut self, len: Self::Offset) -> Result<Self>; |
| 303 | |
| 304 | /// This trait cannot be implemented if "read" feature is not enabled. |
| 305 | /// |
| 306 | /// `Reader` trait has a few methods that depend on `alloc` crate. |
| 307 | /// Disallowing `Reader` trait implementation prevents a crate that only depends on |
| 308 | /// "read-core" from being broken if another crate depending on `gimli` enables |
| 309 | /// "read" feature. |
| 310 | #[cfg (not(feature = "read" ))] |
| 311 | fn cannot_implement() -> seal_if_no_alloc::Sealed; |
| 312 | |
| 313 | /// Return all remaining data as a clone-on-write slice. |
| 314 | /// |
| 315 | /// The slice will be borrowed where possible, but some readers may |
| 316 | /// always return an owned vector. |
| 317 | /// |
| 318 | /// Does not advance the reader. |
| 319 | #[cfg (feature = "read" )] |
| 320 | fn to_slice(&self) -> Result<Cow<'_, [u8]>>; |
| 321 | |
| 322 | /// Convert all remaining data to a clone-on-write string. |
| 323 | /// |
| 324 | /// The string will be borrowed where possible, but some readers may |
| 325 | /// always return an owned string. |
| 326 | /// |
| 327 | /// Does not advance the reader. |
| 328 | /// |
| 329 | /// Returns an error if the data contains invalid characters. |
| 330 | #[cfg (feature = "read" )] |
| 331 | fn to_string(&self) -> Result<Cow<'_, str>>; |
| 332 | |
| 333 | /// Convert all remaining data to a clone-on-write string, including invalid characters. |
| 334 | /// |
| 335 | /// The string will be borrowed where possible, but some readers may |
| 336 | /// always return an owned string. |
| 337 | /// |
| 338 | /// Does not advance the reader. |
| 339 | #[cfg (feature = "read" )] |
| 340 | fn to_string_lossy(&self) -> Result<Cow<'_, str>>; |
| 341 | |
| 342 | /// Read exactly `buf.len()` bytes into `buf`. |
| 343 | fn read_slice(&mut self, buf: &mut [u8]) -> Result<()>; |
| 344 | |
| 345 | /// Read a u8 array. |
| 346 | #[inline ] |
| 347 | fn read_u8_array<A>(&mut self) -> Result<A> |
| 348 | where |
| 349 | A: Sized + Default + AsMut<[u8]>, |
| 350 | { |
| 351 | let mut val = Default::default(); |
| 352 | self.read_slice(<A as AsMut<[u8]>>::as_mut(&mut val))?; |
| 353 | Ok(val) |
| 354 | } |
| 355 | |
| 356 | /// Return true if the number of bytes remaining is zero. |
| 357 | #[inline ] |
| 358 | fn is_empty(&self) -> bool { |
| 359 | self.len() == Self::Offset::from_u8(0) |
| 360 | } |
| 361 | |
| 362 | /// Read a u8. |
| 363 | #[inline ] |
| 364 | fn read_u8(&mut self) -> Result<u8> { |
| 365 | let a: [u8; 1] = self.read_u8_array()?; |
| 366 | Ok(a[0]) |
| 367 | } |
| 368 | |
| 369 | /// Read an i8. |
| 370 | #[inline ] |
| 371 | fn read_i8(&mut self) -> Result<i8> { |
| 372 | let a: [u8; 1] = self.read_u8_array()?; |
| 373 | Ok(a[0] as i8) |
| 374 | } |
| 375 | |
| 376 | /// Read a u16. |
| 377 | #[inline ] |
| 378 | fn read_u16(&mut self) -> Result<u16> { |
| 379 | let a: [u8; 2] = self.read_u8_array()?; |
| 380 | Ok(self.endian().read_u16(&a)) |
| 381 | } |
| 382 | |
| 383 | /// Read an i16. |
| 384 | #[inline ] |
| 385 | fn read_i16(&mut self) -> Result<i16> { |
| 386 | let a: [u8; 2] = self.read_u8_array()?; |
| 387 | Ok(self.endian().read_i16(&a)) |
| 388 | } |
| 389 | |
| 390 | /// Read a u32. |
| 391 | #[inline ] |
| 392 | fn read_u32(&mut self) -> Result<u32> { |
| 393 | let a: [u8; 4] = self.read_u8_array()?; |
| 394 | Ok(self.endian().read_u32(&a)) |
| 395 | } |
| 396 | |
| 397 | /// Read an i32. |
| 398 | #[inline ] |
| 399 | fn read_i32(&mut self) -> Result<i32> { |
| 400 | let a: [u8; 4] = self.read_u8_array()?; |
| 401 | Ok(self.endian().read_i32(&a)) |
| 402 | } |
| 403 | |
| 404 | /// Read a u64. |
| 405 | #[inline ] |
| 406 | fn read_u64(&mut self) -> Result<u64> { |
| 407 | let a: [u8; 8] = self.read_u8_array()?; |
| 408 | Ok(self.endian().read_u64(&a)) |
| 409 | } |
| 410 | |
| 411 | /// Read an i64. |
| 412 | #[inline ] |
| 413 | fn read_i64(&mut self) -> Result<i64> { |
| 414 | let a: [u8; 8] = self.read_u8_array()?; |
| 415 | Ok(self.endian().read_i64(&a)) |
| 416 | } |
| 417 | |
| 418 | /// Read a f32. |
| 419 | #[inline ] |
| 420 | fn read_f32(&mut self) -> Result<f32> { |
| 421 | let a: [u8; 4] = self.read_u8_array()?; |
| 422 | Ok(self.endian().read_f32(&a)) |
| 423 | } |
| 424 | |
| 425 | /// Read a f64. |
| 426 | #[inline ] |
| 427 | fn read_f64(&mut self) -> Result<f64> { |
| 428 | let a: [u8; 8] = self.read_u8_array()?; |
| 429 | Ok(self.endian().read_f64(&a)) |
| 430 | } |
| 431 | |
| 432 | /// Read an unsigned n-bytes integer u64. |
| 433 | /// |
| 434 | /// # Panics |
| 435 | /// |
| 436 | /// Panics when nbytes < 1 or nbytes > 8 |
| 437 | #[inline ] |
| 438 | fn read_uint(&mut self, n: usize) -> Result<u64> { |
| 439 | let mut buf = [0; 8]; |
| 440 | self.read_slice(&mut buf[..n])?; |
| 441 | Ok(self.endian().read_uint(&buf[..n])) |
| 442 | } |
| 443 | |
| 444 | /// Read a null-terminated slice, and return it (excluding the null). |
| 445 | fn read_null_terminated_slice(&mut self) -> Result<Self> { |
| 446 | let idx = self.find(0)?; |
| 447 | let val = self.split(idx)?; |
| 448 | self.skip(Self::Offset::from_u8(1))?; |
| 449 | Ok(val) |
| 450 | } |
| 451 | |
| 452 | /// Skip a LEB128 encoded integer. |
| 453 | fn skip_leb128(&mut self) -> Result<()> { |
| 454 | leb128::read::skip(self) |
| 455 | } |
| 456 | |
| 457 | /// Read an unsigned LEB128 encoded integer. |
| 458 | fn read_uleb128(&mut self) -> Result<u64> { |
| 459 | leb128::read::unsigned(self) |
| 460 | } |
| 461 | |
| 462 | /// Read an unsigned LEB128 encoded u32. |
| 463 | fn read_uleb128_u32(&mut self) -> Result<u32> { |
| 464 | leb128::read::unsigned(self)? |
| 465 | .try_into() |
| 466 | .map_err(|_| Error::BadUnsignedLeb128) |
| 467 | } |
| 468 | |
| 469 | /// Read an unsigned LEB128 encoded u16. |
| 470 | fn read_uleb128_u16(&mut self) -> Result<u16> { |
| 471 | leb128::read::u16(self) |
| 472 | } |
| 473 | |
| 474 | /// Read a signed LEB128 encoded integer. |
| 475 | fn read_sleb128(&mut self) -> Result<i64> { |
| 476 | leb128::read::signed(self) |
| 477 | } |
| 478 | |
| 479 | /// Read an initial length field. |
| 480 | /// |
| 481 | /// This field is encoded as either a 32-bit length or |
| 482 | /// a 64-bit length, and the returned `Format` indicates which. |
| 483 | fn read_initial_length(&mut self) -> Result<(Self::Offset, Format)> { |
| 484 | const MAX_DWARF_32_UNIT_LENGTH: u32 = 0xffff_fff0; |
| 485 | const DWARF_64_INITIAL_UNIT_LENGTH: u32 = 0xffff_ffff; |
| 486 | |
| 487 | let val = self.read_u32()?; |
| 488 | if val < MAX_DWARF_32_UNIT_LENGTH { |
| 489 | Ok((Self::Offset::from_u32(val), Format::Dwarf32)) |
| 490 | } else if val == DWARF_64_INITIAL_UNIT_LENGTH { |
| 491 | let val = self.read_u64().and_then(Self::Offset::from_u64)?; |
| 492 | Ok((val, Format::Dwarf64)) |
| 493 | } else { |
| 494 | Err(Error::UnknownReservedLength) |
| 495 | } |
| 496 | } |
| 497 | |
| 498 | /// Read a byte and validate it as an address size. |
| 499 | fn read_address_size(&mut self) -> Result<u8> { |
| 500 | let size = self.read_u8()?; |
| 501 | match size { |
| 502 | 1 | 2 | 4 | 8 => Ok(size), |
| 503 | _ => Err(Error::UnsupportedAddressSize(size)), |
| 504 | } |
| 505 | } |
| 506 | |
| 507 | /// Read an address-sized integer, and return it as a `u64`. |
| 508 | fn read_address(&mut self, address_size: u8) -> Result<u64> { |
| 509 | match address_size { |
| 510 | 1 => self.read_u8().map(u64::from), |
| 511 | 2 => self.read_u16().map(u64::from), |
| 512 | 4 => self.read_u32().map(u64::from), |
| 513 | 8 => self.read_u64(), |
| 514 | otherwise => Err(Error::UnsupportedAddressSize(otherwise)), |
| 515 | } |
| 516 | } |
| 517 | |
| 518 | /// Parse a word-sized integer according to the DWARF format. |
| 519 | /// |
| 520 | /// These are always used to encode section offsets or lengths, |
| 521 | /// and so have a type of `Self::Offset`. |
| 522 | fn read_word(&mut self, format: Format) -> Result<Self::Offset> { |
| 523 | match format { |
| 524 | Format::Dwarf32 => self.read_u32().map(Self::Offset::from_u32), |
| 525 | Format::Dwarf64 => self.read_u64().and_then(Self::Offset::from_u64), |
| 526 | } |
| 527 | } |
| 528 | |
| 529 | /// Parse a word-sized section length according to the DWARF format. |
| 530 | #[inline ] |
| 531 | fn read_length(&mut self, format: Format) -> Result<Self::Offset> { |
| 532 | self.read_word(format) |
| 533 | } |
| 534 | |
| 535 | /// Parse a word-sized section offset according to the DWARF format. |
| 536 | #[inline ] |
| 537 | fn read_offset(&mut self, format: Format) -> Result<Self::Offset> { |
| 538 | self.read_word(format) |
| 539 | } |
| 540 | |
| 541 | /// Parse a section offset of the given size. |
| 542 | /// |
| 543 | /// This is used for `DW_FORM_ref_addr` values in DWARF version 2. |
| 544 | fn read_sized_offset(&mut self, size: u8) -> Result<Self::Offset> { |
| 545 | match size { |
| 546 | 1 => self.read_u8().map(u64::from), |
| 547 | 2 => self.read_u16().map(u64::from), |
| 548 | 4 => self.read_u32().map(u64::from), |
| 549 | 8 => self.read_u64(), |
| 550 | otherwise => Err(Error::UnsupportedOffsetSize(otherwise)), |
| 551 | } |
| 552 | .and_then(Self::Offset::from_u64) |
| 553 | } |
| 554 | } |
| 555 | |