summaryrefslogtreecommitdiffstats
path: root/dwarflint/checked_read.cc
blob: 48b15359d150b6549cf7bb50e353aeddc5f7f7d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/* Pedantic checking of DWARF files
   Copyright (C) 2010, 2011 Red Hat, Inc.
   This file is part of elfutils.

   This file 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.

   elfutils 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, see <http://www.gnu.org/licenses/>.  */

#define __STDC_FORMAT_MACROS
#include <inttypes.h>

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <dwarf.h>
#include <cassert>

#include "checked_read.hh"
#include "messages.hh"
#include "misc.hh"

bool
read_size_extra (struct read_ctx *ctx, uint32_t size32, uint64_t *sizep,
		 int *offset_sizep, locus const &loc)
{
  if (size32 == DWARF3_LENGTH_64_BIT)
    {
      if (!read_ctx_read_8ubyte (ctx, sizep))
	{
	  wr_error (loc) << "can't read 64bit CU length.\n";
	  return false;
	}

      *offset_sizep = 8;
    }
  else if (size32 >= DWARF3_LENGTH_MIN_ESCAPE_CODE)
    {
      wr_error (loc)
	<< "unrecognized CU length escape value: " << size32 << ".\n";
      return false;
    }
  else
    {
      *sizep = size32;
      *offset_sizep = 4;
    }

  return true;
}

error_code
read_address_size (struct read_ctx *ctx, bool addr_64,
		   int *address_sizep, locus const &loc)
{
  uint8_t address_size;
  if (!read_ctx_read_ubyte (ctx, &address_size))
    {
      wr_error (loc) << "can't read address size.\n";
      return err_fatal;
    }

  error_code ret = err_ok;
  if (address_size != 4 && address_size != 8)
    {
      /* Keep going.  Deduce the address size from ELF header, and try
	 to parse it anyway.  */
      wr_error (loc) << "invalid address size: " << (int)address_size
		     << " (only 4 or 8 allowed).\n";
      address_size = addr_64 ? 8 : 4;
      ret = err_nohl;
    }
  else if ((address_size == 8) != addr_64)
    {
      /* Keep going, we may still be able to parse it.  */
      wr_error (loc) << "CU reports address size of " << address_size
		     << " in " << (addr_64 ? 64 : 32) << "-bit ELF.\n";
      ret = err_nohl;
    }

  *address_sizep = address_size;
  return ret;
}

bool
checked_read_uleb128 (read_ctx *ctx, uint64_t *ret,
		      locus const &loc, const char *what)
{
  const unsigned char *ptr = ctx->ptr;
  int st = read_ctx_read_uleb128 (ctx, ret);
  if (st < 0)
    wr_error (loc) << "can't read " << what << ".\n";
  else if (st > 0)
    {
      char buf[19]; // 16 hexa digits, "0x", terminating zero
      sprintf (buf, "%#" PRIx64, *ret);
      wr_format_leb128_message (loc, what, buf, ptr, ctx->ptr);
    }
  return st >= 0;
}

bool
checked_read_sleb128 (read_ctx *ctx, int64_t *ret,
		      locus const &loc, const char *what)
{
  const unsigned char *ptr = ctx->ptr;
  int st = read_ctx_read_sleb128 (ctx, ret);
  if (st < 0)
    wr_error (loc) << "can't read " << what << ".\n";
  else if (st > 0)
    {
      char buf[20]; // sign, "0x", 16 hexa digits, terminating zero
      int64_t val = *ret;
      sprintf (buf, "%s%#" PRIx64, val < 0 ? "-" : "", val < 0 ? -val : val);
      wr_format_leb128_message (loc, what, buf, ptr, ctx->ptr);
    }
  return st >= 0;
}

bool
checked_read_leb128 (read_ctx *ctx, form_width_t width, uint64_t *ret,
		     locus const &loc, const char *what)
{
  assert (width == fw_sleb || width == fw_uleb);
  if (width == fw_sleb)
    {
      int64_t svalue;
      if (!checked_read_sleb128 (ctx, &svalue, loc, what))
	return false;
      *ret = (uint64_t) svalue;
      return true;
    }
  else
    return checked_read_uleb128 (ctx, ret, loc, what);
}

bool
read_sc_value (uint64_t *valuep, form_width_t width,
	       read_ctx *ctx, locus const &loc)
{
  switch (width)
    {
    case fw_0:
      *valuep = 1;
      return true;

    case fw_1:
    case fw_2:
    case fw_4:
    case fw_8:
      return read_ctx_read_var (ctx, width, valuep);

    case fw_uleb:
    case fw_sleb:
      return checked_read_leb128 (ctx, width, valuep,
				  loc, "attribute value");

    case fw_unknown:
      ;
    }
  UNREACHABLE;
}

bool
read_generic_value (read_ctx *ctx,
		    form_width_t width, storage_class_t storclass,
		    locus const &loc, uint64_t *valuep, read_ctx *blockp)
{
  uint64_t value;
  if (storclass == sc_value
      || storclass == sc_block)
    {
      if (!read_sc_value (&value, width, ctx, loc))
	return false;
      if (valuep != NULL)
	*valuep = value;
      if (storclass == sc_value)
	return true;
    }

  unsigned char const *start = ctx->ptr;
  if (storclass == sc_string)
    {
      if (!read_ctx_read_str (ctx))
	return false;
    }
  else if (storclass == sc_block)
    {
      if (!read_ctx_skip (ctx, value))
	return false;
    }

  if (blockp != NULL)
    {
      if (!read_ctx_init_sub (blockp, ctx, start, ctx->ptr))
	return false;
    }

  return true;
}