Coverage Report

Created: 2025-10-10 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/elfutils/libelf/elf_compress_gnu.c
Line
Count
Source
1
/* Compress or decompress a section.
2
   Copyright (C) 2015 Red Hat, Inc.
3
   This file is part of elfutils.
4
5
   This file is free software; you can redistribute it and/or modify
6
   it under the terms of either
7
8
     * the GNU Lesser General Public License as published by the Free
9
       Software Foundation; either version 3 of the License, or (at
10
       your option) any later version
11
12
   or
13
14
     * the GNU General Public License as published by the Free
15
       Software Foundation; either version 2 of the License, or (at
16
       your option) any later version
17
18
   or both in parallel, as here.
19
20
   elfutils is distributed in the hope that it will be useful, but
21
   WITHOUT ANY WARRANTY; without even the implied warranty of
22
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23
   General Public License for more details.
24
25
   You should have received copies of the GNU General Public License and
26
   the GNU Lesser General Public License along with this program.  If
27
   not, see <http://www.gnu.org/licenses/>.  */
28
29
#ifdef HAVE_CONFIG_H
30
# include <config.h>
31
#endif
32
33
#include <libelf.h>
34
#include "libelfP.h"
35
#include "common.h"
36
37
int
38
elf_compress_gnu (Elf_Scn *scn, int inflate, unsigned int flags)
39
8.01k
{
40
8.01k
  if (scn == NULL)
41
0
    return -1;
42
43
8.01k
  if ((flags & ~ELF_CHF_FORCE) != 0)
44
0
    {
45
0
      __libelf_seterrno (ELF_E_INVALID_OPERAND);
46
0
      return -1;
47
0
    }
48
49
8.01k
  bool force = (flags & ELF_CHF_FORCE) != 0;
50
51
8.01k
  Elf *elf = scn->elf;
52
8.01k
  GElf_Ehdr ehdr;
53
8.01k
  if (gelf_getehdr (elf, &ehdr) == NULL)
54
0
    return -1;
55
56
8.01k
  int elfclass = elf->class;
57
8.01k
  int elfdata = ehdr.e_ident[EI_DATA];
58
59
8.01k
  Elf64_Xword sh_flags;
60
8.01k
  Elf64_Word sh_type;
61
8.01k
  Elf64_Xword sh_addralign;
62
8.01k
  union shdr
63
8.01k
  {
64
8.01k
    Elf32_Shdr *s32;
65
8.01k
    Elf64_Shdr *s64;
66
8.01k
  } shdr;
67
8.01k
  if (elfclass == ELFCLASS32)
68
6.10k
    {
69
6.10k
      shdr.s32 = elf32_getshdr (scn);
70
6.10k
      if (shdr.s32 == NULL)
71
0
  return -1;
72
73
6.10k
      sh_flags = shdr.s32->sh_flags;
74
6.10k
      sh_type = shdr.s32->sh_type;
75
6.10k
      sh_addralign = shdr.s32->sh_addralign;
76
6.10k
    }
77
1.91k
  else
78
1.91k
    {
79
1.91k
      shdr.s64 = elf64_getshdr (scn);
80
1.91k
      if (shdr.s64 == NULL)
81
0
  return -1;
82
83
1.91k
      sh_flags = shdr.s64->sh_flags;
84
1.91k
      sh_type = shdr.s64->sh_type;
85
1.91k
      sh_addralign = shdr.s64->sh_addralign;
86
1.91k
    }
87
88
  /* Allocated sections, or sections that are already are compressed
89
     cannot (also) be GNU compressed.  */
90
8.01k
  if ((sh_flags & SHF_ALLOC) != 0 || (sh_flags & SHF_COMPRESSED))
91
2.58k
    {
92
2.58k
      __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS);
93
2.58k
      return -1;
94
2.58k
    }
95
96
5.43k
  if (sh_type == SHT_NULL || sh_type == SHT_NOBITS)
97
2.28k
    {
98
2.28k
      __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE);
99
2.28k
      return -1;
100
2.28k
    }
101
102
  /* For GNU compression we cannot really know whether the section is
103
     already compressed or not.  Just try and see what happens...  */
104
  // int compressed = (sh_flags & SHF_COMPRESSED);
105
3.15k
  if (inflate == 1)
