1da0c48c4Sopenharmony_ci/* Update data structures for changes and write them out. 2da0c48c4Sopenharmony_ci Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2015 Red Hat, Inc. 3da0c48c4Sopenharmony_ci This file is part of elfutils. 4da0c48c4Sopenharmony_ci Contributed by Ulrich Drepper <drepper@redhat.com>, 1999. 5da0c48c4Sopenharmony_ci 6da0c48c4Sopenharmony_ci This file is free software; you can redistribute it and/or modify 7da0c48c4Sopenharmony_ci it under the terms of either 8da0c48c4Sopenharmony_ci 9da0c48c4Sopenharmony_ci * the GNU Lesser General Public License as published by the Free 10da0c48c4Sopenharmony_ci Software Foundation; either version 3 of the License, or (at 11da0c48c4Sopenharmony_ci your option) any later version 12da0c48c4Sopenharmony_ci 13da0c48c4Sopenharmony_ci or 14da0c48c4Sopenharmony_ci 15da0c48c4Sopenharmony_ci * the GNU General Public License as published by the Free 16da0c48c4Sopenharmony_ci Software Foundation; either version 2 of the License, or (at 17da0c48c4Sopenharmony_ci your option) any later version 18da0c48c4Sopenharmony_ci 19da0c48c4Sopenharmony_ci or both in parallel, as here. 20da0c48c4Sopenharmony_ci 21da0c48c4Sopenharmony_ci elfutils is distributed in the hope that it will be useful, but 22da0c48c4Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 23da0c48c4Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 24da0c48c4Sopenharmony_ci General Public License for more details. 25da0c48c4Sopenharmony_ci 26da0c48c4Sopenharmony_ci You should have received copies of the GNU General Public License and 27da0c48c4Sopenharmony_ci the GNU Lesser General Public License along with this program. If 28da0c48c4Sopenharmony_ci not, see <http://www.gnu.org/licenses/>. */ 29da0c48c4Sopenharmony_ci 30da0c48c4Sopenharmony_ci#ifdef HAVE_CONFIG_H 31da0c48c4Sopenharmony_ci# include <config.h> 32da0c48c4Sopenharmony_ci#endif 33da0c48c4Sopenharmony_ci 34da0c48c4Sopenharmony_ci#include <libelf.h> 35da0c48c4Sopenharmony_ci#include <fcntl.h> 36da0c48c4Sopenharmony_ci#include <sys/stat.h> 37da0c48c4Sopenharmony_ci 38da0c48c4Sopenharmony_ci#include "libelfP.h" 39da0c48c4Sopenharmony_ci 40da0c48c4Sopenharmony_ci 41da0c48c4Sopenharmony_cistatic int64_t 42da0c48c4Sopenharmony_ciwrite_file (Elf *elf, int64_t size, int change_bo, size_t shnum) 43da0c48c4Sopenharmony_ci{ 44da0c48c4Sopenharmony_ci int class = elf->class; 45da0c48c4Sopenharmony_ci 46da0c48c4Sopenharmony_ci /* Check the mode bits now, before modification might change them. */ 47da0c48c4Sopenharmony_ci struct stat st; 48da0c48c4Sopenharmony_ci if (unlikely (fstat (elf->fildes, &st) != 0)) 49da0c48c4Sopenharmony_ci { 50da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_WRITE_ERROR); 51da0c48c4Sopenharmony_ci return -1; 52da0c48c4Sopenharmony_ci } 53da0c48c4Sopenharmony_ci 54da0c48c4Sopenharmony_ci /* Adjust the size in any case. We do this even if we use `write'. 55da0c48c4Sopenharmony_ci We cannot do this if this file is in an archive. We also don't 56da0c48c4Sopenharmony_ci do it *now* if we are shortening the file since this would 57da0c48c4Sopenharmony_ci prevent programs to use the data of the file in generating the 58da0c48c4Sopenharmony_ci new file. We truncate the file later in this case. */ 59da0c48c4Sopenharmony_ci if (elf->parent == NULL 60da0c48c4Sopenharmony_ci && (elf->maximum_size == ~((size_t) 0) 61da0c48c4Sopenharmony_ci || (size_t) size > elf->maximum_size) 62da0c48c4Sopenharmony_ci && unlikely (ftruncate (elf->fildes, size) != 0)) 63da0c48c4Sopenharmony_ci { 64da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_WRITE_ERROR); 65da0c48c4Sopenharmony_ci return -1; 66da0c48c4Sopenharmony_ci } 67da0c48c4Sopenharmony_ci 68da0c48c4Sopenharmony_ci /* Try to map the file if this isn't done yet. */ 69da0c48c4Sopenharmony_ci if (elf->map_address == NULL && elf->cmd == ELF_C_WRITE_MMAP) 70da0c48c4Sopenharmony_ci { 71da0c48c4Sopenharmony_ci elf->map_address = mmap (NULL, size, PROT_READ | PROT_WRITE, 72da0c48c4Sopenharmony_ci MAP_SHARED, elf->fildes, 0); 73da0c48c4Sopenharmony_ci if (unlikely (elf->map_address == MAP_FAILED)) 74da0c48c4Sopenharmony_ci elf->map_address = NULL; 75da0c48c4Sopenharmony_ci else 76da0c48c4Sopenharmony_ci elf->flags |= ELF_F_MMAPPED; 77da0c48c4Sopenharmony_ci } 78da0c48c4Sopenharmony_ci 79da0c48c4Sopenharmony_ci if (elf->map_address != NULL) 80da0c48c4Sopenharmony_ci { 81da0c48c4Sopenharmony_ci /* When using mmap we want to make sure the file content is 82da0c48c4Sopenharmony_ci really there. Only using ftruncate might mean the file is 83da0c48c4Sopenharmony_ci extended, but space isn't allocated yet. This might cause a 84da0c48c4Sopenharmony_ci SIGBUS once we write into the mmapped space and the disk is 85da0c48c4Sopenharmony_ci full. In glibc posix_fallocate is required to extend the 86da0c48c4Sopenharmony_ci file and allocate enough space even if the underlying 87da0c48c4Sopenharmony_ci filesystem would normally return EOPNOTSUPP. But other 88da0c48c4Sopenharmony_ci implementations might not work as expected. And the glibc 89da0c48c4Sopenharmony_ci fallback case might fail (with unexpected errnos) in some cases. 90da0c48c4Sopenharmony_ci So we only report an error when the call fails and errno is 91da0c48c4Sopenharmony_ci ENOSPC. Otherwise we ignore the error and treat it as just hint. */ 92da0c48c4Sopenharmony_ci if (elf->parent == NULL 93da0c48c4Sopenharmony_ci && (elf->maximum_size == ~((size_t) 0) 94da0c48c4Sopenharmony_ci || (size_t) size > elf->maximum_size)) 95da0c48c4Sopenharmony_ci { 96da0c48c4Sopenharmony_ci if (unlikely (posix_fallocate (elf->fildes, 0, size) != 0)) 97da0c48c4Sopenharmony_ci if (errno == ENOSPC) 98da0c48c4Sopenharmony_ci { 99da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_WRITE_ERROR); 100da0c48c4Sopenharmony_ci return -1; 101da0c48c4Sopenharmony_ci } 102da0c48c4Sopenharmony_ci 103da0c48c4Sopenharmony_ci /* Extend the mmap address if needed. */ 104da0c48c4Sopenharmony_ci if (elf->cmd == ELF_C_RDWR_MMAP 105da0c48c4Sopenharmony_ci && (size_t) size > elf->maximum_size) 106da0c48c4Sopenharmony_ci { 107da0c48c4Sopenharmony_ci#ifdef HAVE_MREMAP 108da0c48c4Sopenharmony_ci if (mremap (elf->map_address, elf->maximum_size, 109da0c48c4Sopenharmony_ci size, 0) == MAP_FAILED) 110da0c48c4Sopenharmony_ci#endif 111da0c48c4Sopenharmony_ci { 112da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_WRITE_ERROR); 113da0c48c4Sopenharmony_ci return -1; 114da0c48c4Sopenharmony_ci } 115da0c48c4Sopenharmony_ci elf->maximum_size = size; 116da0c48c4Sopenharmony_ci } 117da0c48c4Sopenharmony_ci 118da0c48c4Sopenharmony_ci } 119da0c48c4Sopenharmony_ci 120da0c48c4Sopenharmony_ci /* The file is mmaped. */ 121da0c48c4Sopenharmony_ci if ((class == ELFCLASS32 122da0c48c4Sopenharmony_ci ? __elf32_updatemmap (elf, change_bo, shnum) 123da0c48c4Sopenharmony_ci : __elf64_updatemmap (elf, change_bo, shnum)) != 0) 124da0c48c4Sopenharmony_ci /* Some problem while writing. */ 125da0c48c4Sopenharmony_ci size = -1; 126da0c48c4Sopenharmony_ci } 127da0c48c4Sopenharmony_ci else 128da0c48c4Sopenharmony_ci { 129da0c48c4Sopenharmony_ci /* The file is not mmaped. */ 130da0c48c4Sopenharmony_ci if ((class == ELFCLASS32 131da0c48c4Sopenharmony_ci ? __elf32_updatefile (elf, change_bo, shnum) 132da0c48c4Sopenharmony_ci : __elf64_updatefile (elf, change_bo, shnum)) != 0) 133da0c48c4Sopenharmony_ci /* Some problem while writing. */ 134da0c48c4Sopenharmony_ci size = -1; 135da0c48c4Sopenharmony_ci } 136da0c48c4Sopenharmony_ci 137da0c48c4Sopenharmony_ci /* Reduce the file size if necessary. */ 138da0c48c4Sopenharmony_ci if (size != -1 139da0c48c4Sopenharmony_ci && elf->parent == NULL 140da0c48c4Sopenharmony_ci && elf->maximum_size != ~((size_t) 0) 141da0c48c4Sopenharmony_ci && (size_t) size < elf->maximum_size 142da0c48c4Sopenharmony_ci && unlikely (ftruncate (elf->fildes, size) != 0)) 143da0c48c4Sopenharmony_ci { 144da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_WRITE_ERROR); 145da0c48c4Sopenharmony_ci size = -1; 146da0c48c4Sopenharmony_ci } 147da0c48c4Sopenharmony_ci 148da0c48c4Sopenharmony_ci /* POSIX says that ftruncate and write may clear the S_ISUID and S_ISGID 149da0c48c4Sopenharmony_ci mode bits. So make sure we restore them afterwards if they were set. 150da0c48c4Sopenharmony_ci This is not atomic if someone else chmod's the file while we operate. */ 151da0c48c4Sopenharmony_ci if (size != -1 152da0c48c4Sopenharmony_ci && unlikely (st.st_mode & (S_ISUID | S_ISGID)) 153da0c48c4Sopenharmony_ci /* fchmod ignores the bits we cannot change. */ 154da0c48c4Sopenharmony_ci && unlikely (fchmod (elf->fildes, st.st_mode) != 0)) 155da0c48c4Sopenharmony_ci { 156da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_WRITE_ERROR); 157da0c48c4Sopenharmony_ci size = -1; 158da0c48c4Sopenharmony_ci } 159da0c48c4Sopenharmony_ci 160da0c48c4Sopenharmony_ci if (size != -1 && elf->parent == NULL) 161da0c48c4Sopenharmony_ci elf->maximum_size = size; 162da0c48c4Sopenharmony_ci 163da0c48c4Sopenharmony_ci return size; 164da0c48c4Sopenharmony_ci} 165da0c48c4Sopenharmony_ci 166da0c48c4Sopenharmony_ci 167da0c48c4Sopenharmony_ciint64_t 168da0c48c4Sopenharmony_cielf_update (Elf *elf, Elf_Cmd cmd) 169da0c48c4Sopenharmony_ci{ 170da0c48c4Sopenharmony_ci size_t shnum; 171da0c48c4Sopenharmony_ci int64_t size; 172da0c48c4Sopenharmony_ci int change_bo = 0; 173da0c48c4Sopenharmony_ci 174da0c48c4Sopenharmony_ci if (cmd != ELF_C_NULL 175da0c48c4Sopenharmony_ci && cmd != ELF_C_WRITE 176da0c48c4Sopenharmony_ci && unlikely (cmd != ELF_C_WRITE_MMAP)) 177da0c48c4Sopenharmony_ci { 178da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_INVALID_CMD); 179da0c48c4Sopenharmony_ci return -1; 180da0c48c4Sopenharmony_ci } 181da0c48c4Sopenharmony_ci 182da0c48c4Sopenharmony_ci if (elf == NULL) 183da0c48c4Sopenharmony_ci return -1; 184da0c48c4Sopenharmony_ci 185da0c48c4Sopenharmony_ci if (elf->kind != ELF_K_ELF) 186da0c48c4Sopenharmony_ci { 187da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_INVALID_HANDLE); 188da0c48c4Sopenharmony_ci return -1; 189da0c48c4Sopenharmony_ci } 190da0c48c4Sopenharmony_ci 191da0c48c4Sopenharmony_ci rwlock_wrlock (elf->lock); 192da0c48c4Sopenharmony_ci 193da0c48c4Sopenharmony_ci /* Make sure we have an ELF header. */ 194da0c48c4Sopenharmony_ci if (elf->state.elf.ehdr == NULL) 195da0c48c4Sopenharmony_ci { 196da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_WRONG_ORDER_EHDR); 197da0c48c4Sopenharmony_ci size = -1; 198da0c48c4Sopenharmony_ci goto out; 199da0c48c4Sopenharmony_ci } 200da0c48c4Sopenharmony_ci 201da0c48c4Sopenharmony_ci /* Determine the number of sections. */ 202da0c48c4Sopenharmony_ci shnum = (elf->state.elf.scns_last->cnt == 0 203da0c48c4Sopenharmony_ci ? 0 204da0c48c4Sopenharmony_ci : 1 + elf->state.elf.scns_last->data[elf->state.elf.scns_last->cnt - 1].index); 205da0c48c4Sopenharmony_ci 206da0c48c4Sopenharmony_ci /* Update the ELF descriptor. First, place the program header. It 207da0c48c4Sopenharmony_ci will come right after the ELF header. The count the size of all 208da0c48c4Sopenharmony_ci sections and finally place the section table. */ 209da0c48c4Sopenharmony_ci size = (elf->class == ELFCLASS32 210da0c48c4Sopenharmony_ci ? __elf32_updatenull_wrlock (elf, &change_bo, shnum) 211da0c48c4Sopenharmony_ci : __elf64_updatenull_wrlock (elf, &change_bo, shnum)); 212da0c48c4Sopenharmony_ci if (likely (size != -1) 213da0c48c4Sopenharmony_ci /* See whether we actually have to write out the data. */ 214da0c48c4Sopenharmony_ci && (cmd == ELF_C_WRITE || cmd == ELF_C_WRITE_MMAP)) 215da0c48c4Sopenharmony_ci { 216da0c48c4Sopenharmony_ci if (elf->cmd != ELF_C_RDWR 217da0c48c4Sopenharmony_ci && elf->cmd != ELF_C_RDWR_MMAP 218da0c48c4Sopenharmony_ci && elf->cmd != ELF_C_WRITE 219da0c48c4Sopenharmony_ci && unlikely (elf->cmd != ELF_C_WRITE_MMAP)) 220da0c48c4Sopenharmony_ci { 221da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_UPDATE_RO); 222da0c48c4Sopenharmony_ci size = -1; 223da0c48c4Sopenharmony_ci } 224da0c48c4Sopenharmony_ci else if (unlikely (elf->fildes == -1)) 225da0c48c4Sopenharmony_ci { 226da0c48c4Sopenharmony_ci /* We closed the file already. */ 227da0c48c4Sopenharmony_ci __libelf_seterrno (ELF_E_FD_DISABLED); 228da0c48c4Sopenharmony_ci size = -1; 229da0c48c4Sopenharmony_ci } 230da0c48c4Sopenharmony_ci else 231da0c48c4Sopenharmony_ci size = write_file (elf, size, change_bo, shnum); 232da0c48c4Sopenharmony_ci } 233da0c48c4Sopenharmony_ci 234da0c48c4Sopenharmony_ci out: 235da0c48c4Sopenharmony_ci rwlock_unlock (elf->lock); 236da0c48c4Sopenharmony_ci 237da0c48c4Sopenharmony_ci return size; 238da0c48c4Sopenharmony_ci} 239