Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

objtool: Add elf_create_reloc() and elf_init_reloc()

elf_create_rela_section() is quite limited in that it requires the
caller to know how many relocations need to be allocated up front.

In preparation for the objtool klp diff subcommand, allow an arbitrary
number of relocations to be created and initialized on demand after
section creation.

Acked-by: Petr Mladek <pmladek@suse.com>
Tested-by: Joe Lawrence <joe.lawrence@redhat.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>

+165 -14
+156 -14
tools/objtool/elf.c
··· 22 22 #include <objtool/warn.h> 23 23 24 24 #define ALIGN_UP(x, align_to) (((x) + ((align_to)-1)) & ~((align_to)-1)) 25 + #define ALIGN_UP_POW2(x) (1U << ((8 * sizeof(x)) - __builtin_clz((x) - 1U))) 26 + #define MAX(a, b) ((a) > (b) ? (a) : (b)) 25 27 26 28 static inline u32 str_hash(const char *str) 27 29 { ··· 901 899 offset, size); 902 900 } 903 901 904 - static struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec, 905 - unsigned int reloc_idx, 906 - unsigned long offset, struct symbol *sym, 907 - s64 addend, unsigned int type) 902 + struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec, 903 + unsigned int reloc_idx, unsigned long offset, 904 + struct symbol *sym, s64 addend, unsigned int type) 908 905 { 909 906 struct reloc *reloc, empty = { 0 }; 910 907 ··· 1005 1004 1006 1005 rsec->base->rsec = rsec; 1007 1006 1008 - nr_reloc = 0; 1007 + /* nr_alloc_relocs=0: libelf owns d_buf */ 1008 + rsec->nr_alloc_relocs = 0; 1009 + 1009 1010 rsec->relocs = calloc(sec_num_entries(rsec), sizeof(*reloc)); 1010 1011 if (!rsec->relocs) { 1011 1012 ERROR_GLIBC("calloc"); 1012 1013 return -1; 1013 1014 } 1015 + 1016 + nr_reloc = 0; 1014 1017 for (i = 0; i < sec_num_entries(rsec); i++) { 1015 1018 reloc = &rsec->relocs[i]; 1016 1019 ··· 1263 1258 return sec; 1264 1259 } 1265 1260 1261 + static int elf_alloc_reloc(struct elf *elf, struct section *rsec) 1262 + { 1263 + struct reloc *old_relocs, *old_relocs_end, *new_relocs; 1264 + unsigned int nr_relocs_old = sec_num_entries(rsec); 1265 + unsigned int nr_relocs_new = nr_relocs_old + 1; 1266 + unsigned long nr_alloc; 1267 + struct symbol *sym; 1268 + 1269 + if (!rsec->data) { 1270 + rsec->data = elf_newdata(elf_getscn(elf->elf, rsec->idx)); 1271 + if (!rsec->data) { 1272 + ERROR_ELF("elf_newdata"); 1273 + return -1; 1274 + } 1275 + 1276 + rsec->data->d_align = 1; 1277 + rsec->data->d_type = ELF_T_RELA; 1278 + rsec->data->d_buf = NULL; 1279 + } 1280 + 1281 + rsec->data->d_size = nr_relocs_new * elf_rela_size(elf); 1282 + rsec->sh.sh_size = rsec->data->d_size; 1283 + 1284 + nr_alloc = MAX(64, ALIGN_UP_POW2(nr_relocs_new)); 1285 + if (nr_alloc <= rsec->nr_alloc_relocs) 1286 + return 0; 1287 + 1288 + if (rsec->data->d_buf && !rsec->nr_alloc_relocs) { 1289 + void *orig_buf = rsec->data->d_buf; 1290 + 1291 + /* 1292 + * The original d_buf is owned by libelf so it can't be 1293 + * realloced. 1294 + */ 1295 + rsec->data->d_buf = malloc(nr_alloc * elf_rela_size(elf)); 1296 + if (!rsec->data->d_buf) { 1297 + ERROR_GLIBC("malloc"); 1298 + return -1; 1299 + } 1300 + memcpy(rsec->data->d_buf, orig_buf, 1301 + nr_relocs_old * elf_rela_size(elf)); 1302 + } else { 1303 + rsec->data->d_buf = realloc(rsec->data->d_buf, 1304 + nr_alloc * elf_rela_size(elf)); 1305 + if (!rsec->data->d_buf) { 1306 + ERROR_GLIBC("realloc"); 1307 + return -1; 1308 + } 1309 + } 1310 + 1311 + rsec->nr_alloc_relocs = nr_alloc; 1312 + 1313 + old_relocs = rsec->relocs; 1314 + new_relocs = calloc(nr_alloc, sizeof(struct reloc)); 1315 + if (!new_relocs) { 1316 + ERROR_GLIBC("calloc"); 1317 + return -1; 1318 + } 1319 + 1320 + if (!old_relocs) 1321 + goto done; 1322 + 1323 + /* 1324 + * The struct reloc's address has changed. Update all the symbols and 1325 + * relocs which reference it. 1326 + */ 1327 + 1328 + old_relocs_end = &old_relocs[nr_relocs_old]; 1329 + for_each_sym(elf, sym) { 1330 + struct reloc *reloc; 1331 + 1332 + reloc = sym->relocs; 1333 + if (!reloc) 1334 + continue; 1335 + 1336 + if (reloc >= old_relocs && reloc < old_relocs_end) 1337 + sym->relocs = &new_relocs[reloc - old_relocs]; 1338 + 1339 + while (1) { 1340 + struct reloc *next_reloc = sym_next_reloc(reloc); 1341 + 1342 + if (!next_reloc) 1343 + break; 1344 + 1345 + if (next_reloc >= old_relocs && next_reloc < old_relocs_end) 1346 + set_sym_next_reloc(reloc, &new_relocs[next_reloc - old_relocs]); 1347 + 1348 + reloc = next_reloc; 1349 + } 1350 + } 1351 + 1352 + memcpy(new_relocs, old_relocs, nr_relocs_old * sizeof(struct reloc)); 1353 + 1354 + for (int i = 0; i < nr_relocs_old; i++) { 1355 + struct reloc *old = &old_relocs[i]; 1356 + struct reloc *new = &new_relocs[i]; 1357 + u32 key = reloc_hash(old); 1358 + 1359 + elf_hash_del(reloc, &old->hash, key); 1360 + elf_hash_add(reloc, &new->hash, key); 1361 + } 1362 + 1363 + free(old_relocs); 1364 + done: 1365 + rsec->relocs = new_relocs; 1366 + return 0; 1367 + } 1368 + 1266 1369 struct section *elf_create_rela_section(struct elf *elf, struct section *sec, 1267 - unsigned int reloc_nr) 1370 + unsigned int nr_relocs) 1268 1371 { 1269 1372 struct section *rsec; 1270 1373 char *rsec_name; ··· 1385 1272 strcpy(rsec_name, ".rela"); 1386 1273 strcat(rsec_name, sec->name); 1387 1274 1388 - rsec = elf_create_section(elf, rsec_name, reloc_nr * elf_rela_size(elf), 1275 + rsec = elf_create_section(elf, rsec_name, nr_relocs * elf_rela_size(elf), 1389 1276 elf_rela_size(elf), SHT_RELA, elf_addr_size(elf), 1390 1277 SHF_INFO_LINK); 1391 1278 free(rsec_name); 1392 1279 if (!rsec) 1393 1280 return NULL; 1394 1281 1395 - rsec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; 1396 - rsec->sh.sh_info = sec->idx; 1397 - 1398 - if (reloc_nr) { 1282 + if (nr_relocs) { 1399 1283 rsec->data->d_type = ELF_T_RELA; 1400 - rsec->relocs = calloc(sec_num_entries(rsec), sizeof(struct reloc)); 1284 + 1285 + rsec->nr_alloc_relocs = nr_relocs; 1286 + rsec->relocs = calloc(nr_relocs, sizeof(struct reloc)); 1401 1287 if (!rsec->relocs) { 1402 1288 ERROR_GLIBC("calloc"); 1403 1289 return NULL; 1404 1290 } 1405 1291 } 1292 + 1293 + rsec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; 1294 + rsec->sh.sh_info = sec->idx; 1406 1295 1407 1296 sec->rsec = rsec; 1408 1297 rsec->base = sec; ··· 1412 1297 return rsec; 1413 1298 } 1414 1299 1300 + struct reloc *elf_create_reloc(struct elf *elf, struct section *sec, 1301 + unsigned long offset, 1302 + struct symbol *sym, s64 addend, 1303 + unsigned int type) 1304 + { 1305 + struct section *rsec = sec->rsec; 1306 + 1307 + if (!rsec) { 1308 + rsec = elf_create_rela_section(elf, sec, 0); 1309 + if (!rsec) 1310 + return NULL; 1311 + } 1312 + 1313 + if (find_reloc_by_dest(elf, sec, offset)) { 1314 + ERROR_FUNC(sec, offset, "duplicate reloc"); 1315 + return NULL; 1316 + } 1317 + 1318 + if (elf_alloc_reloc(elf, rsec)) 1319 + return NULL; 1320 + 1321 + mark_sec_changed(elf, rsec, true); 1322 + 1323 + return elf_init_reloc(elf, rsec, sec_num_entries(rsec) - 1, offset, sym, 1324 + addend, type); 1325 + } 1326 + 1415 1327 struct section *elf_create_section_pair(struct elf *elf, const char *name, 1416 1328 size_t entsize, unsigned int nr, 1417 - unsigned int reloc_nr) 1329 + unsigned int nr_relocs) 1418 1330 { 1419 1331 struct section *sec; 1420 1332 ··· 1450 1308 if (!sec) 1451 1309 return NULL; 1452 1310 1453 - if (!elf_create_rela_section(elf, sec, reloc_nr)) 1311 + if (!elf_create_rela_section(elf, sec, nr_relocs)) 1454 1312 return NULL; 1455 1313 1456 1314 return sec;
+9
tools/objtool/include/objtool/elf.h
··· 47 47 int idx; 48 48 bool _changed, text, rodata, noinstr, init, truncate; 49 49 struct reloc *relocs; 50 + unsigned long nr_alloc_relocs; 50 51 }; 51 52 52 53 struct symbol { ··· 140 139 size_t size); 141 140 142 141 unsigned int elf_add_string(struct elf *elf, struct section *strtab, const char *str); 142 + 143 + struct reloc *elf_create_reloc(struct elf *elf, struct section *sec, 144 + unsigned long offset, struct symbol *sym, 145 + s64 addend, unsigned int type); 146 + 147 + struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec, 148 + unsigned int reloc_idx, unsigned long offset, 149 + struct symbol *sym, s64 addend, unsigned int type); 143 150 144 151 struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec, 145 152 unsigned long offset,