Skip to content

Commit bf1ec5a

Browse files
committed
Allow very large offsets in SHT_CREL
1 parent b291100 commit bf1ec5a

File tree

1 file changed

+20
-8
lines changed

1 file changed

+20
-8
lines changed

src/input-files.cc

+20-8
Original file line numberDiff line numberDiff line change
@@ -249,23 +249,35 @@ std::vector<ElfRel<E>> decode_crel(Context<E> &ctx, std::string_view contents) {
249249
bool is_rela = hdr & 0b100;
250250
i64 scale = hdr & 0b11;
251251

252-
i64 offset = 0;
253-
i64 addend = 0;
254-
i64 symidx = 0;
252+
u64 offset = 0;
255253
i64 type = 0;
254+
i64 symidx = 0;
255+
i64 addend = 0;
256256

257257
std::vector<ElfRel<E>> vec;
258258
vec.reserve(nrels);
259259

260260
while (vec.size() < nrels) {
261-
u64 val = read_uleb(&p);
262-
offset += (val >> (is_rela ? 3 : 2)) << scale;
261+
u8 flags = *p++;
262+
i64 nflags = is_rela ? 3 : 2;
263+
264+
// The first ULEB-128 encoded value is a concatenation of bit flags and
265+
// an offset delta. The delta may be a very large to decrease the
266+
// current offset value by wrapping around. Combined, the encoded value
267+
// can be up to 67 bit long. Thus we can't simply use read_uleb() which
268+
// returns a u64.
269+
u64 delta;
270+
if (flags & 0x80)
271+
delta = (read_uleb(&p) << (7 - nflags)) | ((flags & 0x7f) >> nflags);
272+
else
273+
delta = flags >> nflags;
274+
offset += delta << scale;
263275

264-
if (val & 1)
276+
if (flags & 1)
265277
symidx += read_sleb(&p);
266-
if (val & 2)
278+
if (flags & 2)
267279
type += read_sleb(&p);
268-
if (is_rela && (val & 4))
280+
if (is_rela && (flags & 4))
269281
addend += read_sleb(&p);
270282
vec.emplace_back(offset, type, symidx, addend);
271283
}

0 commit comments

Comments
 (0)