| @@ -1295,44 +1295,35 @@ static void build_got(TCCState *s1) · 1294 unmodified lines |
| 1295 | #endif | 1295 | #endif |
| 1296 | } | 1296 | } |
| 1297 | | 1297 | |
| 1298 | /* put a got or plt entry corresponding to a symbol in symtab_section. 'size' | 1298 | /* Create a GOT and (for function call) a PLT entry corresponding to a symbol |
| 1299 | and 'info' can be modifed if more precise info comes from the DLL. | 1299 | in s1->symtab. When creating the dynamic symbol table entry for the GOT |
| 1300 | Returns offset of GOT or PLT slot. */ | 1300 | relocation, use 'size' and 'info' for the corresponding symbol metadata. |
| | 1301 | Returns the offset of the GOT or (if any) PLT entry. */ |
| 1301 | static unsigned long put_got_entry(TCCState *s1, | 1302 | static unsigned long put_got_entry(TCCState *s1, |
| 1302 | int reloc_type, unsigned long size, int info, | 1303 | int reloc_type, unsigned long size, int info, |
| 1303 | int sym_index) | 1304 | int sym_index) |
| 1304 | { | 1305 | { |
| 1305 | int index, need_plt_entry; | 1306 | int index, need_plt_entry = 0; |
| 1306 | const char *name; | 1307 | const char *name; |
| 1307 | ElfW(Sym) *sym; | 1308 | ElfW(Sym) *sym; |
| 1308 | unsigned long offset; | 1309 | unsigned long offset; |
| 1309 | int *ptr; | 1310 | int *ptr; |
| | 1311 | size_t got_offset; |
| 1310 | struct sym_attr *symattr; | 1312 | struct sym_attr *symattr; |
| 1311 | | 1313 | |
| | 1314 | need_plt_entry = (reloc_type == R_JMP_SLOT); |
| | 1315 | |
| 1312 | if (!s1->got) | 1316 | if (!s1->got) |
| 1313 | build_got(s1); | 1317 | build_got(s1); |
| 1314 | | 1318 | |
| 1315 | need_plt_entry = | 1319 | /* create PLT if needed */ |
| 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 | | | |
| 1328 | if (need_plt_entry && !s1->plt) { | 1320 | if (need_plt_entry && !s1->plt) { |
| 1329 | /* add PLT */ | | |
| 1330 | s1->plt = new_section(s1, ".plt", SHT_PROGBITS, | 1321 | s1->plt = new_section(s1, ".plt", SHT_PROGBITS, |
| 1331 | SHF_ALLOC | SHF_EXECINSTR); | 1322 | SHF_ALLOC | SHF_EXECINSTR); |
| 1332 | s1->plt->sh_entsize = 4; | 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 | if (sym_index < s1->nb_sym_attrs) { | 1327 | if (sym_index < s1->nb_sym_attrs) { |
| 1337 | if (need_plt_entry && s1->sym_attrs[sym_index].plt_offset) | 1328 | if (need_plt_entry && s1->sym_attrs[sym_index].plt_offset) |
| 1338 | return s1->sym_attrs[sym_index].plt_offset; | 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 | symattr = alloc_sym_attr(s1, sym_index); | 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 | if (!need_plt_entry) | 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 | name = (char *) symtab_section->link->data + sym->st_name; | 1351 | name = (char *) symtab_section->link->data + sym->st_name; |
| 1351 | offset = sym->st_value; | 1352 | offset = sym->st_value; |
| | 1353 | |
| | 1354 | /* create PLT entry */ |
| | 1355 | if (need_plt_entry) { |
| 1352 | #if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) | 1356 | #if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) |
| 1353 | if (need_plt_entry) { | 1357 | Section *plt; |
| 1354 | Section *plt; | 1358 | uint8_t *p; |
| 1355 | uint8_t *p; | 1359 | int modrm; |
| 1356 | int modrm; | 1360 | unsigned long relofs; |
| 1357 | unsigned long relofs; | | |
| 1358 | | 1361 | |
| 1359 | #if defined(TCC_OUTPUT_DLL_WITH_PLT) | 1362 | #if defined(TCC_OUTPUT_DLL_WITH_PLT) |
| 1360 | modrm = 0x25; | 1363 | modrm = 0x25; |
| 1361 | #else | 1364 | #else |
| 1362 | /* if we build a DLL, we add a %ebx offset */ | 1365 | /* if we build a DLL, we add a %ebx offset */ |
| 1363 | if (s1->output_type == TCC_OUTPUT_DLL) | 1366 | if (s1->output_type == TCC_OUTPUT_DLL) |
| 1364 | modrm = 0xa3; | 1367 | modrm = 0xa3; |
| 1365 | else | 1368 | else |
| 1366 | modrm = 0x25; | 1369 | modrm = 0x25; |
| 1367 | #endif | 1370 | #endif |
| 1368 | | 1371 | |
| 1369 | /* add a PLT entry */ | 1372 | plt = s1->plt; |
| 1370 | plt = s1->plt; | 1373 | /* empty PLT: create PLT0 entry that pushes the library indentifier |
| 1371 | if (plt->data_offset == 0) { | 1374 | (GOT + PTR_SIZE) and jumps to ld.so resolution routine |
| 1372 | /* first plt entry */ | 1375 | (GOT + 2 * PTR_SIZE) */ |
| 1373 | p = section_ptr_add(plt, 16); | 1376 | if (plt->data_offset == 0) { |
| 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; | | |
| 1387 | p = section_ptr_add(plt, 16); | 1377 | p = section_ptr_add(plt, 16); |
| 1388 | p[0] = 0xff; /* jmp *(got + x) */ | 1378 | p[0] = 0xff; /* pushl got + PTR_SIZE */ |
| 1389 | p[1] = modrm; | 1379 | p[1] = modrm + 0x10; |
| 1390 | write32le(p + 2, s1->got->data_offset); | 1380 | write32le(p + 2, PTR_SIZE); |
| 1391 | p[6] = 0x68; /* push $xxx */ | 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 | #ifdef TCC_TARGET_X86_64 | 1398 | #ifdef TCC_TARGET_X86_64 |
| 1393 | /* On x86-64, the relocation is referred to by _index_. */ | 1399 | /* On x86-64, the relocation is referred to by _index_ */ |
| 1394 | write32le(p + 7, relofs / sizeof (ElfW_Rel)); | 1400 | write32le(p + 7, relofs / sizeof (ElfW_Rel)); |
| 1395 | #else | 1401 | #else |
| 1396 | write32le(p + 7, relofs); | 1402 | write32le(p + 7, relofs); |
| 1397 | #endif | 1403 | #endif |
| 1398 | p[11] = 0xe9; /* jmp plt_start */ | 1404 | p[11] = 0xe9; /* jmp plt_start */ |
| 1399 | write32le(p + 12, -(plt->data_offset)); | 1405 | 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 | } | | |
| 1426 | | 1406 | |
| 1427 | symattr->plt_offset = plt->data_offset; | 1407 | /* If this was an UNDEF symbol set the offset in the dynsymtab to the |
| 1428 | if (symattr->plt_thumb_stub) { | 1408 | PLT slot, so that PC32 relocs to it can be resolved */ |
| 1429 | p = section_ptr_add(plt, 20); | 1409 | if (sym->st_shndx == SHN_UNDEF) |
| 1430 | write32le(p, 0x4778); /* bx pc */ | 1410 | offset = plt->data_offset - 16; |
| 1431 | write32le(p+2, 0x46c0); /* nop */ | 1411 | #elif defined(TCC_TARGET_ARM) |
| 1432 | p += 4; | 1412 | Section *plt; |
| 1433 | } else | 1413 | uint8_t *p; |
| 1434 | p = section_ptr_add(plt, 16); | 1414 | |
| 1435 | write32le(p, 0xe59fc004); /* ldr ip, [pc, #4] ; GOT entry offset */ | 1415 | /* when building a DLL, GOT entry accesses must be done relative to |
| 1436 | write32le(p+4, 0xe08fc00c); /* add ip, pc, ip ; addr of GOT entry */ | 1416 | start of GOT (see x86_64 examble above) */ |
| 1437 | write32le(p+8, 0xe59cf000); /* ldr pc, [ip] ; jump to GOT entry */ | 1417 | if (s1->output_type == TCC_OUTPUT_DLL) |
| 1438 | write32le(p+12, s1->got->data_offset); /* GOT entry off once patched */ | 1418 | tcc_error("DLLs unimplemented!"); |
| 1439 | | 1419 | |
| 1440 | /* the symbol is modified so that it will be relocated to | 1420 | plt = s1->plt; |
| 1441 | the PLT */ | 1421 | /* empty PLT: create PLT0 entry that push address of call site and |
| 1442 | if (sym->st_shndx == SHN_UNDEF) | 1422 | jump to ld.so resolution routine (GOT + 8) */ |
| 1443 | offset = plt->data_offset - 16; | 1423 | if (plt->data_offset == 0) { |
| 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; | | |
| 1457 | p = section_ptr_add(plt, 16); | 1424 | p = section_ptr_add(plt, 16); |
| 1458 | write32le(p, s1->got->data_offset); | 1425 | write32le(p, 0xe52de004); /* push {lr} */ |
| 1459 | write32le(p + 4, (uint64_t)s1->got->data_offset >> 32); | 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) | 1431 | symattr->plt_offset = plt->data_offset; |
| 1462 | offset = plt->data_offset - 16; | 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 | #elif defined(TCC_TARGET_C67) | 1465 | #elif defined(TCC_TARGET_C67) |
| 1465 | if (s1->dynsym) { | | |
| 1466 | tcc_error("C67 got not implemented"); | 1466 | tcc_error("C67 got not implemented"); |
| 1467 | } | | |
| 1468 | #else | 1467 | #else |
| 1469 | #error unsupported CPU | 1468 | #error unsupported CPU |
| 1470 | #endif | 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 | if (s1->dynsym) { | 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 | /* XXX This might generate multiple syms for name. */ | 1482 | /* XXX This might generate multiple syms for name. */ |
| 1473 | index = put_elf_sym(s1->dynsym, offset, | 1483 | index = put_elf_sym(s1->dynsym, offset, size, info, 0, sym->st_shndx, |
| 1474 | size, info, 0, sym->st_shndx, name); | 1484 | name); |
| 1475 | /* Create the relocation (it's against the GOT for PLT | 1485 | put_elf_reloc(s1->dynsym, s1->got, got_offset, reloc_type, index); |
| 1476 | and GOT relocs). */ | | |
| 1477 | put_elf_reloc(s1->dynsym, s1->got, | | |
| 1478 | s1->got->data_offset, | | |
| 1479 | reloc_type, index); | | |
| 1480 | } else { | 1486 | } else { |
| 1481 | /* Without .dynsym (i.e. static link or memory output) we | 1487 | put_elf_reloc(symtab_section, s1->got, got_offset, reloc_type, |
| 1482 | still need relocs against the generated got, so as to fill | 1488 | sym_index); |
| 1483 | the entries with the symbol values (determined later). */ | 1489 | } |
| 1484 | put_elf_reloc(symtab_section, s1->got, | 1490 | |
| 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; | | |
| 1491 | if (need_plt_entry) | 1491 | if (need_plt_entry) |
| 1492 | return symattr->plt_offset; | 1492 | return symattr->plt_offset; |
| 1493 | else | 1493 | else |