1 /* Test program for copying a whole ELF file using libelf. 2 Copyright (C) 2018 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 the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 elfutils is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 17 18 19 #ifdef HAVE_CONFIG_H 20 # include <config.h> 21 #endif 22 23 #include <errno.h> 24 #include <fcntl.h> 25 #include <inttypes.h> 26 #include <stdbool.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <unistd.h> 31 #include <sys/types.h> 32 #include <sys/stat.h> 33 34 #include ELFUTILS_HEADER(elf) 35 #include <gelf.h> 36 37 38 /* shstrndx is special, might overflow into section zero header sh_link. */ 39 static int setshstrndx(Elf * elf,size_t ndx)40 setshstrndx (Elf *elf, size_t ndx) 41 { 42 printf ("setshstrndx: %zd\n", ndx); 43 44 GElf_Ehdr ehdr_mem; 45 GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); 46 if (ehdr == NULL) 47 return -1; 48 49 if (ndx < SHN_LORESERVE) 50 ehdr->e_shstrndx = ndx; 51 else 52 { 53 ehdr->e_shstrndx = SHN_XINDEX; 54 Elf_Scn *zscn = elf_getscn (elf, 0); 55 GElf_Shdr zshdr_mem; 56 GElf_Shdr *zshdr = gelf_getshdr (zscn, &zshdr_mem); 57 if (zshdr == NULL) 58 return -1; 59 zshdr->sh_link = ndx; 60 if (gelf_update_shdr (zscn, zshdr) == 0) 61 return -1; 62 } 63 64 if (gelf_update_ehdr (elf, ehdr) == 0) 65 return -1; 66 67 return 0; 68 } 69 70 /* Copies all elements of an ELF file either using mmap or read. */ 71 static void copy_elf(const char * in,const char * out,bool use_mmap)72 copy_elf (const char *in, const char *out, bool use_mmap) 73 { 74 printf ("\ncopy_elf: %s -> %s (%s)\n", in, out, use_mmap ? "mmap" : "read"); 75 76 /* Existing ELF file. */ 77 int fda = open (in, O_RDONLY); 78 if (fda < 0) 79 { 80 fprintf (stderr, "Couldn't open file '%s': %s\n", 81 in, strerror (errno)); 82 exit (1); 83 } 84 85 Elf *elfa = elf_begin (fda, use_mmap ? ELF_C_READ_MMAP : ELF_C_READ, NULL); 86 if (elfa == NULL) 87 { 88 fprintf (stderr, "Couldn't open ELF file '%s': %s\n", 89 in, elf_errmsg (-1)); 90 exit (1); 91 } 92 93 /* Open new file. */ 94 int fdb = open (out, O_RDWR | O_CREAT | O_TRUNC, 0644); 95 if (fdb < 0) 96 { 97 fprintf (stderr, "Couldn't create file '%s': %s\n", 98 out, strerror (errno)); 99 exit (1); 100 } 101 102 Elf *elfb = elf_begin (fdb, use_mmap ? ELF_C_WRITE_MMAP : ELF_C_WRITE, NULL); 103 if (elfb == NULL) 104 { 105 fprintf (stderr, "Couldn't create ELF file '%s': %s\n", 106 out, elf_errmsg (-1)); 107 exit (1); 108 } 109 110 // Copy ELF header. 111 GElf_Ehdr ehdr_mema; 112 GElf_Ehdr *ehdra = gelf_getehdr (elfa, &ehdr_mema); 113 if (ehdra == NULL) 114 { 115 printf ("cannot get ELF header: %s\n", elf_errmsg (-1)); 116 exit (1); 117 } 118 119 int class = gelf_getclass (elfa); 120 // Create an ELF header. 121 if (gelf_newehdr (elfb, class) == 0) 122 { 123 printf ("cannot create ELF header: %s\n", elf_errmsg (-1)); 124 exit (1); 125 } 126 127 /* New elf header is an exact copy. */ 128 GElf_Ehdr ehdr_memb; 129 GElf_Ehdr *ehdrb = &ehdr_memb; 130 *ehdrb = *ehdra; 131 if (gelf_update_ehdr (elfb, ehdrb) == 0) 132 { 133 printf ("cannot update ELF header: %s\n", elf_errmsg (-1)); 134 exit (1); 135 } 136 137 /* shstrndx is special. (Technically phdrnum and shdrnum are also 138 special, but they are handled by libelf.) */ 139 size_t shstrndx; 140 if (elf_getshdrstrndx (elfa, &shstrndx) < 0) 141 { 142 printf ("cannot get shstrndx: %s\n", elf_errmsg (-1)); 143 exit (1); 144 } 145 if (setshstrndx (elfb, shstrndx) < 0) 146 { 147 printf ("cannot set shstrndx: %s\n", elf_errmsg (-1)); 148 exit (1); 149 } 150 151 /* If there are phdrs, copy them over. */ 152 size_t phnum; 153 if (elf_getphdrnum (elfa, &phnum) != 0) 154 { 155 printf ("cannot get phdrs: %s\n", elf_errmsg (-1)); 156 exit (1); 157 } 158 159 if (phnum > 0) 160 { 161 if (gelf_newphdr (elfb, phnum) == 0) 162 { 163 printf ("cannot create phdrs: %s\n", elf_errmsg (-1)); 164 exit (1); 165 } 166 167 for (size_t cnt = 0; cnt < phnum; ++cnt) 168 { 169 GElf_Phdr phdr_mem; 170 GElf_Phdr *phdr = gelf_getphdr (elfa, cnt, &phdr_mem); 171 if (phdr == NULL) 172 { 173 printf ("couldn't get phdr %zd: %s\n", cnt, elf_errmsg (-1)); 174 exit (1); 175 } 176 177 if (gelf_update_phdr (elfb, cnt, phdr) == 0) 178 { 179 printf ("couldn't update phdr %zd: %s\n", cnt, elf_errmsg (-1)); 180 exit (1); 181 } 182 } 183 } 184 185 /* Copy all sections, headers and data. */ 186 Elf_Scn *scn = NULL; 187 while ((scn = elf_nextscn (elfa, scn)) != NULL) 188 { 189 /* Get the header. */ 190 GElf_Shdr shdr; 191 if (gelf_getshdr (scn, &shdr) == NULL) 192 { 193 printf ("couldn't get shdr: %s\n", elf_errmsg (-1)); 194 exit (1); 195 } 196 197 /* Create new section. */ 198 Elf_Scn *new_scn = elf_newscn (elfb); 199 if (new_scn == NULL) 200 { 201 printf ("couldn't create new section: %s\n", elf_errmsg (-1)); 202 exit (1); 203 } 204 205 if (gelf_update_shdr (new_scn, &shdr) == 0) 206 { 207 printf ("couldn't update shdr: %s\n", elf_errmsg (-1)); 208 exit (1); 209 } 210 211 /* Copy over section data. */ 212 Elf_Data *data = NULL; 213 while ((data = elf_getdata (scn, data)) != NULL) 214 { 215 Elf_Data *new_data = elf_newdata (new_scn); 216 if (new_data == NULL) 217 { 218 printf ("couldn't create new section data: %s\n", 219 elf_errmsg (-1)); 220 exit (1); 221 } 222 *new_data = *data; 223 } 224 } 225 226 /* Write everything to disk. If there are any phdrs then we want 227 the exact same layout. Do we want ELF_F_PERMISSIVE? */ 228 if (phnum > 0) 229 elf_flagelf (elfb, ELF_C_SET, ELF_F_LAYOUT); 230 if (elf_update (elfb, ELF_C_WRITE) < 0) 231 { 232 printf ("failure in elf_update: %s\n", elf_errmsg (-1)); 233 exit (1); 234 } 235 236 if (elf_end (elfa) != 0) 237 { 238 printf ("couldn't cleanup elf '%s': %s\n", in, elf_errmsg (-1)); 239 exit (1); 240 } 241 242 if (close (fda) != 0) 243 { 244 printf ("couldn't close '%s': %s\n", in, strerror (errno)); 245 exit (1); 246 } 247 248 if (elf_end (elfb) != 0) 249 { 250 printf ("couldn't cleanup elf '%s': %s\n", out, elf_errmsg (-1)); 251 exit (1); 252 } 253 254 if (close (fdb) != 0) 255 { 256 printf ("couldn't close '%s': %s\n", out, strerror (errno)); 257 exit (1); 258 } 259 } 260 261 int main(int argc,const char * argv[])262 main (int argc, const char *argv[]) 263 { 264 elf_version (EV_CURRENT); 265 266 /* Takes the given file, and create a new identical one. */ 267 if (argc < 3 || argc > 4) 268 { 269 fprintf (stderr, "elfcopy [--mmap] in.elf out.elf\n"); 270 exit (1); 271 } 272 273 int argn = 1; 274 bool use_mmap = false; 275 if (strcmp (argv[argn], "--mmap") == 0) 276 { 277 use_mmap = true; 278 argn++; 279 } 280 281 const char *in = argv[argn++]; 282 const char *out = argv[argn]; 283 copy_elf (in, out, use_mmap); 284 285 return 0; 286 } 287