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