| // int_encoding.cc -- variable length and unaligned integer encoding support. |
| |
| // Copyright (C) 2009-2024 Free Software Foundation, Inc. |
| // Written by Doug Kwan <dougkwan@google.com> by refactoring scattered |
| // contents from other files in gold. Original code written by Ian |
| // Lance Taylor <iant@google.com> and Caleb Howe <cshowe@google.com>. |
| |
| // This file is part of gold. |
| |
| // This program 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; either version 3 of the License, or |
| // (at your option) any later version. |
| |
| // This program 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 this program; if not, write to the Free Software |
| // Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| // MA 02110-1301, USA. |
| |
| #include "gold.h" |
| |
| #include <vector> |
| |
| #include "int_encoding.h" |
| |
| namespace gold { |
| |
| // Read an unsigned LEB128 number. Each byte contains 7 bits of |
| // information, plus one bit saying whether the number continues or |
| // not. BYTE contains the first byte of the number, and is guaranteed |
| // to have the continuation bit set. |
| |
| uint64_t |
| read_unsigned_LEB_128_x(const unsigned char* buffer, size_t* len, |
| unsigned char byte) |
| { |
| uint64_t result = static_cast<uint64_t>(byte & 0x7f); |
| size_t num_read = 1; |
| unsigned int shift = 7; |
| |
| do |
| { |
| if (num_read > 64 / 7 + 1) |
| { |
| gold_warning(_("Unusually large LEB128 decoded, " |
| "debug information may be corrupted")); |
| break; |
| } |
| byte = *buffer++; |
| num_read++; |
| result |= (static_cast<uint64_t>(byte & 0x7f)) << shift; |
| shift += 7; |
| } |
| while (byte & 0x80); |
| |
| *len = num_read; |
| |
| return result; |
| } |
| |
| // Read a signed LEB128 number. These are like regular LEB128 |
| // numbers, except the last byte may have a sign bit set. |
| // BYTE contains the first byte of the number, and is guaranteed |
| // to have the continuation bit set. |
| |
| int64_t |
| read_signed_LEB_128_x(const unsigned char* buffer, size_t* len, |
| unsigned char byte) |
| { |
| int64_t result = static_cast<uint64_t>(byte & 0x7f); |
| int shift = 7; |
| size_t num_read = 1; |
| |
| do |
| { |
| if (num_read > 64 / 7 + 1) |
| { |
| gold_warning(_("Unusually large LEB128 decoded, " |
| "debug information may be corrupted")); |
| break; |
| } |
| byte = *buffer++; |
| num_read++; |
| result |= (static_cast<uint64_t>(byte & 0x7f) << shift); |
| shift += 7; |
| } |
| while (byte & 0x80); |
| |
| if ((shift < 8 * static_cast<int>(sizeof(result))) && (byte & 0x40)) |
| result |= -((static_cast<int64_t>(1)) << shift); |
| *len = num_read; |
| return result; |
| } |
| |
| void |
| write_unsigned_LEB_128(std::vector<unsigned char>* buffer, uint64_t value) |
| { |
| do |
| { |
| unsigned char current_byte = value & 0x7f; |
| value >>= 7; |
| if (value != 0) |
| { |
| current_byte |= 0x80; |
| } |
| buffer->push_back(current_byte); |
| } |
| while (value != 0); |
| } |
| |
| size_t |
| get_length_as_unsigned_LEB_128(uint64_t value) |
| { |
| size_t length = 0; |
| do |
| { |
| value >>= 7; |
| length++; |
| } |
| while (value != 0); |
| return length; |
| } |
| |
| } // End namespace gold. |