| @@ -1295,44 +1295,35 @@ static void build_got(TCCState *s1) · 1294 unmodified lines |
| 1295 | 1295 | #endif |
| 1296 | 1296 | } |
| 1297 | 1297 | |
| 1298 | | −/* put a got or plt entry corresponding to a symbol in symtab_section. 'size' |
| 1299 | | − and 'info' can be modifed if more precise info comes from the DLL. |
| 1300 | | − Returns offset of GOT or PLT slot. */ |
| 1298 | +/* Create a GOT and (for function call) a PLT entry corresponding to a symbol |
| 1299 | + in s1->symtab. When creating the dynamic symbol table entry for the GOT |
| 1300 | + relocation, use 'size' and 'info' for the corresponding symbol metadata. |
| 1301 | + Returns the offset of the GOT or (if any) PLT entry. */ |
| 1301 | 1302 | static unsigned long put_got_entry(TCCState *s1, |
| 1302 | 1303 | int reloc_type, unsigned long size, int info, |
| 1303 | 1304 | int sym_index) |
| 1304 | 1305 | { |
| 1305 | | − int index, need_plt_entry; |
| 1306 | + int index, need_plt_entry = 0; |
| 1306 | 1307 | const char *name; |
| 1307 | 1308 | ElfW(Sym) *sym; |
| 1308 | 1309 | unsigned long offset; |
| 1309 | 1310 | int *ptr; |
| 1311 | + size_t got_offset; |
| 1310 | 1312 | struct sym_attr *symattr; |
| 1311 | 1313 | |
| 1314 | + need_plt_entry = (reloc_type == R_JMP_SLOT); |
| 1315 | + |
| 1312 | 1316 | if (!s1->got) |
| 1313 | 1317 | build_got(s1); |
| 1314 | 1318 | |
| 1315 | | − need_plt_entry = |
| 1316 | | −#ifdef TCC_TARGET_X86_64 |
| 1317 | | − (reloc_type == R_X86_64_JUMP_SLOT); |
| 1318 | | −#elif defined(TCC_TARGET_I386) |
| 1319 | | − (reloc_type == R_386_JMP_SLOT); |
| 1320 | | −#elif defined(TCC_TARGET_ARM) |
| 1321 | | − (reloc_type == R_ARM_JUMP_SLOT); |
| 1322 | | −#elif defined(TCC_TARGET_ARM64) |
| 1323 | | − (reloc_type == R_AARCH64_JUMP_SLOT); |
| 1324 | | −#else |
| 1325 | | − 0; |
| 1326 | | −#endif |
| 1327 | | − |
| 1319 | + /* create PLT if needed */ |
| 1328 | 1320 | if (need_plt_entry && !s1->plt) { |
| 1329 | | − /* add PLT */ |
| 1330 | 1321 | s1->plt = new_section(s1, ".plt", SHT_PROGBITS, |
| 1331 | 1322 | SHF_ALLOC | SHF_EXECINSTR); |
| 1332 | 1323 | s1->plt->sh_entsize = 4; |
| 1333 | 1324 | } |
| 1334 | 1325 | |
| 1335 | | − /* If a got/plt entry already exists for that symbol, no need to add one */ |
| 1326 | + /* already a GOT and/or PLT entry, no need to add one */ |
| 1336 | 1327 | if (sym_index < s1->nb_sym_attrs) { |
| 1337 | 1328 | if (need_plt_entry && s1->sym_attrs[sym_index].plt_offset) |
| 1338 | 1329 | return s1->sym_attrs[sym_index].plt_offset; |
| @@ -1342,152 +1333,161 @@ static unsigned long put_got_entry(TCCState *s1, · 3 unmodified lines |
| 1342 | 1333 | |
| 1343 | 1334 | symattr = alloc_sym_attr(s1, sym_index); |
| 1344 | 1335 | |
| 1345 | | − /* Only store the GOT offset if it's not generated for the PLT entry. */ |
| 1336 | + /* create the GOT entry */ |
| 1337 | + ptr = section_ptr_add(s1->got, PTR_SIZE); |
| 1338 | + *ptr = 0; |
| 1339 | + got_offset = OFFSET_FROM_SECTION_START (s1->got, ptr); |
| 1340 | + |
| 1341 | + /* In case a function is both called and its address taken 2 GOT entries |
| 1342 | + are created, one for taking the address (GOT) and the other for the PLT |
| 1343 | + entry (PLTGOT). We don't record the offset of the PLTGOT entry in the |
| 1344 | + got_offset field since it might overwrite the offset of a GOT entry. |
| 1345 | + Besides, for PLT entry the static relocation is against the PLT entry |
| 1346 | + and the dynamic relocation for PLTGOT is created in this function. */ |
| 1346 | 1347 | if (!need_plt_entry) |
| 1347 | | − symattr->got_offset = s1->got->data_offset; |
| 1348 | + symattr->got_offset = got_offset; |
| 1348 | 1349 | |
| 1349 | | − sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; |
| 1350 | + sym = &((ElfW(Sym) *) symtab_section->data)[sym_index]; |
| 1350 | 1351 | name = (char *) symtab_section->link->data + sym->st_name; |
| 1351 | 1352 | offset = sym->st_value; |
| 1353 | + |
| 1354 | + /* create PLT entry */ |
| 1355 | + if (need_plt_entry) { |
| 1352 | 1356 | #if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) |
| 1353 | | − if (need_plt_entry) { |
| 1354 | | − Section *plt; |
| 1355 | | − uint8_t *p; |
| 1356 | | − int modrm; |
| 1357 | | − unsigned long relofs; |
| 1357 | + Section *plt; |
| 1358 | + uint8_t *p; |
| 1359 | + int modrm; |
| 1360 | + unsigned long relofs; |
| 1358 | 1361 | |
| 1359 | 1362 | #if defined(TCC_OUTPUT_DLL_WITH_PLT) |
| 1360 | | − modrm = 0x25; |
| 1363 | + modrm = 0x25; |
| 1361 | 1364 | #else |
| 1362 | | − /* if we build a DLL, we add a %ebx offset */ |
| 1363 | | − if (s1->output_type == TCC_OUTPUT_DLL) |
| 1364 | | − modrm = 0xa3; |
| 1365 | | − else |
| 1366 | | − modrm = 0x25; |
| 1365 | + /* if we build a DLL, we add a %ebx offset */ |
| 1366 | + if (s1->output_type == TCC_OUTPUT_DLL) |
| 1367 | + modrm = 0xa3; |
| 1368 | + else |
| 1369 | + modrm = 0x25; |
| 1367 | 1370 | #endif |
| 1368 | 1371 | |
| 1369 | | − /* add a PLT entry */ |
| 1370 | | − plt = s1->plt; |
| 1371 | | − if (plt->data_offset == 0) { |
| 1372 | | − /* first plt entry */ |
| 1373 | | − p = section_ptr_add(plt, 16); |
| 1374 | | − p[0] = 0xff; /* pushl got + PTR_SIZE */ |
| 1375 | | − p[1] = modrm + 0x10; |
| 1376 | | − write32le(p + 2, PTR_SIZE); |
| 1377 | | − p[6] = 0xff; /* jmp *(got + PTR_SIZE * 2) */ |
| 1378 | | − p[7] = modrm; |
| 1379 | | − write32le(p + 8, PTR_SIZE * 2); |
| 1380 | | − } |
| 1381 | | − |
| 1382 | | − /* The PLT slot refers to the relocation entry it needs |
| 1383 | | − via offset. The reloc entry is created below, so its |
| 1384 | | − offset is the current data_offset. */ |
| 1385 | | − relofs = s1->got->reloc ? s1->got->reloc->data_offset : 0; |
| 1386 | | − symattr->plt_offset = plt->data_offset; |
| 1372 | + plt = s1->plt; |
| 1373 | + /* empty PLT: create PLT0 entry that pushes the library indentifier |
| 1374 | + (GOT + PTR_SIZE) and jumps to ld.so resolution routine |
| 1375 | + (GOT + 2 * PTR_SIZE) */ |
| 1376 | + if (plt->data_offset == 0) { |
| 1387 | 1377 | p = section_ptr_add(plt, 16); |
| 1388 | | − p[0] = 0xff; /* jmp *(got + x) */ |
| 1389 | | − p[1] = modrm; |
| 1390 | | − write32le(p + 2, s1->got->data_offset); |
| 1391 | | − p[6] = 0x68; /* push $xxx */ |
| 1378 | + p[0] = 0xff; /* pushl got + PTR_SIZE */ |
| 1379 | + p[1] = modrm + 0x10; |
| 1380 | + write32le(p + 2, PTR_SIZE); |
| 1381 | + p[6] = 0xff; /* jmp *(got + PTR_SIZE * 2) */ |
| 1382 | + p[7] = modrm; |
| 1383 | + write32le(p + 8, PTR_SIZE * 2); |
| 1384 | + } |
| 1385 | + |
| 1386 | + /* The PLT slot refers to the relocation entry it needs via offset. |
| 1387 | + The reloc entry is created below, so its offset is the current |
| 1388 | + data_offset */ |
| 1389 | + relofs = s1->got->reloc ? s1->got->reloc->data_offset : 0; |
| 1390 | + symattr->plt_offset = plt->data_offset; |
| 1391 | + |
| 1392 | + /* Jump to GOT entry where ld.so initially put the address of ip + 4 */ |
| 1393 | + p = section_ptr_add(plt, 16); |
| 1394 | + p[0] = 0xff; /* jmp *(got + x) */ |
| 1395 | + p[1] = modrm; |
| 1396 | + write32le(p + 2, got_offset); |
| 1397 | + p[6] = 0x68; /* push $xxx */ |
| 1392 | 1398 | #ifdef TCC_TARGET_X86_64 |
| 1393 | | − /* On x86-64, the relocation is referred to by _index_. */ |
| 1394 | | − write32le(p + 7, relofs / sizeof (ElfW_Rel)); |
| 1399 | + /* On x86-64, the relocation is referred to by _index_ */ |
| 1400 | + write32le(p + 7, relofs / sizeof (ElfW_Rel)); |
| 1395 | 1401 | #else |
| 1396 | | − write32le(p + 7, relofs); |
| 1402 | + write32le(p + 7, relofs); |
| 1397 | 1403 | #endif |
| 1398 | | − p[11] = 0xe9; /* jmp plt_start */ |
| 1399 | | − write32le(p + 12, -(plt->data_offset)); |
| 1400 | | − |
| 1401 | | − /* If this was an UNDEF symbol set the offset in the |
| 1402 | | − dynsymtab to the PLT slot, so that PC32 relocs to it |
| 1403 | | − can be resolved. */ |
| 1404 | | − if (sym->st_shndx == SHN_UNDEF) |
| 1405 | | − offset = plt->data_offset - 16; |
| 1406 | | − } |
| 1407 | | −#elif defined(TCC_TARGET_ARM) |
| 1408 | | − if (need_plt_entry) { |
| 1409 | | − Section *plt; |
| 1410 | | − uint8_t *p; |
| 1411 | | − |
| 1412 | | − /* if we build a DLL, we add a %ebx offset */ |
| 1413 | | − if (s1->output_type == TCC_OUTPUT_DLL) |
| 1414 | | − tcc_error("DLLs unimplemented!"); |
| 1415 | | − |
| 1416 | | − /* add a PLT entry */ |
| 1417 | | − plt = s1->plt; |
| 1418 | | − if (plt->data_offset == 0) { |
| 1419 | | − /* first plt entry */ |
| 1420 | | − p = section_ptr_add(plt, 16); |
| 1421 | | − write32le(p, 0xe52de004); /* push {lr} */ |
| 1422 | | − write32le(p+4, 0xe59fe010); /* ldr lr, [pc, #16] */ |
| 1423 | | − write32le(p+8, 0xe08fe00e); /* add lr, pc, lr */ |
| 1424 | | − write32le(p+12, 0xe5bef008); /* ldr pc, [lr, #8]! */ |
| 1425 | | − } |
| 1404 | + p[11] = 0xe9; /* jmp plt_start */ |
| 1405 | + write32le(p + 12, -(plt->data_offset)); |
| 1426 | 1406 | |
| 1427 | | − symattr->plt_offset = plt->data_offset; |
| 1428 | | − if (symattr->plt_thumb_stub) { |
| 1429 | | − p = section_ptr_add(plt, 20); |
| 1430 | | − write32le(p, 0x4778); /* bx pc */ |
| 1431 | | − write32le(p+2, 0x46c0); /* nop */ |
| 1432 | | − p += 4; |
| 1433 | | − } else |
| 1434 | | − p = section_ptr_add(plt, 16); |
| 1435 | | − write32le(p, 0xe59fc004); /* ldr ip, [pc, #4] ; GOT entry offset */ |
| 1436 | | − write32le(p+4, 0xe08fc00c); /* add ip, pc, ip ; addr of GOT entry */ |
| 1437 | | − write32le(p+8, 0xe59cf000); /* ldr pc, [ip] ; jump to GOT entry */ |
| 1438 | | − write32le(p+12, s1->got->data_offset); /* GOT entry off once patched */ |
| 1439 | | − |
| 1440 | | − /* the symbol is modified so that it will be relocated to |
| 1441 | | − the PLT */ |
| 1442 | | − if (sym->st_shndx == SHN_UNDEF) |
| 1443 | | − offset = plt->data_offset - 16; |
| 1444 | | − } |
| 1445 | | −#elif defined(TCC_TARGET_ARM64) |
| 1446 | | − if (need_plt_entry) { |
| 1447 | | − Section *plt; |
| 1448 | | − uint8_t *p; |
| 1449 | | − |
| 1450 | | − if (s1->output_type == TCC_OUTPUT_DLL) |
| 1451 | | − tcc_error("DLLs unimplemented!"); |
| 1452 | | − |
| 1453 | | − plt = s1->plt; |
| 1454 | | − if (plt->data_offset == 0) |
| 1455 | | − section_ptr_add(plt, 32); |
| 1456 | | − symattr->plt_offset = plt->data_offset; |
| 1407 | + /* If this was an UNDEF symbol set the offset in the dynsymtab to the |
| 1408 | + PLT slot, so that PC32 relocs to it can be resolved */ |
| 1409 | + if (sym->st_shndx == SHN_UNDEF) |
| 1410 | + offset = plt->data_offset - 16; |
| 1411 | +#elif defined(TCC_TARGET_ARM) |
| 1412 | + Section *plt; |
| 1413 | + uint8_t *p; |
| 1414 | + |
| 1415 | + /* when building a DLL, GOT entry accesses must be done relative to |
| 1416 | + start of GOT (see x86_64 examble above) */ |
| 1417 | + if (s1->output_type == TCC_OUTPUT_DLL) |
| 1418 | + tcc_error("DLLs unimplemented!"); |
| 1419 | + |
| 1420 | + plt = s1->plt; |
| 1421 | + /* empty PLT: create PLT0 entry that push address of call site and |
| 1422 | + jump to ld.so resolution routine (GOT + 8) */ |
| 1423 | + if (plt->data_offset == 0) { |
| 1457 | 1424 | p = section_ptr_add(plt, 16); |
| 1458 | | − write32le(p, s1->got->data_offset); |
| 1459 | | − write32le(p + 4, (uint64_t)s1->got->data_offset >> 32); |
| 1425 | + write32le(p, 0xe52de004); /* push {lr} */ |
| 1426 | + write32le(p+4, 0xe59fe010); /* ldr lr, [pc, #16] */ |
| 1427 | + write32le(p+8, 0xe08fe00e); /* add lr, pc, lr */ |
| 1428 | + write32le(p+12, 0xe5bef008); /* ldr pc, [lr, #8]! */ |
| 1429 | + } |
| 1460 | 1430 | |
| 1461 | | − if (sym->st_shndx == SHN_UNDEF) |
| 1462 | | − offset = plt->data_offset - 16; |
| 1431 | + symattr->plt_offset = plt->data_offset; |
| 1432 | + if (symattr->plt_thumb_stub) { |
| 1433 | + p = section_ptr_add(plt, 4); |
| 1434 | + write32le(p, 0x4778); /* bx pc */ |
| 1435 | + write32le(p+2, 0x46c0); /* nop */ |
| 1463 | 1436 | } |
| 1437 | + p = section_ptr_add(plt, 16); |
| 1438 | + /* Jump to GOT entry where ld.so initially put address of PLT0 */ |
| 1439 | + write32le(p, 0xe59fc004); /* ldr ip, [pc, #4] */ |
| 1440 | + write32le(p+4, 0xe08fc00c); /* add ip, pc, ip */ |
| 1441 | + write32le(p+8, 0xe59cf000); /* ldr pc, [ip] */ |
| 1442 | + /* p + 12 contains offset to GOT entry once patched by relocate_plt */ |
| 1443 | + write32le(p+12, got_offset); |
| 1444 | + |
| 1445 | + /* the symbol is modified so that it will be relocated to the PLT */ |
| 1446 | + if (sym->st_shndx == SHN_UNDEF) |
| 1447 | + offset = plt->data_offset - 16; |
| 1448 | +#elif defined(TCC_TARGET_ARM64) |
| 1449 | + Section *plt; |
| 1450 | + uint8_t *p; |
| 1451 | + |
| 1452 | + if (s1->output_type == TCC_OUTPUT_DLL) |
| 1453 | + tcc_error("DLLs unimplemented!"); |
| 1454 | + |
| 1455 | + plt = s1->plt; |
| 1456 | + if (plt->data_offset == 0) |
| 1457 | + section_ptr_add(plt, 32); |
| 1458 | + symattr->plt_offset = plt->data_offset; |
| 1459 | + p = section_ptr_add(plt, 16); |
| 1460 | + write32le(p, got_offset); |
| 1461 | + write32le(p + 4, (uint64_t) got_offset >> 32); |
| 1462 | + |
| 1463 | + if (sym->st_shndx == SHN_UNDEF) |
| 1464 | + offset = plt->data_offset - 16; |
| 1464 | 1465 | #elif defined(TCC_TARGET_C67) |
| 1465 | | − if (s1->dynsym) { |
| 1466 | 1466 | tcc_error("C67 got not implemented"); |
| 1467 | | − } |
| 1468 | 1467 | #else |
| 1469 | 1468 | #error unsupported CPU |
| 1470 | 1469 | #endif |
| 1470 | + } |
| 1471 | + |
| 1472 | + /* Create the GOT relocation that will insert the address of the object or |
| 1473 | + function of interest in the GOT entry. This is a static relocation for |
| 1474 | + memory output (dlsym will give us the address of symbols) and dynamic |
| 1475 | + relocation otherwise (executable and DLLs). The relocation should be |
| 1476 | + done lazily for GOT entry with *_JUMP_SLOT relocation type (the one |
| 1477 | + associated to a PLT entry) but is currently done at load time for an |
| 1478 | + unknown reason. */ |
| 1471 | 1479 | if (s1->dynsym) { |
| 1480 | + /* create the dynamic symbol table entry that the relocation refers to |
| 1481 | + in its r_info field to identify the symbol */ |
| 1472 | 1482 | /* XXX This might generate multiple syms for name. */ |
| 1473 | | − index = put_elf_sym(s1->dynsym, offset, |
| 1474 | | − size, info, 0, sym->st_shndx, name); |
| 1475 | | − /* Create the relocation (it's against the GOT for PLT |
| 1476 | | − and GOT relocs). */ |
| 1477 | | − put_elf_reloc(s1->dynsym, s1->got, |
| 1478 | | − s1->got->data_offset, |
| 1479 | | − reloc_type, index); |
| 1483 | + index = put_elf_sym(s1->dynsym, offset, size, info, 0, sym->st_shndx, |
| 1484 | + name); |
| 1485 | + put_elf_reloc(s1->dynsym, s1->got, got_offset, reloc_type, index); |
| 1480 | 1486 | } else { |
| 1481 | | − /* Without .dynsym (i.e. static link or memory output) we |
| 1482 | | − still need relocs against the generated got, so as to fill |
| 1483 | | − the entries with the symbol values (determined later). */ |
| 1484 | | − put_elf_reloc(symtab_section, s1->got, |
| 1485 | | − s1->got->data_offset, |
| 1486 | | − reloc_type, sym_index); |
| 1487 | | − } |
| 1488 | | − /* And now create the GOT slot itself. */ |
| 1489 | | − ptr = section_ptr_add(s1->got, PTR_SIZE); |
| 1490 | | − *ptr = 0; |
| 1487 | + put_elf_reloc(symtab_section, s1->got, got_offset, reloc_type, |
| 1488 | + sym_index); |
| 1489 | + } |
| 1490 | + |
| 1491 | 1491 | if (need_plt_entry) |
| 1492 | 1492 | return symattr->plt_offset; |
| 1493 | 1493 | else |