Branch data Line data Source code
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 : 725 : elf_compress_gnu (Elf_Scn *scn, int inflate, unsigned int flags)
39 : : {
40 [ - + ]: 725 : if (scn == NULL)
41 : : return -1;
42 : :
43 [ - + ]: 725 : if ((flags & ~ELF_CHF_FORCE) != 0)
44 : : {
45 : 0 : __libelf_seterrno (ELF_E_INVALID_OPERAND);
46 : 0 : return -1;
47 : : }
48 : :
49 : 725 : bool force = (flags & ELF_CHF_FORCE) != 0;
50 : :
51 : 725 : Elf *elf = scn->elf;
52 : 725 : GElf_Ehdr ehdr;
53 [ - + ]: 725 : if (gelf_getehdr (elf, &ehdr) == NULL)
54 : : return -1;
55 : :
56 : 725 : int elfclass = elf->class;
57 : 725 : int elfdata = ehdr.e_ident[EI_DATA];
58 : :
59 : 725 : Elf64_Xword sh_flags;
60 : 725 : Elf64_Word sh_type;
61 : 725 : Elf64_Xword sh_addralign;
62 [ + + ]: 725 : if (elfclass == ELFCLASS32)
63 : : {
64 : 286 : Elf32_Shdr *shdr = elf32_getshdr (scn);
65 [ - + ]: 286 : if (shdr == NULL)
66 : : return -1;
67 : :
68 : 286 : sh_flags = shdr->sh_flags;
69 : 286 : sh_type = shdr->sh_type;
70 : 286 : sh_addralign = shdr->sh_addralign;
71 : : }
72 : : else
73 : : {
74 : 439 : Elf64_Shdr *shdr = elf64_getshdr (scn);
75 [ - + ]: 439 : if (shdr == NULL)
76 : : return -1;
77 : :
78 : 439 : sh_flags = shdr->sh_flags;
79 : 439 : sh_type = shdr->sh_type;
80 : 439 : sh_addralign = shdr->sh_addralign;
81 : : }
82 : :
83 : : /* Allocated sections, or sections that are already are compressed
84 : : cannot (also) be GNU compressed. */
85 [ - + ]: 725 : if ((sh_flags & SHF_ALLOC) != 0 || (sh_flags & SHF_COMPRESSED))
86 : : {
87 : 0 : __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS);
88 : 0 : return -1;
89 : : }
90 : :
91 [ - + ]: 725 : if (sh_type == SHT_NULL || sh_type == SHT_NOBITS)
92 : : {
93 : 0 : __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE);
94 : 0 : return -1;
95 : : }
96 : :
97 : : /* For GNU compression we cannot really know whether the section is
98 : : already compressed or not. Just try and see what happens... */
99 : : // int compressed = (sh_flags & SHF_COMPRESSED);
100 [ + + ]: 725 : if (inflate == 1)
101 : : {
102 : 417 : size_t hsize = 4 + 8; /* GNU "ZLIB" + 8 byte size. */
103 : 417 : size_t orig_size, new_size, orig_addralign;
104 : 417 : void *out_buf = __libelf_compress (scn, hsize, elfdata,
105 : : &orig_size, &orig_addralign,
106 : : &new_size, force,
107 : : /* use_zstd */ false);
108 : :
109 : : /* Compression would make section larger, don't change anything. */
110 [ + + ]: 417 : if (out_buf == (void *) -1)
111 : : return 0;
112 : :
113 : : /* Compression failed, return error. */
114 [ + - ]: 364 : if (out_buf == NULL)
115 : : return -1;
116 : :
117 [ + + ]: 364 : uint64_t be64_size = htobe64 (orig_size);
118 [ + + ]: 364 : memmove (out_buf, "ZLIB", 4);
119 : 364 : memmove (out_buf + 4, &be64_size, sizeof (be64_size));
120 : :
121 : : /* We don't know anything about sh_entsize, sh_addralign and
122 : : sh_flags won't have a SHF_COMPRESSED hint in the GNU format.
123 : : Just adjust the sh_size. */
124 [ + + ]: 364 : if (elfclass == ELFCLASS32)
125 : : {
126 : 160 : Elf32_Shdr *shdr = elf32_getshdr (scn);
127 : 160 : shdr->sh_size = new_size;
128 : : }
129 : : else
130 : : {
131 : 204 : Elf64_Shdr *shdr = elf64_getshdr (scn);
132 : 204 : shdr->sh_size = new_size;
133 : : }
134 : :
135 : 364 : __libelf_reset_rawdata (scn, out_buf, new_size, 1, ELF_T_BYTE);
136 : :
137 : : /* The section is now compressed, we could keep the uncompressed
138 : : data around, but since that might have been multiple Elf_Data
139 : : buffers let the user uncompress it explicitly again if they
140 : : want it to simplify bookkeeping. */
141 : 364 : scn->zdata_base = NULL;
142 : :
143 : 364 : return 1;
144 : : }
145 [ + - ]: 308 : else if (inflate == 0)
146 : : {
147 : : /* In theory the user could have constructed a compressed section
148 : : by hand. And in practice they do. For example when copying
149 : : a section from one file to another using elf_newdata. So we
150 : : have to use elf_getdata (not elf_rawdata). */
151 : 308 : Elf_Data *data = elf_getdata (scn, NULL);
152 [ - + ]: 308 : if (data == NULL)
153 : : return -1;
154 : :
155 : 308 : size_t hsize = 4 + 8; /* GNU "ZLIB" + 8 byte size. */
156 [ + - + + ]: 308 : if (data->d_size < hsize || memcmp (data->d_buf, "ZLIB", 4) != 0)
157 : : {
158 : 12 : __libelf_seterrno (ELF_E_NOT_COMPRESSED);
159 : 12 : return -1;
160 : : }
161 : :
162 : : /* There is a 12-byte header of "ZLIB" followed by
163 : : an 8-byte big-endian size. There is only one type and
164 : : Alignment isn't preserved separately. */
165 : 296 : uint64_t gsize;
166 [ - + ]: 296 : memcpy (&gsize, data->d_buf + 4, sizeof gsize);
167 [ - + ]: 296 : gsize = be64toh (gsize);
168 : :
169 : : /* One more sanity check, size should be bigger than original
170 : : data size plus some overhead (4 chars ZLIB + 8 bytes size + 6
171 : : bytes zlib stream overhead + 5 bytes overhead max for one 16K
172 : : block) and should fit into a size_t. */
173 [ - + ]: 296 : if (gsize + 4 + 8 + 6 + 5 < data->d_size || gsize > SIZE_MAX)
174 : : {
175 : 0 : __libelf_seterrno (ELF_E_NOT_COMPRESSED);
176 : 0 : return -1;
177 : : }
178 : :
179 : 296 : size_t size = gsize;
180 : 296 : size_t size_in = data->d_size - hsize;
181 : 296 : void *buf_in = data->d_buf + hsize;
182 : 296 : void *buf_out = __libelf_decompress (ELFCOMPRESS_ZLIB, buf_in, size_in, size);
183 [ - + ]: 296 : if (buf_out == NULL)
184 : : return -1;
185 : :
186 : : /* We don't know anything about sh_entsize, sh_addralign and
187 : : sh_flags won't have a SHF_COMPRESSED hint in the GNU format.
188 : : Just adjust the sh_size. */
189 [ + + ]: 296 : if (elfclass == ELFCLASS32)
190 : : {
191 : 104 : Elf32_Shdr *shdr = elf32_getshdr (scn);
192 : 104 : shdr->sh_size = size;
193 : : }
194 : : else
195 : : {
196 : 192 : Elf64_Shdr *shdr = elf64_getshdr (scn);
197 : 192 : shdr->sh_size = size;
198 : : }
199 : :
200 : 296 : __libelf_reset_rawdata (scn, buf_out, size, sh_addralign,
201 : : __libelf_data_type (&ehdr, sh_type,
202 : : sh_addralign));
203 : :
204 : 296 : scn->zdata_base = buf_out;
205 : :
206 : 296 : return 1;
207 : : }
208 : : else
209 : : {
210 : 0 : __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
211 : 0 : return -1;
212 : : }
213 : : }
|