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 440 : elf_compress_gnu (Elf_Scn *scn, int inflate, unsigned int flags)
39 : {
40 440 : if (scn == NULL)
41 : return -1;
42 :
43 440 : if ((flags & ~ELF_CHF_FORCE) != 0)
44 : {
45 0 : __libelf_seterrno (ELF_E_INVALID_OPERAND);
46 0 : return -1;
47 : }
48 :
49 440 : bool force = (flags & ELF_CHF_FORCE) != 0;
50 :
51 440 : Elf *elf = scn->elf;
52 : GElf_Ehdr ehdr;
53 440 : if (gelf_getehdr (elf, &ehdr) == NULL)
54 : return -1;
55 :
56 440 : int elfclass = elf->class;
57 440 : int elfdata = ehdr.e_ident[EI_DATA];
58 :
59 : Elf64_Xword sh_flags;
60 : Elf64_Word sh_type;
61 : Elf64_Xword sh_addralign;
62 440 : if (elfclass == ELFCLASS32)
63 : {
64 183 : Elf32_Shdr *shdr = elf32_getshdr (scn);
65 183 : if (shdr == NULL)
66 : return -1;
67 :
68 183 : sh_flags = shdr->sh_flags;
69 183 : sh_type = shdr->sh_type;
70 183 : sh_addralign = shdr->sh_addralign;
71 : }
72 : else
73 : {
74 257 : Elf64_Shdr *shdr = elf64_getshdr (scn);
75 257 : if (shdr == NULL)
76 : return -1;
77 :
78 257 : sh_flags = shdr->sh_flags;
79 257 : sh_type = shdr->sh_type;
80 257 : sh_addralign = shdr->sh_addralign;
81 : }
82 :
83 : /* Allocated sections, or sections that are already are compressed
84 : cannot (also) be GNU compressed. */
85 440 : 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 440 : 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 440 : if (inflate == 1)
101 : {
102 176 : size_t hsize = 4 + 8; /* GNU "ZLIB" + 8 byte size. */
103 : size_t orig_size, new_size, orig_addralign;
104 176 : void *out_buf = __libelf_compress (scn, hsize, elfdata,
105 : &orig_size, &orig_addralign,
106 : &new_size, force);
107 :
108 : /* Compression would make section larger, don't change anything. */
109 176 : if (out_buf == (void *) -1)
110 : return 0;
111 :
112 : /* Compression failed, return error. */
113 147 : if (out_buf == NULL)
114 : return -1;
115 :
116 294 : uint64_t be64_size = htobe64 (orig_size);
117 147 : memmove (out_buf, "ZLIB", 4);
118 294 : memmove (out_buf + 4, &be64_size, sizeof (be64_size));
119 :
120 : /* We don't know anything about sh_entsize, sh_addralign and
121 : sh_flags won't have a SHF_COMPRESSED hint in the GNU format.
122 : Just adjust the sh_size. */
123 147 : if (elfclass == ELFCLASS32)
124 : {
125 73 : Elf32_Shdr *shdr = elf32_getshdr (scn);
126 73 : shdr->sh_size = new_size;
127 : }
128 : else
129 : {
130 74 : Elf64_Shdr *shdr = elf64_getshdr (scn);
131 74 : shdr->sh_size = new_size;
132 : }
133 :
134 147 : __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 147 : scn->zdata_base = NULL;
141 :
142 147 : return 1;
143 : }
144 264 : else if (inflate == 0)
145 : {
146 : /* In theory the user could have constucted a compressed section
147 : by hand. But we always just take the rawdata directly and
148 : decompress that. */
149 264 : Elf_Data *data = elf_rawdata (scn, NULL);
150 264 : if (data == NULL)
151 : return -1;
152 :
153 264 : size_t hsize = 4 + 8; /* GNU "ZLIB" + 8 byte size. */
154 264 : if (data->d_size < hsize || memcmp (data->d_buf, "ZLIB", 4) != 0)
155 : {
156 4 : __libelf_seterrno (ELF_E_NOT_COMPRESSED);
157 4 : return -1;
158 : }
159 :
160 : /* There is a 12-byte header of "ZLIB" followed by
161 : an 8-byte big-endian size. There is only one type and
162 : Alignment isn't preserved separately. */
163 : uint64_t gsize;
164 260 : memcpy (&gsize, data->d_buf + 4, sizeof gsize);
165 520 : gsize = be64toh (gsize);
166 :
167 : /* One more sanity check, size should be bigger than original
168 : data size plus some overhead (4 chars ZLIB + 8 bytes size + 6
169 : bytes zlib stream overhead + 5 bytes overhead max for one 16K
170 : block) and should fit into a size_t. */
171 260 : if (gsize + 4 + 8 + 6 + 5 < data->d_size || gsize > SIZE_MAX)
172 : {
173 0 : __libelf_seterrno (ELF_E_NOT_COMPRESSED);
174 0 : return -1;
175 : }
176 :
177 260 : size_t size = gsize;
178 260 : size_t size_in = data->d_size - hsize;
179 260 : void *buf_in = data->d_buf + hsize;
180 260 : void *buf_out = __libelf_decompress (buf_in, size_in, size);
181 260 : if (buf_out == NULL)
182 : return -1;
183 :
184 : /* We don't know anything about sh_entsize, sh_addralign and
185 : sh_flags won't have a SHF_COMPRESSED hint in the GNU format.
186 : Just adjust the sh_size. */
187 260 : if (elfclass == ELFCLASS32)
188 : {
189 98 : Elf32_Shdr *shdr = elf32_getshdr (scn);
190 98 : shdr->sh_size = size;
191 : }
192 : else
193 : {
194 162 : Elf64_Shdr *shdr = elf64_getshdr (scn);
195 162 : shdr->sh_size = size;
196 : }
197 :
198 260 : __libelf_reset_rawdata (scn, buf_out, size, sh_addralign,
199 : __libelf_data_type (elf, sh_type));
200 :
201 260 : scn->zdata_base = buf_out;
202 :
203 260 : return 1;
204 : }
205 : else
206 : {
207 0 : __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
208 0 : return -1;
209 : }
210 : }
|