Fixed multi-archness of the `demo' script.
[libobjid.git] / build.c
1 /* $Id$ */
2
3
4 #include <stdio.h>
5 #include <link.h>
6 #include <assert.h>
7 #include <asm/page.h>
8 #include <sys/mman.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <stddef.h>
12
13 #include "build.h"
14 #include "objid.h"
15 #include "common.h"
16 #include "maps.h"
17 #include "hook.h"
18 #include "hook-arch-c.h"
19
20
21 /* Make the checksumming faster but core file specific by mincore(2)?
22    For some important swapped out libraries the checksum would get void.  */
23
24
25 static ElfW(LibobjidHdr) *header;
26 static size_t pages_size;
27 static unsigned obj_allocated;
28 static ElfW(LibobjidObj) *obj;
29
30
31 static void header_update (void)
32 {
33   obj_allocated = (pages_size - offsetof (ElfW(LibobjidHdr), obj)) / sizeof (header->obj[0]);
34   assert (obj_allocated >= header->obj_count);
35 }
36
37 static int header_init (void)
38 {
39   pages_size = PAGE_SIZE;
40   header = mmap (NULL, pages_size, PROT_READ | PROT_WRITE,
41                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
42   if (header == MAP_FAILED)
43     {
44       if (debug >= 1)
45         fprintf (stderr, "libobjid: Failed to map %lu bytes: %m\n",
46                  (unsigned long) pages_size);
47       return -1;
48     }
49   header->obj_count = 0;
50   header_update ();
51
52   return 0;
53 }
54
55 static int header_resize (size_t pages_size_new)
56 {
57   ElfW(LibobjidHdr) *header_new;
58
59   pages_size_new = (pages_size_new + PAGE_SIZE - 1) & PAGE_MASK;
60
61   header_new = mremap (header, pages_size, pages_size_new, MREMAP_MAYMOVE);
62   if (header_new == MAP_FAILED)
63     {
64       if (debug >= 1)
65         fprintf (stderr, "libobjid: Failed to remap %lu to %lu bytes: %m\n",
66                  (unsigned long) pages_size, (unsigned long) pages_size_new);
67       return -1;
68     }
69
70   header = header_new;
71   pages_size = pages_size_new;
72   header_update ();
73
74   return 0;
75 }
76
77 static void header_finish (void)
78 {
79   size_t strings_size = 0;
80   unsigned obji;
81   char *string_ptr;
82
83   for (obji = 0; obji < header->obj_count; obji++)
84     strings_size += strlen ((const char *) header->obj[obji].filename) + 1;
85
86   header->size = sizeof (*header) + header->obj_count * sizeof (*header->obj)
87                  + strings_size;
88   /* Possibly even shrink the allocated pages.  */
89   header_resize (header->size);
90
91   /* Do not reuse any address above, header_resize() could move it.  */
92   string_ptr = (char *) header + sizeof (*header)
93                + header->obj_count * sizeof (*header->obj);
94
95   for (obji = 0; obji < header->obj_count; obji++)
96     {
97       const char *filename_orig = (const char *) header->obj[obji].filename;
98
99       strcpy (string_ptr, filename_orig);
100       header->obj[obji].filename = (ElfW (Addr)) string_ptr;
101       string_ptr += strlen (filename_orig) + 1;
102     }
103   assert (string_ptr == (char *) header + header->size);
104
105   header->magic = ElfW(Magic);
106   header->self = (ElfW(Addr)) header;
107   header->self_not = ~(ElfW(Addr)) header;
108 }
109
110 /* Returns non-zero on error.  */
111 static int checksum_open (const char *filename)
112 {
113   assert (header != NULL);
114
115   if (header->obj_count == obj_allocated)
116     {
117       assert (pages_size >= 1);
118       if (header_resize (pages_size * 2))
119         return -1;
120       assert (header->obj_count < obj_allocated);
121     }
122
123   obj = &header->obj[header->obj_count];
124
125   /* It should not happen but avoid the array termination.  */
126   if (filename == NULL)
127     filename = "";
128
129   obj->filename = (ElfW(Addr)) filename;
130   obj->checksum = 0;
131   if (debug >= 1)
132     fprintf (stderr, "%s\n", filename);
133
134   return 0;
135 }
136
137 static void checksum_data (ElfW(Word) *sum, void *start, void *end)
138 {
139   unsigned char *ptr;
140
141   for (ptr = start; (void *) ptr < end; ptr++)
142     *sum = *sum * 31 + *ptr;
143
144   if (debug >= 1)
145     fprintf (stderr, "\t%p - %p (0x%lx)\n", start, end, (unsigned long) *sum);
146 }
147
148 static void checksum_close (void)
149 {
150   header->obj_count++;
151   
152   assert (header->obj_count <= obj_allocated);
153 }
154
155 /* Range is provided only for debug purposes.  */
156 static void checksum_cancel (void *start, void *end)
157 {
158   if (debug >= 1)
159     fprintf (stderr, "\tCANCEL: %p - %p\n", start, end);
160 }
161
162 static int phdr_callback (struct dl_phdr_info *info, size_t size, void *data)
163 {
164   int j;
165   int started = 0;
166
167   for (j = 0; j < info->dlpi_phnum; j++)
168     {
169       void *start, *end;
170
171       if (info->dlpi_phdr[j].p_type != PT_LOAD)
172         continue;
173       if (!(info->dlpi_phdr[j].p_flags & PF_R))
174         continue;
175       /* Present?  We probably could not checksum such one. */
176       if (info->dlpi_phdr[j].p_flags & PF_W)
177         continue;
178       if (!started)
179         {
180           /* No longer storage space?  No other object may succeed.  */
181           if (checksum_open (info->dlpi_name) != 0)
182             return 1;
183           started = 1;
184         }
185       start = (void *) (info->dlpi_addr + info->dlpi_phdr[j].p_vaddr);
186       end = start + info->dlpi_phdr[j].p_memsz;
187       /* Use maps_verify() after checksum_open() for more debugging info.  */
188       if (maps_verify (start, end) != 0)
189         {
190           /* This object's checksum is invalid but other objects may be
191              checksummable.  */
192           if (started)
193             checksum_cancel (start, end);
194           return 0;
195         }
196       if ((void *) sigaction_orig_libc_cont < start
197           || (void *) ((char *) sigaction_orig_libc_cont - trampoline_size)
198              >= end)
199         checksum_data (&obj->checksum, start, end);
200       else
201         {
202           checksum_data (&obj->checksum, start,
203                          (char *) sigaction_orig_libc_cont - trampoline_size);
204           checksum_data (&obj->checksum, sigaction_trampoline,
205                          (char *) sigaction_trampoline + trampoline_size);
206           checksum_data (&obj->checksum, sigaction_orig_libc_cont, end);
207         }
208     }
209
210   if (started)
211     checksum_close ();
212
213   return 0;
214 }
215
216 INTERNAL void core_dump (void)
217 {
218   const char *msg = "libobjid: core\n";
219   write (STDERR_FILENO, msg, strlen (msg));
220
221   if (header_init ())
222     return;
223   maps_read ();
224   dl_iterate_phdr (phdr_callback, NULL);
225   header_finish ();
226 }