rtld.c (df618d033c1d50bde6516eb2d1867745590cb695) | rtld.c (d3980376e8c139e07958914c5184ab37463e4818) |
---|---|
1/*- 2 * Copyright 1996, 1997, 1998, 1999 John D. Polstra. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright --- 38 unchanged lines hidden (view full) --- 47#include <stdio.h> 48#include <stdlib.h> 49#include <string.h> 50#include <unistd.h> 51 52#include "debug.h" 53#include "rtld.h" 54 | 1/*- 2 * Copyright 1996, 1997, 1998, 1999 John D. Polstra. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright --- 38 unchanged lines hidden (view full) --- 47#include <stdio.h> 48#include <stdlib.h> 49#include <string.h> 50#include <unistd.h> 51 52#include "debug.h" 53#include "rtld.h" 54 |
55/* 56 * Debugging support. 57 */ 58 59#define assert(cond) ((cond) ? (void) 0 :\ 60 (msg("oops: " __XSTRING(__LINE__) "\n"), abort())) 61#define msg(s) (write(1, s, strlen(s))) 62#define trace() msg("trace: " __XSTRING(__LINE__) "\n"); 63 | |
64#define END_SYM "_end" 65#define PATH_RTLD "/usr/libexec/ld-elf.so.1" 66 67/* Types. */ 68typedef void (*func_ptr_type)(); 69 | 55#define END_SYM "_end" 56#define PATH_RTLD "/usr/libexec/ld-elf.so.1" 57 58/* Types. */ 59typedef void (*func_ptr_type)(); 60 |
61typedef struct Struct_LockInfo { 62 void *context; /* Client context for creating locks */ 63 void *thelock; /* The one big lock */ 64 /* Methods */ 65 void (*rlock_acquire)(void *lock); 66 void (*wlock_acquire)(void *lock); 67 void (*lock_release)(void *lock); 68 void (*lock_destroy)(void *lock); 69 void (*context_destroy)(void *context); 70} LockInfo; 71 |
|
70/* 71 * Function declarations. 72 */ 73static const char *basename(const char *); 74static void call_fini_functions(Obj_Entry *); 75static void call_init_functions(Obj_Entry *); 76static void die(void); 77static void digest_dynamic(Obj_Entry *); --- 5 unchanged lines hidden (view full) --- 83static void init_dag1(Obj_Entry *root, Obj_Entry *obj); 84static void init_rtld(caddr_t); 85static bool is_exported(const Elf_Sym *); 86static void linkmap_add(Obj_Entry *); 87static void linkmap_delete(Obj_Entry *); 88static int load_needed_objects(Obj_Entry *); 89static int load_preload_objects(void); 90static Obj_Entry *load_object(char *); | 72/* 73 * Function declarations. 74 */ 75static const char *basename(const char *); 76static void call_fini_functions(Obj_Entry *); 77static void call_init_functions(Obj_Entry *); 78static void die(void); 79static void digest_dynamic(Obj_Entry *); --- 5 unchanged lines hidden (view full) --- 85static void init_dag1(Obj_Entry *root, Obj_Entry *obj); 86static void init_rtld(caddr_t); 87static bool is_exported(const Elf_Sym *); 88static void linkmap_add(Obj_Entry *); 89static void linkmap_delete(Obj_Entry *); 90static int load_needed_objects(Obj_Entry *); 91static int load_preload_objects(void); 92static Obj_Entry *load_object(char *); |
93static void lock_nop(void *); |
|
91static Obj_Entry *obj_from_addr(const void *); 92static void objlist_add(Objlist *, Obj_Entry *); 93static Objlist_Entry *objlist_find(Objlist *, const Obj_Entry *); 94static void objlist_remove(Objlist *, Obj_Entry *); 95static int relocate_objects(Obj_Entry *, bool); 96static void rtld_exit(void); 97static char *search_library_path(const char *, const char *); 98static void set_program_var(const char *, const void *); --- 24 unchanged lines hidden (view full) --- 123static Obj_Entry obj_rtld; /* The dynamic linker shared object */ 124static unsigned long curmark; /* Current mark value */ 125 126static Objlist list_global = /* Objects dlopened with RTLD_GLOBAL */ 127 STAILQ_HEAD_INITIALIZER(list_global); 128static Objlist list_main = /* Objects loaded at program startup */ 129 STAILQ_HEAD_INITIALIZER(list_main); 130 | 94static Obj_Entry *obj_from_addr(const void *); 95static void objlist_add(Objlist *, Obj_Entry *); 96static Objlist_Entry *objlist_find(Objlist *, const Obj_Entry *); 97static void objlist_remove(Objlist *, Obj_Entry *); 98static int relocate_objects(Obj_Entry *, bool); 99static void rtld_exit(void); 100static char *search_library_path(const char *, const char *); 101static void set_program_var(const char *, const void *); --- 24 unchanged lines hidden (view full) --- 126static Obj_Entry obj_rtld; /* The dynamic linker shared object */ 127static unsigned long curmark; /* Current mark value */ 128 129static Objlist list_global = /* Objects dlopened with RTLD_GLOBAL */ 130 STAILQ_HEAD_INITIALIZER(list_global); 131static Objlist list_main = /* Objects loaded at program startup */ 132 STAILQ_HEAD_INITIALIZER(list_main); 133 |
134static LockInfo lockinfo; 135 |
|
131static Elf_Sym sym_zero; /* For resolving undefined weak refs. */ 132 133#define GDB_STATE(s) r_debug.r_state = s; r_debug_state(); 134 135extern Elf_Dyn _DYNAMIC; 136#pragma weak _DYNAMIC 137 138/* 139 * These are the functions the dynamic linker exports to application 140 * programs. They are the only symbols the dynamic linker is willing 141 * to export from itself. 142 */ 143static func_ptr_type exports[] = { 144 (func_ptr_type) &_rtld_error, 145 (func_ptr_type) &dlclose, 146 (func_ptr_type) &dlerror, 147 (func_ptr_type) &dlopen, 148 (func_ptr_type) &dlsym, 149 (func_ptr_type) &dladdr, | 136static Elf_Sym sym_zero; /* For resolving undefined weak refs. */ 137 138#define GDB_STATE(s) r_debug.r_state = s; r_debug_state(); 139 140extern Elf_Dyn _DYNAMIC; 141#pragma weak _DYNAMIC 142 143/* 144 * These are the functions the dynamic linker exports to application 145 * programs. They are the only symbols the dynamic linker is willing 146 * to export from itself. 147 */ 148static func_ptr_type exports[] = { 149 (func_ptr_type) &_rtld_error, 150 (func_ptr_type) &dlclose, 151 (func_ptr_type) &dlerror, 152 (func_ptr_type) &dlopen, 153 (func_ptr_type) &dlsym, 154 (func_ptr_type) &dladdr, |
155 (func_ptr_type) &dllockinit, |
|
150 NULL 151}; 152 153/* 154 * Global declarations normally provided by crt1. The dynamic linker is 155 * not build with crt1, so we have to provide them ourselves. 156 */ 157char *__progname; 158char **environ; 159 | 156 NULL 157}; 158 159/* 160 * Global declarations normally provided by crt1. The dynamic linker is 161 * not build with crt1, so we have to provide them ourselves. 162 */ 163char *__progname; 164char **environ; 165 |
166static __inline void 167rlock_acquire(void) 168{ 169 lockinfo.rlock_acquire(lockinfo.thelock); 170} 171 172static __inline void 173wlock_acquire(void) 174{ 175 lockinfo.wlock_acquire(lockinfo.thelock); 176} 177 178static __inline void 179lock_release(void) 180{ 181 lockinfo.lock_release(lockinfo.thelock); 182} 183 |
|
160/* 161 * Main entry point for dynamic linking. The first argument is the 162 * stack pointer. The stack is expected to be laid out as described 163 * in the SVR4 ABI specification, Intel 386 Processor Supplement. 164 * Specifically, the stack pointer points to a word containing 165 * ARGC. Following that in the stack is a null-terminated sequence 166 * of pointers to argument strings. Then comes a null-terminated 167 * sequence of pointers to environment strings. Finally, there is a --- 149 unchanged lines hidden (view full) --- 317 dbg("doing copy relocations"); 318 if (do_copy_relocations(obj_main) == -1) 319 die(); 320 321 dbg("initializing key program variables"); 322 set_program_var("__progname", argv[0] != NULL ? basename(argv[0]) : ""); 323 set_program_var("environ", env); 324 | 184/* 185 * Main entry point for dynamic linking. The first argument is the 186 * stack pointer. The stack is expected to be laid out as described 187 * in the SVR4 ABI specification, Intel 386 Processor Supplement. 188 * Specifically, the stack pointer points to a word containing 189 * ARGC. Following that in the stack is a null-terminated sequence 190 * of pointers to argument strings. Then comes a null-terminated 191 * sequence of pointers to environment strings. Finally, there is a --- 149 unchanged lines hidden (view full) --- 341 dbg("doing copy relocations"); 342 if (do_copy_relocations(obj_main) == -1) 343 die(); 344 345 dbg("initializing key program variables"); 346 set_program_var("__progname", argv[0] != NULL ? basename(argv[0]) : ""); 347 set_program_var("environ", env); 348 |
349 dbg("initializing default locks"); 350 dllockinit(NULL, NULL, NULL, NULL, NULL, NULL, NULL); 351 |
|
325 r_debug_state(); /* say hello to gdb! */ 326 327 dbg("calling _init functions"); 328 call_init_functions(obj_main->next); 329 330 dbg("transferring control to program entry point = %p", obj_main->entry); 331 332 /* Return the exit procedure and the program entry point. */ --- 6 unchanged lines hidden (view full) --- 339_rtld_bind(Obj_Entry *obj, Elf_Word reloff) 340{ 341 const Elf_Rel *rel; 342 const Elf_Sym *def; 343 const Obj_Entry *defobj; 344 Elf_Addr *where; 345 Elf_Addr target; 346 | 352 r_debug_state(); /* say hello to gdb! */ 353 354 dbg("calling _init functions"); 355 call_init_functions(obj_main->next); 356 357 dbg("transferring control to program entry point = %p", obj_main->entry); 358 359 /* Return the exit procedure and the program entry point. */ --- 6 unchanged lines hidden (view full) --- 366_rtld_bind(Obj_Entry *obj, Elf_Word reloff) 367{ 368 const Elf_Rel *rel; 369 const Elf_Sym *def; 370 const Obj_Entry *defobj; 371 Elf_Addr *where; 372 Elf_Addr target; 373 |
374 wlock_acquire(); |
|
347 if (obj->pltrel) 348 rel = (const Elf_Rel *) ((caddr_t) obj->pltrel + reloff); 349 else 350 rel = (const Elf_Rel *) ((caddr_t) obj->pltrela + reloff); 351 352 where = (Elf_Addr *) (obj->relocbase + rel->r_offset); 353 def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true); 354 if (def == NULL) 355 die(); 356 357 target = (Elf_Addr)(defobj->relocbase + def->st_value); 358 359 dbg("\"%s\" in \"%s\" ==> %p in \"%s\"", 360 defobj->strtab + def->st_name, basename(obj->path), 361 (void *)target, basename(defobj->path)); 362 363 reloc_jmpslot(where, target); | 375 if (obj->pltrel) 376 rel = (const Elf_Rel *) ((caddr_t) obj->pltrel + reloff); 377 else 378 rel = (const Elf_Rel *) ((caddr_t) obj->pltrela + reloff); 379 380 where = (Elf_Addr *) (obj->relocbase + rel->r_offset); 381 def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true); 382 if (def == NULL) 383 die(); 384 385 target = (Elf_Addr)(defobj->relocbase + def->st_value); 386 387 dbg("\"%s\" in \"%s\" ==> %p in \"%s\"", 388 defobj->strtab + def->st_name, basename(obj->path), 389 (void *)target, basename(defobj->path)); 390 391 reloc_jmpslot(where, target); |
392 lock_release(); |
|
364 return target; 365} 366 367/* 368 * Error reporting function. Use it like printf. If formats the message 369 * into a buffer, and sets things up so that the next call to dlerror() 370 * will return the message. 371 */ --- 681 unchanged lines hidden (view full) --- 1053 dbg(" WARNING: %s has impure text", obj->path); 1054 } else 1055 free(path); 1056 1057 obj->refcount++; 1058 return obj; 1059} 1060 | 393 return target; 394} 395 396/* 397 * Error reporting function. Use it like printf. If formats the message 398 * into a buffer, and sets things up so that the next call to dlerror() 399 * will return the message. 400 */ --- 681 unchanged lines hidden (view full) --- 1082 dbg(" WARNING: %s has impure text", obj->path); 1083 } else 1084 free(path); 1085 1086 obj->refcount++; 1087 return obj; 1088} 1089 |
1090static void 1091lock_nop(void *lock) 1092{ 1093} 1094 |
|
1061static Obj_Entry * 1062obj_from_addr(const void *addr) 1063{ 1064 unsigned long endhash; 1065 Obj_Entry *obj; 1066 1067 endhash = elf_hash(END_SYM); 1068 for (obj = obj_list; obj != NULL; obj = obj->next) { --- 149 unchanged lines hidden (view full) --- 1218 } 1219 1220 return NULL; 1221} 1222 1223int 1224dlclose(void *handle) 1225{ | 1095static Obj_Entry * 1096obj_from_addr(const void *addr) 1097{ 1098 unsigned long endhash; 1099 Obj_Entry *obj; 1100 1101 endhash = elf_hash(END_SYM); 1102 for (obj = obj_list; obj != NULL; obj = obj->next) { --- 149 unchanged lines hidden (view full) --- 1252 } 1253 1254 return NULL; 1255} 1256 1257int 1258dlclose(void *handle) 1259{ |
1226 Obj_Entry *root = dlcheck(handle); | 1260 Obj_Entry *root; |
1227 | 1261 |
1228 if (root == NULL) | 1262 wlock_acquire(); 1263 root = dlcheck(handle); 1264 if (root == NULL) { 1265 lock_release(); |
1229 return -1; | 1266 return -1; |
1267 } |
|
1230 1231 GDB_STATE(RT_DELETE); 1232 unload_object(root, true); 1233 root->dl_refcount--; 1234 GDB_STATE(RT_CONSISTENT); 1235 | 1268 1269 GDB_STATE(RT_DELETE); 1270 unload_object(root, true); 1271 root->dl_refcount--; 1272 GDB_STATE(RT_CONSISTENT); 1273 |
1274 lock_release(); |
|
1236 return 0; 1237} 1238 1239const char * 1240dlerror(void) 1241{ 1242 char *msg = error_message; 1243 error_message = NULL; 1244 return msg; 1245} 1246 | 1275 return 0; 1276} 1277 1278const char * 1279dlerror(void) 1280{ 1281 char *msg = error_message; 1282 error_message = NULL; 1283 return msg; 1284} 1285 |
1286void 1287dllockinit(void *context, 1288 void *(*lock_create)(void *context), 1289 void (*rlock_acquire)(void *lock), 1290 void (*wlock_acquire)(void *lock), 1291 void (*lock_release)(void *lock), 1292 void (*lock_destroy)(void *lock), 1293 void (*context_destroy)(void *context)) 1294{ 1295 /* NULL arguments mean reset to the built-in locks. */ 1296 if (lock_create == NULL) { 1297 context = NULL; 1298 lock_create = lockdflt_create; 1299 rlock_acquire = wlock_acquire = lockdflt_acquire; 1300 lock_release = lockdflt_release; 1301 lock_destroy = lockdflt_destroy; 1302 context_destroy = NULL; 1303 } 1304 1305 /* Temporarily set locking methods to no-ops. */ 1306 lockinfo.rlock_acquire = lock_nop; 1307 lockinfo.wlock_acquire = lock_nop; 1308 lockinfo.lock_release = lock_nop; 1309 1310 /* Release any existing locks and context. */ 1311 if (lockinfo.lock_destroy != NULL) 1312 lockinfo.lock_destroy(lockinfo.thelock); 1313 if (lockinfo.context_destroy != NULL) 1314 lockinfo.context_destroy(lockinfo.context); 1315 1316 /* 1317 * Allocate the locks we will need and call all the new locking 1318 * methods, to accomplish any needed lazy binding for the methods 1319 * themselves. 1320 */ 1321 lockinfo.thelock = lock_create(lockinfo.context); 1322 rlock_acquire(lockinfo.thelock); 1323 lock_release(lockinfo.thelock); 1324 wlock_acquire(lockinfo.thelock); 1325 lock_release(lockinfo.thelock); 1326 1327 /* Record the new method information. */ 1328 lockinfo.context = context; 1329 lockinfo.rlock_acquire = rlock_acquire; 1330 lockinfo.wlock_acquire = wlock_acquire; 1331 lockinfo.lock_release = lock_release; 1332 lockinfo.lock_destroy = lock_destroy; 1333 lockinfo.context_destroy = context_destroy; 1334} 1335 |
|
1247void * 1248dlopen(const char *name, int mode) 1249{ | 1336void * 1337dlopen(const char *name, int mode) 1338{ |
1250 Obj_Entry **old_obj_tail = obj_tail; 1251 Obj_Entry *obj = NULL; | 1339 Obj_Entry **old_obj_tail; 1340 Obj_Entry *obj; |
1252 | 1341 |
1342 wlock_acquire(); |
|
1253 GDB_STATE(RT_ADD); 1254 | 1343 GDB_STATE(RT_ADD); 1344 |
1345 old_obj_tail = obj_tail; 1346 obj = NULL; |
|
1255 if (name == NULL) { 1256 obj = obj_main; 1257 obj->refcount++; 1258 } else { 1259 char *path = find_library(name, obj_main); 1260 if (path != NULL) 1261 obj = load_object(path); 1262 } --- 12 unchanged lines hidden (view full) --- 1275 obj->dl_refcount--; 1276 obj = NULL; 1277 } else 1278 call_init_functions(obj); 1279 } 1280 } 1281 1282 GDB_STATE(RT_CONSISTENT); | 1347 if (name == NULL) { 1348 obj = obj_main; 1349 obj->refcount++; 1350 } else { 1351 char *path = find_library(name, obj_main); 1352 if (path != NULL) 1353 obj = load_object(path); 1354 } --- 12 unchanged lines hidden (view full) --- 1367 obj->dl_refcount--; 1368 obj = NULL; 1369 } else 1370 call_init_functions(obj); 1371 } 1372 } 1373 1374 GDB_STATE(RT_CONSISTENT); |
1283 | 1375 lock_release(); |
1284 return obj; 1285} 1286 1287void * 1288dlsym(void *handle, const char *name) 1289{ 1290 const Obj_Entry *obj; 1291 unsigned long hash; 1292 const Elf_Sym *def; 1293 const Obj_Entry *defobj; 1294 1295 hash = elf_hash(name); 1296 def = NULL; 1297 defobj = NULL; 1298 | 1376 return obj; 1377} 1378 1379void * 1380dlsym(void *handle, const char *name) 1381{ 1382 const Obj_Entry *obj; 1383 unsigned long hash; 1384 const Elf_Sym *def; 1385 const Obj_Entry *defobj; 1386 1387 hash = elf_hash(name); 1388 def = NULL; 1389 defobj = NULL; 1390 |
1391 wlock_acquire(); |
|
1299 if (handle == NULL || handle == RTLD_NEXT) { 1300 void *retaddr; 1301 1302 retaddr = __builtin_return_address(0); /* __GNUC__ only */ 1303 if ((obj = obj_from_addr(retaddr)) == NULL) { 1304 _rtld_error("Cannot determine caller's shared object"); | 1392 if (handle == NULL || handle == RTLD_NEXT) { 1393 void *retaddr; 1394 1395 retaddr = __builtin_return_address(0); /* __GNUC__ only */ 1396 if ((obj = obj_from_addr(retaddr)) == NULL) { 1397 _rtld_error("Cannot determine caller's shared object"); |
1398 lock_release(); |
|
1305 return NULL; 1306 } 1307 if (handle == NULL) { /* Just the caller's shared object. */ 1308 def = symlook_obj(name, hash, obj, true); 1309 defobj = obj; 1310 } else { /* All the shared objects after the caller's */ 1311 while ((obj = obj->next) != NULL) { 1312 if ((def = symlook_obj(name, hash, obj, true)) != NULL) { 1313 defobj = obj; 1314 break; 1315 } 1316 } 1317 } 1318 } else { | 1399 return NULL; 1400 } 1401 if (handle == NULL) { /* Just the caller's shared object. */ 1402 def = symlook_obj(name, hash, obj, true); 1403 defobj = obj; 1404 } else { /* All the shared objects after the caller's */ 1405 while ((obj = obj->next) != NULL) { 1406 if ((def = symlook_obj(name, hash, obj, true)) != NULL) { 1407 defobj = obj; 1408 break; 1409 } 1410 } 1411 } 1412 } else { |
1319 if ((obj = dlcheck(handle)) == NULL) | 1413 if ((obj = dlcheck(handle)) == NULL) { 1414 lock_release(); |
1320 return NULL; | 1415 return NULL; |
1416 } |
|
1321 1322 if (obj->mainprog) { 1323 /* Search main program and all libraries loaded by it. */ 1324 curmark++; 1325 def = symlook_list(name, hash, &list_main, &defobj, true); 1326 } else { 1327 /* 1328 * XXX - This isn't correct. The search should include the whole 1329 * DAG rooted at the given object. 1330 */ 1331 def = symlook_obj(name, hash, obj, true); 1332 defobj = obj; 1333 } 1334 } 1335 | 1417 1418 if (obj->mainprog) { 1419 /* Search main program and all libraries loaded by it. */ 1420 curmark++; 1421 def = symlook_list(name, hash, &list_main, &defobj, true); 1422 } else { 1423 /* 1424 * XXX - This isn't correct. The search should include the whole 1425 * DAG rooted at the given object. 1426 */ 1427 def = symlook_obj(name, hash, obj, true); 1428 defobj = obj; 1429 } 1430 } 1431 |
1336 if (def != NULL) | 1432 if (def != NULL) { 1433 lock_release(); |
1337 return defobj->relocbase + def->st_value; | 1434 return defobj->relocbase + def->st_value; |
1435 } |
|
1338 1339 _rtld_error("Undefined symbol \"%s\"", name); | 1436 1437 _rtld_error("Undefined symbol \"%s\"", name); |
1438 lock_release(); |
|
1340 return NULL; 1341} 1342 1343int 1344dladdr(const void *addr, Dl_info *info) 1345{ 1346 const Obj_Entry *obj; 1347 const Elf_Sym *def; 1348 void *symbol_addr; 1349 unsigned long symoffset; 1350 | 1439 return NULL; 1440} 1441 1442int 1443dladdr(const void *addr, Dl_info *info) 1444{ 1445 const Obj_Entry *obj; 1446 const Elf_Sym *def; 1447 void *symbol_addr; 1448 unsigned long symoffset; 1449 |
1450 wlock_acquire(); |
|
1351 obj = obj_from_addr(addr); 1352 if (obj == NULL) { 1353 _rtld_error("No shared object contains address"); | 1451 obj = obj_from_addr(addr); 1452 if (obj == NULL) { 1453 _rtld_error("No shared object contains address"); |
1454 lock_release(); |
|
1354 return 0; 1355 } 1356 info->dli_fname = obj->path; 1357 info->dli_fbase = obj->mapbase; 1358 info->dli_saddr = (void *)0; 1359 info->dli_sname = NULL; 1360 1361 /* --- 22 unchanged lines hidden (view full) --- 1384 /* Update our idea of the nearest symbol. */ 1385 info->dli_sname = obj->strtab + def->st_name; 1386 info->dli_saddr = symbol_addr; 1387 1388 /* Exact match? */ 1389 if (info->dli_saddr == addr) 1390 break; 1391 } | 1455 return 0; 1456 } 1457 info->dli_fname = obj->path; 1458 info->dli_fbase = obj->mapbase; 1459 info->dli_saddr = (void *)0; 1460 info->dli_sname = NULL; 1461 1462 /* --- 22 unchanged lines hidden (view full) --- 1485 /* Update our idea of the nearest symbol. */ 1486 info->dli_sname = obj->strtab + def->st_name; 1487 info->dli_saddr = symbol_addr; 1488 1489 /* Exact match? */ 1490 if (info->dli_saddr == addr) 1491 break; 1492 } |
1493 lock_release(); |
|
1392 return 1; 1393} 1394 1395static void 1396linkmap_add(Obj_Entry *obj) 1397{ 1398 struct link_map *l = &obj->linkmap; 1399 struct link_map *prev; --- 303 unchanged lines hidden --- | 1494 return 1; 1495} 1496 1497static void 1498linkmap_add(Obj_Entry *obj) 1499{ 1500 struct link_map *l = &obj->linkmap; 1501 struct link_map *prev; --- 303 unchanged lines hidden --- |