]> sigrok.org Git - sigrok-androidutils.git/blame - ant/src/org/sigrok/androidutils/ant/CopyLibsTask.java
CopyLibsTask: Fix null dereference in getLibForSoname
[sigrok-androidutils.git] / ant / src / org / sigrok / androidutils / ant / CopyLibsTask.java
CommitLineData
ea3ce762 1/*
5de7ce63 2 * This file is part of the sigrok-androidutils project.
ea3ce762
MC
3 *
4 * Copyright (C) 2014 Marcus Comstedt <marcus@mc.pp.se>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20package org.sigrok.androidutils.ant;
21
22import java.io.File;
23import java.io.IOException;
24import java.io.FileInputStream;
25import java.io.FileOutputStream;
26import java.io.InputStream;
27import java.io.OutputStream;
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.Collections;
31import java.util.HashMap;
32import java.util.HashSet;
33import java.util.LinkedList;
34import java.util.Queue;
35import java.util.TreeSet;
36import java.util.Vector;
37import org.apache.tools.ant.BuildException;
38import org.apache.tools.ant.Task;
39import org.apache.tools.ant.types.FileSet;
40import org.apache.tools.ant.types.PatternSet;
41import org.apache.tools.ant.types.Resource;
42import org.apache.tools.ant.types.ResourceCollection;
43import org.apache.tools.ant.types.resources.FileProvider;
44import org.apache.tools.ant.types.selectors.SelectorUtils;
45
6f1c3a93
UH
46public class CopyLibsTask extends Task
47{
48 private static BuildException buildException(Exception e)
49 {
50 if (e instanceof BuildException)
51 return (BuildException)e;
ea3ce762 52 else
6f1c3a93 53 return new BuildException(e);
ea3ce762
MC
54 }
55
6f1c3a93 56 private static int indexOf(byte[] data, byte v, int start)
ea3ce762 57 {
6f1c3a93
UH
58 if (data != null) {
59 for (int i = start; i < data.length; i++) {
60 if (data[i] == v)
61 return i;
62 }
63 }
64 return -1;
ea3ce762
MC
65 }
66
6f1c3a93 67 private static String fixSoname(String s)
ea3ce762 68 {
6f1c3a93
UH
69 int l = s.length();
70 int i = s.lastIndexOf(".so");
ea3ce762 71
6f1c3a93
UH
72 if (i >= 0 && i < (l - 3))
73 return s.substring(0, i + 3);
74 else
75 return s;
ea3ce762
MC
76 }
77
6f1c3a93 78 protected class Library implements Comparable<Library>
ea3ce762 79 {
6f1c3a93
UH
80 protected final File file;
81 protected final ElfFile elf;
82 protected final HashSet<String> needed;
83 protected final Vector<String> rpath;
84 protected final TreeSet<Range> fixups;
85 protected final String subdir;
86 protected String soname, destname;
87 protected final HashSet<Library> dependencies;
88 protected boolean dependedUpon;
89
90 protected class Range implements Comparable<Range>
91 {
92 public final long start, end;
93
94 public int compareTo(Range r)
95 {
96 if (start < r.start)
97 return -1;
98 else if (start > r.start)
99 return 1;
100 else if (end < r.end)
101 return -1;
102 else if (end > r.end)
103 return 1;
104 else
105 return 0;
106 }
107
108 public Range(long start, long end)
109 {
110 this.start = start;
111 this.end = end;
112 }
ea3ce762 113 }
ea3ce762 114
6f1c3a93
UH
115 public int compareTo(Library l)
116 {
117 return destname.compareTo(l.destname);
118 }
ea3ce762 119
6f1c3a93
UH
120 protected void addNeeded(String so)
121 {
122 needed.add(so);
123 }
ea3ce762 124
6f1c3a93
UH
125 protected void addRpath(String rp)
126 {
127 for (String p : rp.split(":")) {
128 if (!rpath.contains(p))
129 rpath.add(p);
130 }
131 }
ea3ce762 132
6f1c3a93
UH
133 private String getDynstr(ElfFile.Dynamic d, byte[] s,
134 long base) throws Exception
135 {
136 int offs = (int)d.d_val;
137 int nul = indexOf(s, (byte)0, offs);
138
139 if (nul < 0)
140 throw new Exception("Invalid dynamic string");
141
142 String name = new String(s, offs, nul-offs, "US-ASCII");
143 offs += base;
144
0c38754c
MC
145 if (d.d_tag == ElfFile.DT_RPATH ||
146 d.d_tag == ElfFile.DT_RUNPATH) {
6f1c3a93
UH
147 // Zap rpath,
148 fixups.add(new Range(offs, offs + name.length()));
149 } else {
150 String fix = fixSoname(name);
151 if (fix.length() < name.length()) {
152 fixups.add(new Range(offs + fix.length(),
153 offs + name.length()));
154 }
155 }
156 return name;
ea3ce762 157 }
6f1c3a93
UH
158
159 private void checkDynamic(ElfFile.SectionHeader dynsh,
160 ElfFile.SectionHeader strsh) throws Exception
161 {
162 byte strs[] = new byte[(int)strsh.sh_size];
163
164 elf.read(strsh, strs);
165
166 for (ElfFile.Dynamic d : elf.readDynamic(dynsh)) {
167 if (d.d_tag == ElfFile.DT_NULL)
168 break;
169 else if (d.d_tag == ElfFile.DT_SONAME)
170 soname = getDynstr(d, strs, strsh.sh_offset);
171 else if (d.d_tag == ElfFile.DT_NEEDED)
172 addNeeded(getDynstr(d, strs, strsh.sh_offset));
0c38754c
MC
173 else if (d.d_tag == ElfFile.DT_RPATH ||
174 d.d_tag == ElfFile.DT_RUNPATH)
6f1c3a93
UH
175 addRpath(getDynstr(d, strs, strsh.sh_offset));
176 }
ea3ce762 177 }
6f1c3a93
UH
178
179 private void checkElf() throws Exception
180 {
181 for (ElfFile.SectionHeader sh : elf.secHeaders) {
182 if (sh.sh_type == ElfFile.SHT_DYNAMIC)
183 checkDynamic(sh, elf.secHeaders[sh.sh_link]);
ea3ce762 184 }
ea3ce762 185 }
ea3ce762 186
6f1c3a93
UH
187 protected File getDestName(File dest)
188 {
189 File d = new File(dest, subdir);
190 File f = new File(d, destname);
191 return f;
192 }
ea3ce762 193
6f1c3a93
UH
194 protected void writeTo(File dest) throws IOException
195 {
196 FileInputStream is = new FileInputStream(file);
197 FileOutputStream os = new FileOutputStream(dest);
198 byte[] buf = new byte[65536];
199 TreeSet<Range> ranges = new TreeSet<Range>(fixups);
200 long offs = 0;
201
202 outer: for(;;) {
203 long next = offs + buf.length;
204 if (!ranges.isEmpty())
205 next = ranges.first().start;
206 if (next > offs) {
207 long chunk = next - offs;
208 if (chunk > buf.length)
209 chunk = buf.length;
210 int r = is.read(buf, 0, (int)chunk);
211 if (r < 0)
212 break;
213 os.write(buf, 0, r);
214 offs += r;
215 continue;
216 }
217 while (!ranges.isEmpty() && ranges.first().start <= offs) {
218 Range rg = ranges.pollFirst();
219 if (rg.end > offs) {
220 long chunk = rg.end - offs;
221 while (chunk > 0) {
222 int slice = (chunk > buf.length ? buf.length : (int)chunk);
223 int r = is.read(buf, 0, slice);
224 if (r < 0)
225 break outer;
226 if (r > 0) {
227 Arrays.fill(buf, 0, r, (byte)0);
228 os.write(buf, 0, r);
229 chunk -= r;
230 }
231 }
232 offs = rg.end;
233 }
234 }
235 }
ea3ce762 236
6f1c3a93
UH
237 os.close();
238 is.close();
239 }
ea3ce762 240
6f1c3a93
UH
241 protected Library(File f, String s) throws Exception
242 {
243 file = f;
244 subdir = s;
245 elf = new ElfFile(file);
246 needed = new HashSet<String>();
247 rpath = new Vector<String>();
248 fixups = new TreeSet<Range>();
249 soname = f.getName();
250 dependencies = new HashSet<Library>();
251 dependedUpon = false;
252 checkElf();
253 destname = fixSoname(soname);
254 }
ea3ce762 255
6f1c3a93
UH
256 protected Library(Resource r) throws Exception
257 {
258 this(r.as(FileProvider.class).getFile(),
259 new File(r.getName()).getParent());
260 }
ea3ce762 261
6f1c3a93
UH
262 public String toString()
263 {
264 return "Library(" + file + ")";
265 }
ea3ce762 266
6f1c3a93 267 };
ea3ce762 268
6f1c3a93 269 protected class Worker
ea3ce762 270 {
6f1c3a93
UH
271 protected final int machine;
272 protected final Queue<Library> workQueue;
273 protected final HashMap<String,Library> knownLibs;
274 protected final HashSet<Library> processedLibs;
275 protected final HashSet<String> allDests;
276 protected final Vector<String> rpath;
277
278 protected void addWork(Library l)
279 {
280 if (l == null)
281 return;
282 Library kl = knownLibs.get(l.soname);
283 if (kl == l)
284 return; // Already processed.
285 if (kl != null)
286 throw new BuildException("Multiple libs with the same soname " + l.soname);
287 knownLibs.put(l.soname, l);
288 if (allDests.contains(l.destname))
289 throw new BuildException("Multiple libs with simplified soname " + l.destname);
290 allDests.add(l.destname);
291 workQueue.add(l);
292 }
293
294 protected void addRpath(Vector<String> rp)
295 {
296 for (String p : rp) {
297 if (!rpath.contains(p))
298 rpath.add(p);
299 }
300 }
301
302 protected void setDependency(Library l1, Library l2)
303 {
ed3a4c3c 304 if (l2 == null) // Dependency on external lib.
6f1c3a93
UH
305 return;
306 l1.dependencies.add(l2);
307 l2.dependedUpon = true;
308 }
309
310 protected Library findLibInRpath(String s, String subdir)
311 throws Exception
312 {
313 for (String p : rpath) {
314 File f = new File(p, s);
315 if (f.exists()) {
316 Library l = new Library(f, subdir);
317 if (l.elf.header.e_machine == machine)
318 return l;
319 }
320 }
321 return null;
322 }
323
324 protected Library getLibForSoname(String s, String subdir)
325 throws Exception
326 {
327 Library l = knownLibs.get(s);
328 if (l != null)
329 return l;
330 boolean include = false;
4785171b
MC
331 String[] includePatterns = patterns.getIncludePatterns(getProject());
332 if (includePatterns != null) {
333 for (String patt : includePatterns) {
334 if (SelectorUtils.match(patt, s)) {
335 include = true;
336 break;
337 }
6f1c3a93
UH
338 }
339 }
340 if (!include) {
4785171b
MC
341 String[] excludePatterns = patterns.getExcludePatterns(getProject());
342 if (excludePatterns != null) {
343 for (String patt : excludePatterns) {
344 if (SelectorUtils.match(patt, s))
345 return null;
346 }
6f1c3a93
UH
347 }
348 }
349 l = findLibInRpath(s, subdir);
350 if (l == null)
351 throw new Exception("Library " + s + " not found");
352 addWork(l);
ea3ce762
MC
353 return l;
354 }
6f1c3a93
UH
355
356 protected void process(Library l) throws Exception
357 {
358 if (processedLibs.contains(l))
ed3a4c3c 359 return; // Already processed.
6f1c3a93
UH
360 processedLibs.add(l);
361 addRpath(l.rpath);
362 for (String need : l.needed)
363 setDependency(l, getLibForSoname(need, l.subdir));
364 }
365
366 protected Vector<Library> topoSort(HashSet<Library> libs)
367 {
368 Vector<Library> order = new Vector<Library>();
369 for (Library chk : new HashSet<Library>(libs)) {
370 if (!chk.dependedUpon)
371 libs.remove(chk);
372 }
373 while (!libs.isEmpty()) {
374 HashSet<Library> leafs = new HashSet<Library>();
375 for (Library chk : new HashSet<Library>(libs)) {
376 if (chk.dependencies.isEmpty())
377 leafs.add(chk);
378 }
379 if (leafs.isEmpty())
380 throw new BuildException("Circular dependency found");
381 ArrayList<Library> llist = new ArrayList<Library>(leafs);
382 Collections.sort(llist);
383 order.addAll(llist);
384 libs.removeAll(leafs);
385 for (Library l : libs)
386 l.dependencies.removeAll(leafs);
387 }
388 return order;
389 }
390
391 protected void execute() throws BuildException
392 {
393 try {
394 while (!workQueue.isEmpty())
395 process(workQueue.remove());
396 } catch (Exception e) {
397 throw buildException(e);
398 }
399 if (property != null) {
400 Vector<Library> order =
ed3a4c3c 401 topoSort(new HashSet<Library>(processedLibs));
6f1c3a93
UH
402 StringBuilder sb = new StringBuilder();
403 for (Library l : order) {
404 String name = l.destname;
405 if (name.startsWith("lib"))
406 name = name.substring(3);
407 if (name.endsWith(".so"))
408 name = name.substring(0, name.length() - 3);
409 sb.append(" <item>");
410 sb.append(name);
411 sb.append("</item>\n");
412 }
413 String orderedLibs = sb.toString();
414 getProject().setNewProperty(property, orderedLibs);
415 }
416 for (Library chk : new HashSet<Library>(processedLibs)) {
417 File dest = chk.getDestName(destDir);
418 if (dest.exists() &&
419 dest.lastModified() >= chk.file.lastModified())
420 processedLibs.remove(chk);
421 dest = dest.getParentFile();
422 if (!dest.exists())
423 dest.mkdirs();
424 }
425 if (processedLibs.isEmpty())
426 return;
427 log("Copying " + processedLibs.size() + " libraries into " + destDir);
428 ArrayList<Library> libs = new ArrayList<Library>(processedLibs);
429 Collections.sort(libs);
430 try {
431 for (Library l : libs)
432 l.writeTo(l.getDestName(destDir));
433 } catch (Exception e) {
434 throw buildException(e);
435 }
436 }
437
438 protected Worker(int mach)
439 {
440 machine = mach;
441 workQueue = new LinkedList<Library>();
442 knownLibs = new HashMap<String,Library>();
443 processedLibs = new HashSet<Library>();
444 allDests = new HashSet<String>();
445 rpath = new Vector<String>();
446 }
447
448 };
449
450 protected File destDir = null; // The destination directory.
451 protected Vector<ResourceCollection> rcs = new Vector<ResourceCollection>();
452 protected PatternSet patterns = new PatternSet();
453 protected String property = null;
454
455 public void setTodir(File destDir)
456 {
457 this.destDir = destDir;
ea3ce762
MC
458 }
459
6f1c3a93 460 public void addFileset(FileSet set)
ea3ce762 461 {
6f1c3a93 462 add(set);
ea3ce762
MC
463 }
464
6f1c3a93 465 public void add(ResourceCollection res)
ea3ce762 466 {
6f1c3a93 467 rcs.add(res);
ea3ce762
MC
468 }
469
6f1c3a93 470 public PatternSet.NameEntry createExclude()
ea3ce762 471 {
6f1c3a93 472 return patterns.createExclude();
ea3ce762
MC
473 }
474
6f1c3a93 475 public PatternSet.NameEntry createInclude()
ea3ce762 476 {
6f1c3a93 477 return patterns.createInclude();
ea3ce762
MC
478 }
479
6f1c3a93 480 public void setProperty(String prop)
ea3ce762 481 {
6f1c3a93 482 property = prop;
ea3ce762 483 }
6f1c3a93
UH
484
485 public void execute() throws BuildException
486 {
487 HashMap<Integer,Worker> workers = new HashMap<Integer,Worker>();
488 final int size = rcs.size();
489
490 for (int i = 0; i < size; i++) {
491 ResourceCollection rc = rcs.elementAt(i);
492 for (Resource r : rc) {
493 if (!r.isExists()) {
494 String message = "Could not find library "
495 + r.toLongString() + " to copy.";
496 throw new BuildException(message);
497 }
498 Library l;
499 try {
500 l = new Library(r);
501 } catch (Exception e) {
502 throw buildException(e);
503 }
504 Integer m = new Integer(l.elf.header.e_machine);
505 Worker w = workers.get(m);
506 if (w == null)
507 workers.put(m, (w = new Worker(m.intValue())));
508 w.addWork(l);
509 }
ea3ce762 510 }
6f1c3a93
UH
511 ArrayList<Integer> machines = new ArrayList<Integer>(workers.keySet());
512 Collections.sort(machines);
513 for (Integer m : machines)
514 workers.get(m).execute();
ea3ce762 515 }
ea3ce762 516}