Takuto Ikuta | 3dab32e0 | 2023-01-12 18:52:00 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
Avi Drissman | 73a09d1 | 2022-09-08 20:33:38 | [diff] [blame] | 2 | # Copyright 2019 The Chromium Authors |
Christopher Grant | 3b06acce | 2019-04-25 18:26:41 | [diff] [blame] | 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | """Extracts an LLD partition from an ELF file.""" |
| 6 | |
| 7 | import argparse |
Charlie Hu | a61ff7ca | 2021-08-03 05:08:26 | [diff] [blame] | 8 | import hashlib |
Charlie Hu | a61ff7ca | 2021-08-03 05:08:26 | [diff] [blame] | 9 | import os |
| 10 | import struct |
Christopher Grant | 3b06acce | 2019-04-25 18:26:41 | [diff] [blame] | 11 | import subprocess |
| 12 | import sys |
Charlie Hu | a61ff7ca | 2021-08-03 05:08:26 | [diff] [blame] | 13 | import tempfile |
| 14 | |
| 15 | |
| 16 | def_ComputeNewBuildId(old_build_id, file_path): |
| 17 | """ |
| 18 | Computes the new build-id from old build-id and file_path. |
| 19 | |
| 20 | Args: |
| 21 | old_build_id: Original build-id in bytearray. |
| 22 | file_path: Path to output ELF file. |
| 23 | |
| 24 | Returns: |
| 25 | New build id with the same length as |old_build_id|. |
| 26 | """ |
| 27 | m= hashlib.sha256() |
| 28 | m.update(old_build_id) |
| 29 | m.update(os.path.basename(file_path).encode('utf-8')) |
| 30 | hash_bytes= m.digest() |
| 31 | # In case build_id is longer than hash computed, repeat the hash |
| 32 | # to the desired length first. |
| 33 | id_size= len(old_build_id) |
| 34 | hash_size= len(hash_bytes) |
| 35 | return(hash_bytes*(id_size// hash_size+1))[:id_size] |
| 36 | |
| 37 | |
| 38 | def_ExtractPartition(objcopy, input_elf, output_elf, partition): |
| 39 | """ |
| 40 | Extracts a partition from an ELF file. |
| 41 | |
| 42 | For partitions other than main partition, we need to rewrite |
| 43 | the .note.gnu.build-id section so that the build-id remains |
| 44 | unique. |
| 45 | |
| 46 | Note: |
| 47 | - `objcopy` does not modify build-id when partitioning the |
| 48 | combined ELF file by default. |
| 49 | - The new build-id is calculated as hash of original build-id |
| 50 | and partitioned ELF file name. |
| 51 | |
| 52 | Args: |
| 53 | objcopy: Path to objcopy binary. |
| 54 | input_elf: Path to input ELF file. |
| 55 | output_elf: Path to output ELF file. |
| 56 | partition: Partition to extract from combined ELF file. None when |
| 57 | extracting main partition. |
| 58 | """ |
| 59 | ifnot partition:# main partition |
| 60 | # We do not overwrite build-id on main partition to allow the expected |
| 61 | # partition build ids to be synthesized given a libchrome.so binary, |
| 62 | # if necessary. |
| 63 | subprocess.check_call( |
| 64 | [objcopy,'--extract-main-partition', input_elf, output_elf]) |
| 65 | return |
| 66 | |
| 67 | # partitioned libs |
| 68 | build_id_section='.note.gnu.build-id' |
| 69 | |
| 70 | with tempfile.TemporaryDirectory()as tempdir: |
| 71 | temp_elf= os.path.join(tempdir,'obj_without_id.so') |
| 72 | old_build_id_file= os.path.join(tempdir,'old_build_id') |
| 73 | new_build_id_file= os.path.join(tempdir,'new_build_id') |
| 74 | |
Charlie Hu | b4ae21c | 2024-02-08 19:18:57 | [diff] [blame] | 75 | # Dump out build-id section. |
Charlie Hu | a61ff7ca | 2021-08-03 05:08:26 | [diff] [blame] | 76 | subprocess.check_call([ |
| 77 | objcopy, |
| 78 | '--extract-partition', |
| 79 | partition, |
Charlie Hu | a61ff7ca | 2021-08-03 05:08:26 | [diff] [blame] | 80 | '--dump-section', |
| 81 | '{}={}'.format(build_id_section, old_build_id_file), |
| 82 | input_elf, |
| 83 | temp_elf, |
| 84 | ]) |
| 85 | |
| 86 | with open(old_build_id_file,'rb')as f: |
| 87 | note_content= f.read() |
| 88 | |
| 89 | # .note section has following format according to <elf/external.h> |
| 90 | # typedef struct { |
| 91 | # unsigned char namesz[4]; /* Size of entry's owner string */ |
| 92 | # unsigned char descsz[4]; /* Size of the note descriptor */ |
| 93 | # unsigned char type[4]; /* Interpretation of the descriptor */ |
| 94 | # char name[1]; /* Start of the name+desc data */ |
| 95 | # } Elf_External_Note; |
| 96 | # `build-id` rewrite is only required on Android platform, |
| 97 | # where we have partitioned lib. |
| 98 | # Android platform uses little-endian. |
| 99 | # <: little-endian |
| 100 | # 4x: Skip 4 bytes |
| 101 | # L: unsigned long, 4 bytes |
| 102 | descsz,= struct.Struct('<4xL').unpack_from(note_content) |
| 103 | prefix= note_content[:-descsz] |
| 104 | build_id= note_content[-descsz:] |
| 105 | |
| 106 | with open(new_build_id_file,'wb')as f: |
| 107 | f.write(prefix+_ComputeNewBuildId(build_id, output_elf)) |
| 108 | |
Charlie Hu | b4ae21c | 2024-02-08 19:18:57 | [diff] [blame] | 109 | # Update the build-id section. |
Charlie Hu | a61ff7ca | 2021-08-03 05:08:26 | [diff] [blame] | 110 | subprocess.check_call([ |
| 111 | objcopy, |
Charlie Hu | b4ae21c | 2024-02-08 19:18:57 | [diff] [blame] | 112 | '--update-section', |
Charlie Hu | a61ff7ca | 2021-08-03 05:08:26 | [diff] [blame] | 113 | '{}={}'.format(build_id_section, new_build_id_file), |
Charlie Hu | e9c4b8c | 2024-01-26 19:41:44 | [diff] [blame] | 114 | temp_elf, |
Charlie Hu | a61ff7ca | 2021-08-03 05:08:26 | [diff] [blame] | 115 | output_elf, |
| 116 | ]) |
Christopher Grant | 3b06acce | 2019-04-25 18:26:41 | [diff] [blame] | 117 | |
| 118 | |
| 119 | def main(): |
| 120 | parser= argparse.ArgumentParser(description=__doc__) |
| 121 | parser.add_argument( |
| 122 | '--partition', |
| 123 | help='Name of partition if not the main partition', |
| 124 | metavar='PART') |
| 125 | parser.add_argument( |
| 126 | '--objcopy', |
| 127 | required=True, |
| 128 | help='Path to llvm-objcopy binary', |
| 129 | metavar='FILE') |
| 130 | parser.add_argument( |
| 131 | '--unstripped-output', |
| 132 | required=True, |
| 133 | help='Unstripped output file', |
| 134 | metavar='FILE') |
| 135 | parser.add_argument( |
| 136 | '--stripped-output', |
| 137 | required=True, |
| 138 | help='Stripped output file', |
| 139 | metavar='FILE') |
Andrew Grieve | d1828005a | 2021-10-28 16:52:17 | [diff] [blame] | 140 | parser.add_argument('--split-dwarf', action='store_true') |
Christopher Grant | 3b06acce | 2019-04-25 18:26:41 | [diff] [blame] | 141 | parser.add_argument('input', help='Input file') |
| 142 | args= parser.parse_args() |
| 143 | |
Charlie Hu | a61ff7ca | 2021-08-03 05:08:26 | [diff] [blame] | 144 | _ExtractPartition(args.objcopy, args.input, args.unstripped_output, |
| 145 | args.partition) |
| 146 | subprocess.check_call([ |
| 147 | args.objcopy, |
| 148 | '--strip-all', |
| 149 | args.unstripped_output, |
| 150 | args.stripped_output, |
| 151 | ]) |
Christopher Grant | 3b06acce | 2019-04-25 18:26:41 | [diff] [blame] | 152 | |
Andrew Grieve | d1828005a | 2021-10-28 16:52:17 | [diff] [blame] | 153 | # Debug info for partitions is the same as for the main library, so just |
| 154 | # symlink the .dwp files. |
| 155 | if args.split_dwarf: |
| 156 | dest= args.unstripped_output+'.dwp' |
| 157 | try: |
| 158 | os.unlink(dest) |
| 159 | exceptOSError: |
| 160 | pass |
| 161 | relpath= os.path.relpath(args.input+'.dwp', os.path.dirname(dest)) |
| 162 | os.symlink(relpath, dest) |
Nelson Billing | 3ed7301 | 2020-08-25 04:39:15 | [diff] [blame] | 163 | |
Christopher Grant | 3b06acce | 2019-04-25 18:26:41 | [diff] [blame] | 164 | |
| 165 | if __name__=='__main__': |
| 166 | sys.exit(main()) |