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