106
0
    {
107
0
      size_t hsize = 4 + 8; /* GNU "ZLIB" + 8 byte size.  */
108
0
      size_t orig_size, new_size, orig_addralign;
109
0
      void *out_buf = __libelf_compress (scn, hsize, elfdata,
110
0
           &orig_size, &orig_addralign,
111
0
           &new_size, force,
112
0
           /* use_zstd */ false);
113
114
      /* Compression would make section larger, don't change anything.  */
115
0
      if (out_buf == (void *) -1)
116
0
  return 0;
117
118
      /* Compression failed, return error.  */
119
0
      if (out_buf == NULL)
120
0
  return -1;
121
122
0
      uint64_t be64_size = htobe64 (orig_size);
123
0
      memmove (out_buf, "ZLIB", 4);
124
0
      memmove (out_buf + 4, &be64_size, sizeof (be64_size));
125
126
      /* We don't know anything about sh_entsize, sh_addralign and
127
   sh_flags won't have a SHF_COMPRESSED hint in the GNU format.
128
   Just adjust the sh_size.  */
129
0
      if (elfclass == ELFCLASS32)
130
0
    shdr.s32->sh_size = new_size;
131
0
      else
132
0
    shdr.s64->sh_size = new_size;
133
134
0
      __libelf_reset_rawdata (scn, out_buf, new_size, 1, ELF_T_BYTE);
135
136
      /* The section is now compressed, we could keep the uncompressed
137
   data around, but since that might have been multiple Elf_Data
138
   buffers let the user uncompress it explicitly again if they
139
   want it to simplify bookkeeping.  */
140
0
      scn->zdata_base = NULL;
141
142
0
      return 1;
143
0
    }
144
3.15k
  else if (inflate == 0)
145
3.15k
    {
146
      /* In theory the user could have constructed a compressed section
147
   by hand.  And in practice they do. For example when copying
148
   a section from one file to another using elf_newdata. So we
149
   have to use elf_getdata (not elf_rawdata).  */
150
3.15k
      Elf_Data *data = elf_getdata (scn, NULL);
151
3.15k
      if (data == NULL)
152
45
  return -1;
153
154
3.10k
      size_t hsize = 4 + 8; /* GNU "ZLIB" + 8 byte size.  */
155
3.10k
      if (data->d_size < hsize || memcmp (data->d_buf, "ZLIB", 4) != 0)
156
1.83k
  {
157
1.83k
          __libelf_seterrno (ELF_E_NOT_COMPRESSED);
158
1.83k
    return -1;
159
1.83k
  }
160
161
      /* There is a 12-byte header of "ZLIB" followed by
162
   an 8-byte big-endian size.  There is only one type and
163
   Alignment isn't preserved separately.  */
164
1.27k
      uint64_t gsize;
165
1.27k
      memcpy (&gsize, data->d_buf + 4, sizeof gsize);
166
1.27k
      gsize = be64toh (gsize);
167
168
      /* One more sanity check, size should be bigger than original
169
   data size plus some overhead (4 chars ZLIB + 8 bytes size + 6
170
   bytes zlib stream overhead + 5 bytes overhead max for one 16K
171
   block) and should fit into a size_t.  */
172
1.27k
      if (gsize + 4 + 8 + 6 + 5 < data->d_size || gsize > SIZE_MAX)
173
4
  {
174
4
    __libelf_seterrno (ELF_E_NOT_COMPRESSED);
175
4
    return -1;
176
4
  }
177
178
1.26k
      size_t size = gsize;
179
1.26k
      size_t size_in = data->d_size - hsize;
180
1.26k
      void *buf_in = data->d_buf + hsize;
181
1.26k
      void *buf_out = __libelf_decompress (ELFCOMPRESS_ZLIB, buf_in, size_in, size);
182
1.26k
      if (buf_out == NULL)
183
136
  return -1;
184
185
      /* We don't know anything about sh_entsize, sh_addralign and
186
   sh_flags won't have a SHF_COMPRESSED hint in the GNU format.
187
   Just adjust the sh_size.  */
188
1.13k
      if (elfclass == ELFCLASS32)
189
1.13k
  shdr.s32->sh_size = size;
190
0
      else
191
0
  shdr.s64->sh_size = size;
192
193
1.13k
      __libelf_reset_rawdata (scn, buf_out, size, sh_addralign,
194
1.13k
            __libelf_data_type (&ehdr, sh_type,
195
1.13k
              sh_addralign));
196
197
1.13k
      scn->zdata_base = buf_out;
198
199
1.13k
      return 1;
200
1.26k
    }
201
0
  else
202
0
    {
203
0
      __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
204
0
      return -1;
205
0
    }
206
3.15k
}